Skip to content
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

[clang-16 regression] Declaration of a constrained friend class inside of a class template causes compile error #61763

Closed
loskutov opened this issue Mar 28, 2023 · 11 comments · Fixed by #69244
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" regression

Comments

@loskutov
Copy link
Contributor

loskutov commented Mar 28, 2023

#include <concepts>

template <class = void>
struct Foo {
    template <std::same_as<void> Param>
    friend struct Bar;
};

template struct Foo<>;

template <std::same_as<void> Param>
struct Bar {
};

Clang 16 and further reject this program with the following error message:

<source>:11:11: error: type constraint differs in template redeclaration
template <std::same_as<void> Param>
          ^
<source>:5:15: note: previous template declaration is here
    template <std::same_as<void> Param>
              ^
1 error generated.
Compiler returned: 1

Clang versions 11-15 work as expected, though.
Compiler Explorer link: https://godbolt.org/z/erGavP1za.

GCC also had a similar bug at some point: 93467.

@loskutov loskutov changed the title [clang-16 regression] Friend declaration of a constrained friend class inside of a class template causes compile error [clang-16 regression] Declaration of a constrained friend class inside of a class template causes compile error Mar 28, 2023
@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" and removed new issue labels Mar 28, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Mar 28, 2023

@llvm/issue-subscribers-clang-frontend

@shafik
Copy link
Collaborator

shafik commented Mar 28, 2023

Confirmed, looks like we had a similar issue but it no longer reproduces: #50643 I think the friend is messing up this particular case.

CC @erichkeane @royjacobson

@shafik
Copy link
Collaborator

shafik commented Mar 28, 2023

@erichkeane Looks like babdef2 probably fixed the first case but not this one.

@erichkeane
Copy link
Collaborator

Right, there is work being done on trunk, there is a review up on phab (which I cannot seem to find!) that redoes how a lot of this works, but @zygoloid had a bunch of comments, so we're waiting on him to review.

@VoidPhantom
Copy link

The given example can be worked around by adding a forward declaration of struct Bar, so that the friend declaration is no longer the first occurence of Bar. However, there is no similar workaround for (what appears to be) a variation of the same bug involving nested classes:

template<typename T>
concept ok = true;

struct outer {
	template<typename T>
	requires ok<T>
	struct foo {};
};

template<typename U>
struct bar {
	template<typename T>
	requires ok<T>
	friend struct outer::foo;
};

bar<int> x;
$ clang++-16 --std=c++20 -Wall -Wextra main.cpp -o test
main.cpp:13:11: error: requires clause differs in template redeclaration
        requires ok<T>
                 ^
main.cpp:17:10: note: in instantiation of template class 'bar<int>' requested here
bar<int> x;
         ^
main.cpp:6:11: note: previous template declaration is here
        requires ok<T>
                 ^
1 error generated.

Godbolt

@BenBrock
Copy link

This issue appears to prevent Clang from compiling parts of ranges in libstdc++. In particular, there's a constrained friend declaration in zip_transform_view that causes issues. I tried massaging libstdc++'s implementation, and unfortunately the forward declaration workaround does not seem to work here.

@erichkeane
Copy link
Collaborator

So the problem here is that we're doing no work to try to make these two 'equal' despite their different depths because we aren't including the associated declaration. This is because we're using the TemplateParameterListsAreEqual that doesn't take the associated declarations in CheckClassTemplate (SemaTemplate.cpp ~1997).

We probably have to try harder there to figure out what the correct declaration these are associated with are.

@CaseyCarter
Copy link
Member

This issue appears to prevent Clang from compiling parts of ranges in libstdc++.

MSVCSTL makes some private parts public to workaround this issue:

@erichkeane
Copy link
Collaborator

One of the more difficult problems in CheckClassTemplate is going to be that we check the template arguments before the new template is constructed. The PrevClassTemplate works for the 'before', but we likely need to figure out how to move the template parameter list checking 'later'? I don't have a good idea how that looks unfortunately.

@erichkeane
Copy link
Collaborator

An ALTERNATIVE is to find a way to implement TemplateParameterListsAreEqual and AreConstraintExprsEqual in terms of the 'containing' decl context in this case (that is, to automatically just re-insert the same template arguments, then do the normal substitution as before). This is only possible when the new Decl is an uninstantiated template, but that should be OK, since there aren't any valid arguments to be substituting anyway. That way we could pass PrevClassTemplate for Old, and this new situation for New. Others are encouraged to think on this, else I'll think about it over the weekend.

@erichkeane
Copy link
Collaborator

An ALTERNATIVE is to find a way to implement TemplateParameterListsAreEqual and AreConstraintExprsEqual in terms of the 'containing' decl context in this case (that is, to automatically just re-insert the same template arguments, then do the normal substitution as before). This is only possible when the new Decl is an uninstantiated template, but that should be OK, since there aren't any valid arguments to be substituting anyway. That way we could pass PrevClassTemplate for Old, and this new situation for New. Others are encouraged to think on this, else I'll think about it over the weekend.

I ended up deciding that we could figure out how to do the substitution with just the decl-context, so I submitted a patch to support that in this case. WE might end up having a few other cases like this we have to deal with in the future, but this ends up fixing both repros in this issue.

erichkeane added a commit that referenced this issue Oct 18, 2023
…69244)

Out of line class template declaration specializations aren't created at
the time they have their template arguments checked, so we previously
weren't doing any amount of work to substitute the constraints before
comparison. This resulted in the out of line definition's difference in
'depth' causing the constraints to compare differently.

This patch corrects that. Additionally, it handles ClassTemplateDecl
when collecting template arguments.

Fixes: #61763
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" regression
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

9 participants