[clang] [Clang][Sema] Diagnose declarative nested-name-specifiers naming alias templates (PR #80842)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 6 06:56:02 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Krystian Stasiowski (sdkrystian)
<details>
<summary>Changes</summary>
According to [[expr.prim.id.qual] p3](http://eel.is/c++draft/expr.prim.id.qual#<!-- -->3):
> 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:
```cpp
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 a type alias template
```
This patch diagnoses such cases as an extension (in group `alias-template-in-declaration-name`).
---
Full diff: https://github.com/llvm/llvm-project/pull/80842.diff
4 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
- (modified) clang/lib/Sema/SemaDecl.cpp (+26-10)
- (added) clang/test/CXX/expr/expr.prim/expr.prim.id/expr.prim.id.qual/p3.cpp (+29)
- (modified) clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp (+1-1)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f76e7a3392183..d02fddd069341 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8250,6 +8250,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 481d952d2389b..464f3738b0ba5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6207,6 +6207,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();
@@ -6295,22 +6297,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 that begin with a
+ // pack-index-specifier.
+ 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 0000000000000..ba89d2c96c7bb
--- /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 = C<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 82983f05fe878..910dab11ee5e1 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}}
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/80842
More information about the cfe-commits
mailing list