Author: Krystian Stasiowski Date: 2024-02-20T13:25:12-05:00 New Revision: fb615cf3b9c2d887441a4c0cca326eddc592351a
URL: https://github.com/llvm/llvm-project/commit/fb615cf3b9c2d887441a4c0cca326eddc592351a DIFF: https://github.com/llvm/llvm-project/commit/fb615cf3b9c2d887441a4c0cca326eddc592351a.diff LOG: [Clang][Sema] Diagnose declarative nested-name-specifiers naming alias templates (#80842) According to [expr.prim.id.qual] p3: > The _nested-name-specifier_ `::` nominates the global namespace. A _nested-name-specifier_ with a _computed-type-specifier_ nominates the type denoted by the _computed-type-specifier_, which shall be a class or enumeration type. **If a _nested-name-specifier_ `N` is declarative and has a _simple-template-id_ with a template argument list `A` that involves a template parameter, let `T` be the template nominated by `N` without `A`. `T` shall be a class template.** Meaning, the out-of-line definition of `A::f` in the following example is ill-formed: ``` template<typename T> struct A { void f(); }; template<typename T> using B = A<T>; template<typename T> void B<T>::f() { } // error: a declarative nested name specifier cannot name an alias template ``` This patch diagnoses such cases as an extension (in group `alias-template-in-declaration-name`). Added: clang/test/CXX/expr/expr.prim/expr.prim.id/expr.prim.id.qual/p3.cpp Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/SemaDecl.cpp clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index dfc56fc9f6d2c8..5bca2c965c866b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -211,6 +211,8 @@ Improvements to Clang's diagnostics - Clang now diagnoses extraneous template parameter lists as a language extension. +- Clang now diagnoses declarative nested name specifiers that name alias templates. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 47e892e34260c3..11411883e1bfc6 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8255,6 +8255,9 @@ def err_not_tag_in_scope : Error< def ext_template_after_declarative_nns : ExtWarn< "'template' cannot be used after a declarative nested name specifier">, InGroup<DiagGroup<"template-in-declaration-name">>; +def ext_alias_template_in_declarative_nns : ExtWarn< + "a declarative nested name specifier cannot name an alias template">, + InGroup<DiagGroup<"alias-template-in-declaration-name">>; def err_no_typeid_with_fno_rtti : Error< "use of typeid requires -frtti">; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 558109dc089150..10b5c271f25c10 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6210,6 +6210,8 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC, SourceLocation Loc, TemplateIdAnnotation *TemplateId, bool IsMemberSpecialization) { + assert(SS.isValid() && "diagnoseQualifiedDeclaration called for declaration " + "without nested-name-specifier"); DeclContext *Cur = CurContext; while (isa<LinkageSpecDecl>(Cur) || isa<CapturedDecl>(Cur)) Cur = Cur->getParent(); @@ -6298,22 +6300,36 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC, << FixItHint::CreateRemoval(TemplateId->TemplateKWLoc); NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data()); - while (SpecLoc.getPrefix()) { + do { if (SpecLoc.getNestedNameSpecifier()->getKind() == NestedNameSpecifier::TypeSpecWithTemplate) Diag(Loc, diag::ext_template_after_declarative_nns) << FixItHint::CreateRemoval( SpecLoc.getTypeLoc().getTemplateKeywordLoc()); - SpecLoc = SpecLoc.getPrefix(); - } - // C++11 [dcl.meaning]p1: - // [...] "The nested-name-specifier of the qualified declarator-id shall - // not begin with a decltype-specifer" - if (isa_and_nonnull<DecltypeType>( - SpecLoc.getNestedNameSpecifier()->getAsType())) - Diag(Loc, diag::err_decltype_in_declarator) - << SpecLoc.getTypeLoc().getSourceRange(); + if (const Type *T = SpecLoc.getNestedNameSpecifier()->getAsType()) { + if (const auto *TST = T->getAsAdjusted<TemplateSpecializationType>()) { + // C++23 [expr.prim.id.qual]p3: + // [...] If a nested-name-specifier N is declarative and has a + // simple-template-id with a template argument list A that involves a + // template parameter, let T be the template nominated by N without A. + // T shall be a class template. + if (TST->isDependentType() && TST->isTypeAlias()) + Diag(Loc, diag::ext_alias_template_in_declarative_nns) + << SpecLoc.getLocalSourceRange(); + } else if (T->isDecltypeType()) { + // C++23 [expr.prim.id.qual]p2: + // [...] A declarative nested-name-specifier shall not have a + // decltype-specifier. + // + // FIXME: This wording appears to be defective as it does not forbid + // declarative nested-name-specifiers with pack-index-specifiers. + // See https://github.com/cplusplus/CWG/issues/499. + Diag(Loc, diag::err_decltype_in_declarator) + << SpecLoc.getTypeLoc().getSourceRange(); + } + } + } while ((SpecLoc = SpecLoc.getPrefix())); return false; } diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.id/expr.prim.id.qual/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.id/expr.prim.id.qual/p3.cpp new file mode 100644 index 00000000000000..c73ffa55a26a31 --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.id/expr.prim.id.qual/p3.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -verify %s + +template<typename T> +struct A { + void f(); +}; + +template<typename T> +using B = A<T>; + +template<typename T> +void B<T>::f() { } // expected-warning {{a declarative nested name specifier cannot name an alias template}} + +template<> +void B<int>::f() { } // ok, template argument list of simple-template-id doesn't involve template parameters + +namespace N { + + template<typename T> + struct D { + void f(); + }; + + template<typename T> + using E = D<T>; +} + +template<typename T> +void N::E<T>::f() { } // expected-warning {{a declarative nested name specifier cannot name an alias template}} diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp index 82983f05fe8782..910dab11ee5e18 100644 --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp @@ -26,5 +26,5 @@ namespace Example2 { void g(); }; template<class T> using B = A<T>; - template<class T> void B<T>::g() {} // ok. + template<class T> void B<T>::g() {} // // expected-warning {{a declarative nested name specifier cannot name an alias template}} } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits