Skip to content

[17 regression][C++20] templated friend not working if the concept use contains the enclosing class #71595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
zero9178 opened this issue Nov 7, 2023 · 5 comments · Fixed by #74265
Assignees
Labels
c++20 clang:frontend Language frontend issues, e.g. anything involving "Sema" concepts C++20 concepts confirmed Verified by a second party regression

Comments

@zero9178
Copy link
Member

zero9178 commented Nov 7, 2023

Recently upgraded to Clang 17 which lead to code that previously worked in Clang 16 to suddently stop compiling. I have reduced it to the following snippet:

template <class T, class U>
concept Test = true;

class UnwindFrame
{
    template <Test<UnwindFrame> F>
    friend void unwindStack();

    UnwindFrame();
};

template <Test<UnwindFrame> F>
void unwindStack()
{
    auto u = UnwindFrame();
}

int main()
{
    unwindStack<int>();
}

https://godbolt.org/z/1vcqeGnG1

When using Clang 17 or newer the friend declaration seemingly has no effect causing a compilation failure due to the use of a private constructor:

<source>:16:14: error: calling a private constructor of class 'UnwindFrame'
   16 |     auto u = UnwindFrame();
      |              ^
<source>:21:5: note: in instantiation of function template specialization 'unwindStack<int>' requested here
   21 |     unwindStack<int>();
      |     ^
<source>:10:5: note: implicitly declared private here
   10 |     UnwindFrame();
      |     ^

This used to work in Clang 16 and is valid C++20 I believe.

While reducing, I noticed the following important properties for this to be reproducible: unwindStack HAS to be restricted by a concept and the concept HAS to be templated, with a template parameter being the enclosing class UnwindFrame in this case.

@zero9178 zero9178 added clang:frontend Language frontend issues, e.g. anything involving "Sema" regression labels Nov 7, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 7, 2023

@llvm/issue-subscribers-clang-frontend

Author: Markus Böck (zero9178)

Recently upgraded to Clang 17 which lead to code that previously worked in Clang 16 to suddently stop compiling. I have reduced it to the following snippet: ```cpp template <class T, class U> concept Test = true;

class UnwindFrame
{
template <Test<UnwindFrame> F>
friend void unwindStack();

UnwindFrame();

};

template <Test<UnwindFrame> F>
void unwindStack()
{
auto u = UnwindFrame();
}

int main()
{
unwindStack<int>();
}

https://godbolt.org/z/1vcqeGnG1

When using Clang 17 or newer the `friend` declaration seemingly has no effect causing a compilation failure due to the use of a private constructor:

<source>:16:14: error: calling a private constructor of class 'UnwindFrame'
16 | auto u = UnwindFrame();
| ^
<source>:21:5: note: in instantiation of function template specialization 'unwindStack<int>' requested here
21 | unwindStack<int>();
| ^
<source>:10:5: note: implicitly declared private here
10 | UnwindFrame();
| ^

This used to work in Clang 16 and is valid C++20 I believe.

While reducing, I noticed the following important properties for this to be reproducible: `unwindStack` HAS to be restricted by a concept and the concept HAS to be templated, with a template parameter being the enclosing class `UnwindFrame` in this case. 
</details>

@EugeneZelenko EugeneZelenko added c++20 concepts C++20 concepts labels Nov 7, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 7, 2023

@llvm/issue-subscribers-c-20

Author: Markus Böck (zero9178)

Recently upgraded to Clang 17 which lead to code that previously worked in Clang 16 to suddently stop compiling. I have reduced it to the following snippet: ```cpp template <class T, class U> concept Test = true;

class UnwindFrame
{
template <Test<UnwindFrame> F>
friend void unwindStack();

UnwindFrame();

};

template <Test<UnwindFrame> F>
void unwindStack()
{
auto u = UnwindFrame();
}

int main()
{
unwindStack<int>();
}

https://godbolt.org/z/1vcqeGnG1

When using Clang 17 or newer the `friend` declaration seemingly has no effect causing a compilation failure due to the use of a private constructor:

<source>:16:14: error: calling a private constructor of class 'UnwindFrame'
16 | auto u = UnwindFrame();
| ^
<source>:21:5: note: in instantiation of function template specialization 'unwindStack<int>' requested here
21 | unwindStack<int>();
| ^
<source>:10:5: note: implicitly declared private here
10 | UnwindFrame();
| ^

This used to work in Clang 16 and is valid C++20 I believe.

While reducing, I noticed the following important properties for this to be reproducible: `unwindStack` HAS to be restricted by a concept and the concept HAS to be templated, with a template parameter being the enclosing class `UnwindFrame` in this case. 
</details>

@shafik
Copy link
Collaborator

shafik commented Nov 8, 2023

@shafik shafik added the confirmed Verified by a second party label Nov 8, 2023
@zero9178 zero9178 changed the title [17 regression][C++20] templated friend not working if the concept use contains the enclosing friend [17 regression][C++20] templated friend not working if the concept use contains the enclosing class Nov 8, 2023
@joka921
Copy link

joka921 commented Nov 9, 2023

I recently ran into a similar issue:

  • Also uses a templated friend function where the concept uses the enclosing class
  • Same compiler versions (working in Clang 16 and all recent versions of GCC, broken in Clang 17 and the current trunk)
  • Different error (call is ambiguous).
#include <concepts>

struct S {
    template <std::same_as<S> T>
    friend void f(const T&);
};

template <std::same_as<S> T>
void f(const T& t) {}

int main() {
    f(S{});
}


Gives

<source>:13:5: error: call to 'f' is ambiguous
   13 |     f(S{});
      |     ^
<source>:6:17: note: candidate function [with T = S]
    6 |     friend void f(const T&);
      |                 ^
<source>:10:6: note: candidate function [with T = S]
   10 | void f(const T& t) {}

Please let me know if this is similar enough to also be discussed in this issue, or if I should open a new one.

@antangelo antangelo self-assigned this Nov 26, 2023
@antangelo
Copy link
Contributor

I believe the issue is with checking if the constraint uses any enclosing template parameters for [temp.friend]p9. If a containing record is used as argument, we assume that the constraint depends on enclosing template parameters without checking if the record is a template. In the above reproducers UnwindFrame and S are not template classes so this should not apply.

I'm not the most well versed with the standard though, so I'd appreciate if someone more experienced can give their input before I send a patch for this.

antangelo added a commit that referenced this issue Dec 7, 2023
…rs to containing template arguments (#74265)

When checking if the constraint uses any enclosing template parameters
for [temp.friend]p9, if a containing record is used as argument, we
assume that the constraint depends on enclosing template parameters
without checking if the record is templated. The reproducer from the bug
is included as a test case.

Fixes #71595
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++20 clang:frontend Language frontend issues, e.g. anything involving "Sema" concepts C++20 concepts confirmed Verified by a second party regression
Projects
Status: Done
6 participants