diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 7ac0fa0141b47..8f217990a494c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -914,6 +914,7 @@ Bug Fixes to C++ Support - Clang now diagnoses explicit specializations with storage class specifiers in all contexts. - Fix an assertion failure caused by parsing a lambda used as a default argument for the value of a forward-declared class. (#GH93512). +- Clang now transforms qualifier of template name. (#91677). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index cf4e80399632b..6fd9f20647b0b 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4568,6 +4568,25 @@ TreeTransform::TransformTemplateName(CXXScopeSpec &SS, if (QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName()) { TemplateDecl *Template = QTN->getUnderlyingTemplate().getAsTemplateDecl(); assert(Template && "qualified template name must refer to a template"); + if (QTN->getQualifier()) { + CXXScopeSpec QualifierSS; + QualifierSS.MakeTrivial(getSema().getASTContext(), QTN->getQualifier(), + NameLoc); + NestedNameSpecifierLoc QualifierLoc = + QualifierSS.getWithLocInContext(getSema().getASTContext()); + NestedNameSpecifierLoc TransformedQualifierLoc = + getDerived().TransformNestedNameSpecifierLoc(QualifierLoc); + if (!TransformedQualifierLoc) + return Name; + if (getDerived().AlwaysRebuild() || + QualifierLoc != TransformedQualifierLoc) { + QualifierSS.Adopt(TransformedQualifierLoc); + return getDerived().RebuildTemplateName( + QualifierSS, QTN->hasTemplateKeyword() ? SourceLocation() : NameLoc, + *Template->getIdentifier(), NameLoc, ObjectType, + FirstQualifierInScope, /*AllowInjectedClassName=*/true); + } + } TemplateDecl *TransTemplate = cast_or_null(getDerived().TransformDecl(NameLoc, diff --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp index f98005c7e6fb5..4f447be06b926 100644 --- a/clang/test/SemaCXX/many-template-parameter-lists.cpp +++ b/clang/test/SemaCXX/many-template-parameter-lists.cpp @@ -5,7 +5,7 @@ template struct X { template - struct A { // expected-note {{not-yet-instantiated member is declared here}} + struct A { template struct B { template @@ -28,9 +28,9 @@ struct X { template template template - friend void A::template B::template C::template D::template E::operator+=(Z); // expected-warning {{not supported}} expected-error {{no member 'A' in 'X'; it has not yet been instantiated}} + friend void A::template B::template C::template D::template E::operator+=(Z); // expected-warning {{dependent nested name specifier 'A::template B::template C::template D::template E::' for friend class declaration is not supported; turning off access control for 'X'}} }; void test() { - X::A::B::C::D::E() += 1.0; // expected-note {{in instantiation of template class 'X' requested here}} + X::A::B::C::D::E() += 1.0; } diff --git a/clang/test/SemaTemplate/PR12884_original_no_crash.cpp b/clang/test/SemaTemplate/PR12884_original_no_crash.cpp new file mode 100644 index 0000000000000..f2b6f6e28c2f9 --- /dev/null +++ b/clang/test/SemaTemplate/PR12884_original_no_crash.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s + +namespace PR12884_original { + template struct A { + struct B { + template struct X {}; + typedef int arg; + }; + struct C { + typedef B::X x; // expected-error{{typename specifier refers to non-type member 'arg' in 'PR12884_original::A::B'}} + }; + }; + + template <> struct A::B { + template struct X {}; + static const int arg = 0; // expected-note{{referenced member 'arg' is declared here}} + }; + + A::C::x a; // expected-note{{in instantiation of member class 'PR12884_original::A::C' requested here}} +} diff --git a/clang/test/SemaTemplate/PR91677.cpp b/clang/test/SemaTemplate/PR91677.cpp new file mode 100644 index 0000000000000..cc8db60a438ea --- /dev/null +++ b/clang/test/SemaTemplate/PR91677.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s +// expected-no-diagnostics + +template struct t1 { + template + struct t2 {}; +}; + +template +t1::template t2 f1(); + +void f2() { + f1(); +} diff --git a/clang/test/SemaTemplate/typename-specifier-3.cpp b/clang/test/SemaTemplate/typename-specifier-3.cpp index 714830f0032d2..40f9430386773 100644 --- a/clang/test/SemaTemplate/typename-specifier-3.cpp +++ b/clang/test/SemaTemplate/typename-specifier-3.cpp @@ -28,16 +28,17 @@ namespace PR12884_original { typedef int arg; }; struct C { - typedef B::X x; // precxx17-warning{{missing 'typename' prior to dependent type name B::X; implicit 'typename' is a C++20 extension}} + typedef B::X x; // precxx17-warning{{missing 'typename' prior to dependent type name B::X; implicit 'typename' is a C++20 extension}} \ + expected-error{{typename specifier refers to non-type member 'arg' in 'PR12884_original::A::B'}} }; }; template <> struct A::B { template struct X {}; - static const int arg = 0; + static const int arg = 0; // expected-note{{referenced member 'arg' is declared here}} }; - A::C::x a; + A::C::x a; // expected-note{{in instantiation of member class 'PR12884_original::A::C' requested here}} } namespace PR12884_half_fixed { template struct A {