Skip to content

Commit 75524a1

Browse files
committed
[Clang][Sema] Diagnose use of template keyword after declarative nested-name-specifiers
1 parent faef68b commit 75524a1

File tree

14 files changed

+201
-40
lines changed

14 files changed

+201
-40
lines changed

clang/include/clang/AST/TypeLoc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1688,7 +1688,7 @@ class TemplateSpecializationTypeLoc :
16881688
}
16891689

16901690
void initializeLocal(ASTContext &Context, SourceLocation Loc) {
1691-
setTemplateKeywordLoc(Loc);
1691+
setTemplateKeywordLoc(SourceLocation());
16921692
setTemplateNameLoc(Loc);
16931693
setLAngleLoc(Loc);
16941694
setRAngleLoc(Loc);

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8233,6 +8233,8 @@ def err_invalid_declarator_in_block : Error<
82338233
"definition or redeclaration of %0 not allowed inside a block">;
82348234
def err_not_tag_in_scope : Error<
82358235
"no %select{struct|interface|union|class|enum}0 named %1 in %2">;
8236+
def ext_template_after_declarative_nns : ExtWarn<
8237+
"'template' cannot be used after a declarative nested name specifier">;
82368238

82378239
def err_no_typeid_with_fno_rtti : Error<
82388240
"use of typeid requires -frtti">;

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2915,7 +2915,8 @@ class Sema final {
29152915
bool DiagnoseClassNameShadow(DeclContext *DC, DeclarationNameInfo Info);
29162916
bool diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
29172917
DeclarationName Name, SourceLocation Loc,
2918-
bool IsTemplateId);
2918+
TemplateIdAnnotation *TemplateId,
2919+
bool IsMemberSpecialization);
29192920
void
29202921
diagnoseIgnoredQualifiers(unsigned DiagID, unsigned Quals,
29212922
SourceLocation FallbackLoc,

clang/lib/Sema/SemaDecl.cpp

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6264,13 +6264,17 @@ bool Sema::DiagnoseClassNameShadow(DeclContext *DC,
62646264
///
62656265
/// \param Loc The location of the name of the entity being declared.
62666266
///
6267-
/// \param IsTemplateId Whether the name is a (simple-)template-id, and thus
6268-
/// we're declaring an explicit / partial specialization / instantiation.
6267+
/// \param IsMemberSpecialization Whether we are declaring a member
6268+
/// specialization.
6269+
///
6270+
/// \param TemplateId The template-id, if any.
62696271
///
62706272
/// \returns true if we cannot safely recover from this error, false otherwise.
62716273
bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
62726274
DeclarationName Name,
6273-
SourceLocation Loc, bool IsTemplateId) {
6275+
SourceLocation Loc,
6276+
TemplateIdAnnotation *TemplateId,
6277+
bool IsMemberSpecialization) {
62746278
DeclContext *Cur = CurContext;
62756279
while (isa<LinkageSpecDecl>(Cur) || isa<CapturedDecl>(Cur))
62766280
Cur = Cur->getParent();
@@ -6299,7 +6303,7 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
62996303
// Check whether the qualifying scope encloses the scope of the original
63006304
// declaration. For a template-id, we perform the checks in
63016305
// CheckTemplateSpecializationScope.
6302-
if (!Cur->Encloses(DC) && !IsTemplateId) {
6306+
if (!Cur->Encloses(DC) && !(TemplateId || IsMemberSpecialization)) {
63036307
if (Cur->isRecord())
63046308
Diag(Loc, diag::err_member_qualification)
63056309
<< Name << SS.getRange();
@@ -6345,12 +6349,42 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
63456349
return false;
63466350
}
63476351

6352+
// C++23 [temp.names]p5:
6353+
// The keyword template shall not appear immediately after a declarative
6354+
// nested-name-specifier.
6355+
if (TemplateId && TemplateId->TemplateKWLoc.isValid()) {
6356+
Diag(Loc, diag::ext_template_after_declarative_nns)
6357+
<< FixItHint::CreateRemoval(TemplateId->TemplateKWLoc);
6358+
}
6359+
6360+
NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
6361+
while (SpecLoc.getPrefix()) {
6362+
// C++23 [temp.names]p5:
6363+
// The keyword template shall not appear immediately after a declarative
6364+
// nested-name-specifier.
6365+
// FIXME: nested-name-specifiers in friend declarations are declarative,
6366+
// but we don't call diagnoseQualifiedDeclaration for them. We should.
6367+
if (SpecLoc.getNestedNameSpecifier()->getKind() ==
6368+
NestedNameSpecifier::TypeSpecWithTemplate) {
6369+
SourceLocation TemplateKWLoc;
6370+
auto TL = SpecLoc.getTypeLoc();
6371+
if (const auto DTSTL =
6372+
TL.getAsAdjusted<DependentTemplateSpecializationTypeLoc>())
6373+
TemplateKWLoc = DTSTL.getTemplateKeywordLoc();
6374+
else if (const auto TSTL =
6375+
TL.getAsAdjusted<TemplateSpecializationTypeLoc>())
6376+
TemplateKWLoc = TSTL.getTemplateKeywordLoc();
6377+
else
6378+
TemplateKWLoc = TL.getBeginLoc();
6379+
6380+
Diag(Loc, diag::ext_template_after_declarative_nns)
6381+
<< FixItHint::CreateRemoval(TemplateKWLoc);
6382+
}
6383+
SpecLoc = SpecLoc.getPrefix();
6384+
}
63486385
// C++11 [dcl.meaning]p1:
63496386
// [...] "The nested-name-specifier of the qualified declarator-id shall
63506387
// not begin with a decltype-specifer"
6351-
NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
6352-
while (SpecLoc.getPrefix())
6353-
SpecLoc = SpecLoc.getPrefix();
63546388
if (isa_and_nonnull<DecltypeType>(
63556389
SpecLoc.getNestedNameSpecifier()->getAsType()))
63566390
Diag(Loc, diag::err_decltype_in_declarator)
@@ -6418,9 +6452,13 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
64186452
return nullptr;
64196453
}
64206454
if (!D.getDeclSpec().isFriendSpecified()) {
6421-
if (diagnoseQualifiedDeclaration(
6422-
D.getCXXScopeSpec(), DC, Name, D.getIdentifierLoc(),
6423-
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId)) {
6455+
TemplateIdAnnotation *TemplateId =
6456+
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
6457+
? D.getName().TemplateId
6458+
: nullptr;
6459+
if (diagnoseQualifiedDeclaration(D.getCXXScopeSpec(), DC, Name,
6460+
D.getIdentifierLoc(), TemplateId,
6461+
/*IsMemberSpecialization=*/false)) {
64246462
if (DC->isRecord())
64256463
return nullptr;
64266464

@@ -18004,6 +18042,7 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
1800418042
// nested-name-specifier against the current context.
1800518043
if ((TUK == TUK_Definition || TUK == TUK_Declaration) &&
1800618044
diagnoseQualifiedDeclaration(SS, DC, OrigName, Loc,
18045+
/*TemplateId=*/nullptr,
1800718046
isMemberSpecialization))
1800818047
Invalid = true;
1800918048

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3621,14 +3621,18 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
36213621
// class X {
36223622
// int X::member;
36233623
// };
3624-
if (DeclContext *DC = computeDeclContext(SS, false))
3624+
if (DeclContext *DC = computeDeclContext(SS, false)) {
3625+
TemplateIdAnnotation *TemplateId =
3626+
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
3627+
? D.getName().TemplateId
3628+
: nullptr;
36253629
diagnoseQualifiedDeclaration(SS, DC, Name, D.getIdentifierLoc(),
3626-
D.getName().getKind() ==
3627-
UnqualifiedIdKind::IK_TemplateId);
3628-
else
3630+
TemplateId,
3631+
/*IsMemberSpecialization=*/false);
3632+
} else {
36293633
Diag(D.getIdentifierLoc(), diag::err_member_qualification)
36303634
<< Name << SS.getRange();
3631-
3635+
}
36323636
SS.clear();
36333637
}
36343638

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,8 +1890,12 @@ DeclResult Sema::CheckClassTemplate(
18901890
ContextRAII SavedContext(*this, SemanticContext);
18911891
if (RebuildTemplateParamsInCurrentInstantiation(TemplateParams))
18921892
Invalid = true;
1893-
} else if (TUK != TUK_Friend && TUK != TUK_Reference)
1894-
diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc, false);
1893+
}
1894+
1895+
if (TUK != TUK_Friend && TUK != TUK_Reference)
1896+
diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc,
1897+
/*TemplateId-*/ nullptr,
1898+
/*IsMemberSpecialization*/ false);
18951899

18961900
LookupQualifiedName(Previous, SemanticContext);
18971901
} else {
@@ -8826,6 +8830,15 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
88268830
bool isMemberSpecialization = false;
88278831
bool isPartialSpecialization = false;
88288832

8833+
if (SS.isSet()) {
8834+
if (TUK != TUK_Reference && TUK != TUK_Friend &&
8835+
diagnoseQualifiedDeclaration(SS, ClassTemplate->getDeclContext(),
8836+
ClassTemplate->getDeclName(),
8837+
TemplateNameLoc, &TemplateId,
8838+
/*IsMemberSpecialization=*/false))
8839+
return true;
8840+
}
8841+
88298842
// Check the validity of the template headers that introduce this
88308843
// template.
88318844
// FIXME: We probably shouldn't complain about these headers for

clang/lib/Sema/TreeTransform.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4377,8 +4377,14 @@ NestedNameSpecifierLoc TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
43774377
SS.Adopt(ETL.getQualifierLoc());
43784378
TL = ETL.getNamedTypeLoc();
43794379
}
4380-
SS.Extend(SemaRef.Context, /*FIXME:*/ SourceLocation(), TL,
4381-
Q.getLocalEndLoc());
4380+
SourceLocation TemplateKWLoc;
4381+
if (const auto TSTL = TL.getAs<TemplateSpecializationTypeLoc>())
4382+
TemplateKWLoc = TSTL.getTemplateKeywordLoc();
4383+
else if (const auto DTSTL =
4384+
TL.getAs<DependentTemplateSpecializationTypeLoc>())
4385+
TemplateKWLoc = DTSTL.getTemplateKeywordLoc();
4386+
4387+
SS.Extend(SemaRef.Context, TemplateKWLoc, TL, Q.getLocalEndLoc());
43824388
break;
43834389
}
43844390
// If the nested-name-specifier is an invalid type def, don't emit an

clang/test/CXX/drs/dr23xx.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ struct Bad2 { int a, b; };
182182
} // namespace dr2386
183183
namespace std {
184184
template <typename T> struct tuple_size;
185-
template <> struct std::tuple_size<dr2386::Bad1> {};
186-
template <> struct std::tuple_size<dr2386::Bad2> {
185+
template <> struct tuple_size<dr2386::Bad1> {};
186+
template <> struct tuple_size<dr2386::Bad2> {
187187
static const int value = 42;
188188
};
189189
} // namespace std

clang/test/CXX/drs/dr7xx.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ namespace dr727 { // dr727: partial
105105
// expected-note@#dr727-N {{explicitly specialized declaration is here}}
106106

107107
template<> struct A::C<double>;
108-
// expected-error@-1 {{class template specialization of 'C' not in class 'A' or an enclosing namespace}}
108+
// expected-error@-1 {{non-friend class member 'C' cannot have a qualified name}}
109+
// expected-error@-2 {{class template specialization of 'C' not in class 'A' or an enclosing namespace}}
109110
// expected-note@#dr727-C {{explicitly specialized declaration is here}}
110111
template<> void A::f<double>();
111112
// expected-error@-1 {{o function template matches function template specialization 'f'}}
@@ -116,7 +117,8 @@ namespace dr727 { // dr727: partial
116117
// expected-note@#dr727-N {{explicitly specialized declaration is here}}
117118

118119
template<typename T> struct A::C<T***>;
119-
// expected-error@-1 {{class template partial specialization of 'C' not in class 'A' or an enclosing namespace}}
120+
// expected-error@-1 {{non-friend class member 'C' cannot have a qualified name}}
121+
// expected-error@-2 {{class template partial specialization of 'C' not in class 'A' or an enclosing namespace}}
120122
// expected-note@#dr727-C {{explicitly specialized declaration is here}}
121123
template<typename T> static int A::N<T***>;
122124
// expected-error@-1 {{non-friend class member 'N' cannot have a qualified name}}

clang/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@ template<typename T, typename U> // expected-note{{previous template}}
33
class X0 {
44
public:
55
typedef int size_type;
6-
6+
77
X0(int);
88
~X0();
9-
9+
1010
void f0(const T&, const U&);
11-
11+
1212
T& operator[](int i) const;
13-
13+
1414
void f1(size_type) const;
1515
void f2(size_type) const;
1616
void f3(size_type) const;
1717
void f4() ;
18-
18+
1919
operator T*() const;
20-
20+
2121
T value;
2222
};
2323

@@ -41,7 +41,7 @@ template<class X, class Y, class Z> // expected-error{{too many template paramet
4141
void X0<X, Y>::f3(size_type) const {
4242
}
4343

44-
template<class X, class Y>
44+
template<class X, class Y>
4545
void X0<Y, X>::f4() { } // expected-error{{does not refer}}
4646

4747
// FIXME: error message should probably say, "redefinition of 'X0<T, U>::f0'"
@@ -68,14 +68,14 @@ namespace N { template <class X> void A<X>::a() {} }
6868

6969
// PR5566
7070
template<typename T>
71-
struct X1 {
71+
struct X1 {
7272
template<typename U>
7373
struct B { void f(); };
7474
};
7575

7676
template<typename T>
7777
template<typename U>
78-
void X1<T>::template B<U>::f() { }
78+
void X1<T>::template B<U>::f() { } // expected-warning{{'template' cannot be used after a declarative nested name specifier}}
7979

8080
// PR5527
8181
template <template <class> class T>

clang/test/CXX/temp/temp.names/p5.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// RUN: %clang_cc1 -fsyntax-only -pedantic-errors -verify %s
2+
3+
template<typename T> struct A {
4+
template<typename U> struct B {
5+
struct C;
6+
template<typename V> struct D;
7+
template<typename V> struct D<V*>;
8+
9+
void f();
10+
template<typename V> void g();
11+
12+
static int x;
13+
template<typename V> static int y;
14+
template<typename V> static int y<V*>;
15+
16+
enum class E;
17+
};
18+
};
19+
20+
template<typename T>
21+
template<typename U>
22+
struct A<T>::template B<U>::C { }; // expected-error{{'template' cannot be used after a declarative}}
23+
24+
template<>
25+
template<>
26+
struct A<int>::template B<bool>::C; // expected-error{{'template' cannot be used after a declarative}}
27+
28+
template<>
29+
template<>
30+
struct A<int>::template B<bool>::C { }; // expected-error{{'template' cannot be used after a declarative}}
31+
32+
template<typename T>
33+
template<typename U>
34+
template<typename V>
35+
struct A<T>::template B<U>::D { }; // expected-error{{'template' cannot be used after a declarative}}
36+
37+
template<typename T>
38+
template<typename U>
39+
template<typename V>
40+
struct A<T>::template B<U>::D<V*> { }; // expected-error{{'template' cannot be used after a declarative}}
41+
42+
template<>
43+
template<>
44+
template<typename V>
45+
struct A<int>::template B<bool>::D { }; // expected-error{{'template' cannot be used after a declarative}}
46+
47+
template<>
48+
template<>
49+
template<typename V>
50+
struct A<int>::template B<bool>::D<V*> { }; // expected-error{{'template' cannot be used after a declarative}}
51+
52+
template<typename T>
53+
template<typename U>
54+
void A<T>::template B<U>::f() { } // expected-error{{'template' cannot be used after a declarative}}
55+
56+
template<>
57+
template<>
58+
void A<int>::template B<bool>::f() { } // expected-error{{'template' cannot be used after a declarative}}
59+
60+
template<typename T>
61+
template<typename U>
62+
template<typename V>
63+
void A<T>::template B<U>::g() { } // expected-error{{'template' cannot be used after a declarative}}
64+
65+
template<>
66+
template<>
67+
template<typename V>
68+
void A<int>::template B<bool>::g() { } // expected-error{{'template' cannot be used after a declarative}}
69+
70+
template<typename T>
71+
template<typename U>
72+
int A<T>::template B<U>::x = 0; // expected-error{{'template' cannot be used after a declarative}}
73+
74+
template<typename T>
75+
template<typename U>
76+
template<typename V>
77+
int A<T>::template B<U>::y = 0; // expected-error{{'template' cannot be used after a declarative}}
78+
79+
template<typename T>
80+
template<typename U>
81+
template<typename V>
82+
int A<T>::template B<U>::y<V*> = 0; // expected-error{{'template' cannot be used after a declarative}}
83+
84+
template<typename T>
85+
template<typename U>
86+
enum class A<T>::template B<U>::E { a }; // expected-error{{'template' cannot be used after a declarative}}
87+
88+
template<>
89+
template<>
90+
enum class A<int>::template B<bool>::E; // expected-error{{'template' cannot be used after a declarative}}
91+
92+
template<>
93+
template<>
94+
enum class A<int>::template B<bool>::E { a }; // expected-error{{'template' cannot be used after a declarative}}

clang/test/CXX/temp/temp.spec/part.spec.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,4 +478,4 @@ template <typename T> class PCTT6<TestClass::PrivateClass, T> {
478478
};
479479
template <typename T1> template <typename, typename> class PCTT6<TestClass::PrivateClass, T1>::NCT4 final {};
480480
// expected-error@+1 2{{is a private member of}}
481-
template <typename T1> template <typename T2> struct PCTT6<TestClass::PrivateClass, T1>::template NCT3<T2, TestClass::TemplatePrivateClass<TestClass::TemplateProtectedClass<TestClass::PublicClass>>> : PCTT6<TestClass::PrivateClass, T1>::NCT4<T2, TestClass::TemplatePrivateClass<int>> {};
481+
template <typename T1> template <typename T2> struct PCTT6<TestClass::PrivateClass, T1>::NCT3<T2, TestClass::TemplatePrivateClass<TestClass::TemplateProtectedClass<TestClass::PublicClass>>> : PCTT6<TestClass::PrivateClass, T1>::NCT4<T2, TestClass::TemplatePrivateClass<int>> {};

clang/test/SemaCXX/static-assert.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ struct NestedTemplates1 {
197197
template <typename T, typename U, int a>
198198
void foo2() {
199199
static_assert(::ns::NestedTemplates1<T, a>::NestedTemplates2::template NestedTemplates3<U>::value, "message");
200-
// expected-error@-1{{static assertion failed due to requirement '::ns::NestedTemplates1<int, 3>::NestedTemplates2::NestedTemplates3<float>::value': message}}
200+
// expected-error@-1{{static assertion failed due to requirement '::ns::NestedTemplates1<int, 3>::NestedTemplates2::template NestedTemplates3<float>::value': message}}
201201
}
202202
template void foo2<int, float, 3>();
203203
// expected-note@-1{{in instantiation of function template specialization 'foo2<int, float, 3>' requested here}}

0 commit comments

Comments
 (0)