Skip to content

Commit fb615cf

Browse files
authored
[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`).
1 parent c625b99 commit fb615cf

File tree

5 files changed

+61
-11
lines changed

5 files changed

+61
-11
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ Improvements to Clang's diagnostics
211211

212212
- Clang now diagnoses extraneous template parameter lists as a language extension.
213213

214+
- Clang now diagnoses declarative nested name specifiers that name alias templates.
215+
214216
Improvements to Clang's time-trace
215217
----------------------------------
216218

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8255,6 +8255,9 @@ def err_not_tag_in_scope : Error<
82558255
def ext_template_after_declarative_nns : ExtWarn<
82568256
"'template' cannot be used after a declarative nested name specifier">,
82578257
InGroup<DiagGroup<"template-in-declaration-name">>;
8258+
def ext_alias_template_in_declarative_nns : ExtWarn<
8259+
"a declarative nested name specifier cannot name an alias template">,
8260+
InGroup<DiagGroup<"alias-template-in-declaration-name">>;
82588261

82598262
def err_no_typeid_with_fno_rtti : Error<
82608263
"use of typeid requires -frtti">;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6210,6 +6210,8 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
62106210
SourceLocation Loc,
62116211
TemplateIdAnnotation *TemplateId,
62126212
bool IsMemberSpecialization) {
6213+
assert(SS.isValid() && "diagnoseQualifiedDeclaration called for declaration "
6214+
"without nested-name-specifier");
62136215
DeclContext *Cur = CurContext;
62146216
while (isa<LinkageSpecDecl>(Cur) || isa<CapturedDecl>(Cur))
62156217
Cur = Cur->getParent();
@@ -6298,22 +6300,36 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
62986300
<< FixItHint::CreateRemoval(TemplateId->TemplateKWLoc);
62996301

63006302
NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
6301-
while (SpecLoc.getPrefix()) {
6303+
do {
63026304
if (SpecLoc.getNestedNameSpecifier()->getKind() ==
63036305
NestedNameSpecifier::TypeSpecWithTemplate)
63046306
Diag(Loc, diag::ext_template_after_declarative_nns)
63056307
<< FixItHint::CreateRemoval(
63066308
SpecLoc.getTypeLoc().getTemplateKeywordLoc());
63076309

6308-
SpecLoc = SpecLoc.getPrefix();
6309-
}
6310-
// C++11 [dcl.meaning]p1:
6311-
// [...] "The nested-name-specifier of the qualified declarator-id shall
6312-
// not begin with a decltype-specifer"
6313-
if (isa_and_nonnull<DecltypeType>(
6314-
SpecLoc.getNestedNameSpecifier()->getAsType()))
6315-
Diag(Loc, diag::err_decltype_in_declarator)
6316-
<< SpecLoc.getTypeLoc().getSourceRange();
6310+
if (const Type *T = SpecLoc.getNestedNameSpecifier()->getAsType()) {
6311+
if (const auto *TST = T->getAsAdjusted<TemplateSpecializationType>()) {
6312+
// C++23 [expr.prim.id.qual]p3:
6313+
// [...] If a nested-name-specifier N is declarative and has a
6314+
// simple-template-id with a template argument list A that involves a
6315+
// template parameter, let T be the template nominated by N without A.
6316+
// T shall be a class template.
6317+
if (TST->isDependentType() && TST->isTypeAlias())
6318+
Diag(Loc, diag::ext_alias_template_in_declarative_nns)
6319+
<< SpecLoc.getLocalSourceRange();
6320+
} else if (T->isDecltypeType()) {
6321+
// C++23 [expr.prim.id.qual]p2:
6322+
// [...] A declarative nested-name-specifier shall not have a
6323+
// decltype-specifier.
6324+
//
6325+
// FIXME: This wording appears to be defective as it does not forbid
6326+
// declarative nested-name-specifiers with pack-index-specifiers.
6327+
// See https://github.com/cplusplus/CWG/issues/499.
6328+
Diag(Loc, diag::err_decltype_in_declarator)
6329+
<< SpecLoc.getTypeLoc().getSourceRange();
6330+
}
6331+
}
6332+
} while ((SpecLoc = SpecLoc.getPrefix()));
63176333

63186334
return false;
63196335
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 -verify %s
2+
3+
template<typename T>
4+
struct A {
5+
void f();
6+
};
7+
8+
template<typename T>
9+
using B = A<T>;
10+
11+
template<typename T>
12+
void B<T>::f() { } // expected-warning {{a declarative nested name specifier cannot name an alias template}}
13+
14+
template<>
15+
void B<int>::f() { } // ok, template argument list of simple-template-id doesn't involve template parameters
16+
17+
namespace N {
18+
19+
template<typename T>
20+
struct D {
21+
void f();
22+
};
23+
24+
template<typename T>
25+
using E = D<T>;
26+
}
27+
28+
template<typename T>
29+
void N::E<T>::f() { } // expected-warning {{a declarative nested name specifier cannot name an alias template}}

clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ namespace Example2 {
2626
void g();
2727
};
2828
template<class T> using B = A<T>;
29-
template<class T> void B<T>::g() {} // ok.
29+
template<class T> void B<T>::g() {} // // expected-warning {{a declarative nested name specifier cannot name an alias template}}
3030
}

0 commit comments

Comments
 (0)