[clang] fb615cf - [Clang][Sema] Diagnose declarative nested-name-specifiers naming alias templates (#80842)

via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 20 10:25:16 PST 2024


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}}
 }


        


More information about the cfe-commits mailing list