Skip to content

Commit 99823bc

Browse files
committed
[Clang][Sema] Allow elaborated-type-specifiers that declare member class template explict specializations
1 parent 340054e commit 99823bc

File tree

12 files changed

+140
-31
lines changed

12 files changed

+140
-31
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,8 @@ Bug Fixes in This Version
821821
- Fix an issue with missing symbol definitions when the first coroutine
822822
statement appears in a discarded ``if constexpr`` branch.
823823
Fixes (`#78290 <https://github.com/llvm/llvm-project/issues/78290>`_)
824+
- Clang now accepts elaborated-type-specifiers that explicitly specialize
825+
a member class template for an implicit instantiation of a class template.
824826

825827
Bug Fixes to Compiler Builtins
826828
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7129,8 +7129,7 @@ def warn_standalone_specifier : Warning<"'%0' ignored on this declaration">,
71297129
def ext_standalone_specifier : ExtWarn<"'%0' is not permitted on a declaration "
71307130
"of a type">, InGroup<MissingDeclarations>;
71317131
def err_standalone_class_nested_name_specifier : Error<
7132-
"forward declaration of %select{class|struct|interface|union|enum|enum class|enum struct}0 cannot "
7133-
"have a nested name specifier">;
7132+
"forward declaration of %0 cannot have a nested name specifier">;
71347133
def err_typecheck_sclass_func : Error<"illegal storage class on function">;
71357134
def err_static_block_func : Error<
71367135
"function declared in block scope cannot have 'static' storage class">;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5206,25 +5206,6 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
52065206
return ActOnFriendTypeDecl(S, DS, TemplateParams);
52075207
}
52085208

5209-
const CXXScopeSpec &SS = DS.getTypeSpecScope();
5210-
bool IsExplicitSpecialization =
5211-
!TemplateParams.empty() && TemplateParams.back()->size() == 0;
5212-
if (Tag && SS.isNotEmpty() && !Tag->isCompleteDefinition() &&
5213-
!IsExplicitInstantiation && !IsExplicitSpecialization &&
5214-
!isa<ClassTemplatePartialSpecializationDecl>(Tag)) {
5215-
// Per C++ [dcl.type.elab]p1, a class declaration cannot have a
5216-
// nested-name-specifier unless it is an explicit instantiation
5217-
// or an explicit specialization.
5218-
//
5219-
// FIXME: We allow class template partial specializations here too, per the
5220-
// obvious intent of DR1819.
5221-
//
5222-
// Per C++ [dcl.enum]p1, an opaque-enum-declaration can't either.
5223-
Diag(SS.getBeginLoc(), diag::err_standalone_class_nested_name_specifier)
5224-
<< GetDiagnosticTypeSpecifierID(DS) << SS.getRange();
5225-
return nullptr;
5226-
}
5227-
52285209
// Track whether this decl-specifier declares anything.
52295210
bool DeclaresAnything = true;
52305211

@@ -17181,10 +17162,31 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
1718117162
// for non-C++ cases.
1718217163
if (TemplateParameterLists.size() > 0 ||
1718317164
(SS.isNotEmpty() && TUK != TUK_Reference)) {
17184-
if (TemplateParameterList *TemplateParams =
17185-
MatchTemplateParametersToScopeSpecifier(
17186-
KWLoc, NameLoc, SS, nullptr, TemplateParameterLists,
17187-
TUK == TUK_Friend, isMemberSpecialization, Invalid)) {
17165+
TemplateParameterList *TemplateParams =
17166+
MatchTemplateParametersToScopeSpecifier(
17167+
KWLoc, NameLoc, SS, nullptr, TemplateParameterLists,
17168+
TUK == TUK_Friend, isMemberSpecialization, Invalid);
17169+
17170+
// C++23 [dcl.type.elab] p2:
17171+
// If an elaborated-type-specifier is the sole constituent of a
17172+
// declaration, the declaration is ill-formed unless it is an explicit
17173+
// specialization, an explicit instantiation or it has one of the
17174+
// following forms: [...]
17175+
// C++23 [dcl.enum] p1:
17176+
// If the enum-head-name of an opaque-enum-declaration contains a
17177+
// nested-name-specifier, the declaration shall be an explicit
17178+
// specialization.
17179+
//
17180+
// FIXME: Class template partial specializations can be forward declared
17181+
// per CWG2213, but the resolution failed to allow qualified forward
17182+
// declarations. This is almost certainly unintentional, so we allow them.
17183+
if (TUK == TUK_Declaration && SS.isNotEmpty() && !isMemberSpecialization) {
17184+
Diag(SS.getBeginLoc(), diag::err_standalone_class_nested_name_specifier)
17185+
<< TypeWithKeyword::getTagTypeKindName(Kind) << SS.getRange();
17186+
// Invalid = true;
17187+
}
17188+
17189+
if (TemplateParams) {
1718817190
if (Kind == TagTypeKind::Enum) {
1718917191
Diag(KWLoc, diag::err_enum_template);
1719017192
return true;

clang/test/CXX/class.access/p4.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ namespace test21 {
611611
template <class T> class A<T>::Inner {};
612612
class B {
613613
template <class T> class A<T>::Inner; // expected-error{{non-friend class member 'Inner' cannot have a qualified name}}
614+
// expected-error@-1{{forward declaration of class cannot have a nested name specifier}}
614615
};
615616

616617
void test() {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %clang_cc1 -verify %s -std=c++11
2+
3+
template<typename T>
4+
struct S0 {
5+
enum E0 : int;
6+
7+
enum class E1;
8+
};
9+
10+
struct S3 {
11+
enum E2 : int;
12+
13+
enum class E3;
14+
};
15+
16+
template<typename T>
17+
enum S0<T>::E0 : int; // expected-error{{cannot have a nested name specifier}}
18+
19+
template<>
20+
enum S0<int>::E0 : int;
21+
22+
template<typename T>
23+
enum class S0<T>::E1; // expected-error{{cannot have a nested name specifier}}
24+
25+
template<>
26+
enum class S0<int>::E1;
27+
28+
enum S3::E2 : int; // expected-error{{cannot have a nested name specifier}}
29+
30+
enum class S3::E3; // expected-error{{cannot have a nested name specifier}}

clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p1.cpp

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,75 @@ template<> struct N::B<int>;
1616
template struct N::B<float>;
1717

1818
template<typename T> struct C;
19-
template<typename T> struct C<T*>; // FIXME: This is technically ill-formed, but that's not the intent.
19+
template<typename T> struct C<T*>;
2020
template<> struct C<int>;
2121
template struct C<float>;
2222

2323
template<typename T> struct D::A; // expected-error {{cannot have a nested name specifier}}
2424
template<typename T> struct D::A<T*>; // FIXME: This is technically ill-formed, but that's not the intent.
2525
template<> struct D::A<int>;
2626
template struct D::A<float>;
27+
28+
namespace qualified_decl {
29+
template<typename T>
30+
struct S0 {
31+
struct S1;
32+
33+
template<typename U>
34+
struct S2;
35+
36+
enum E0 : int;
37+
38+
enum class E1;
39+
};
40+
41+
struct S3 {
42+
struct S4;
43+
44+
template<typename T>
45+
struct S5;
46+
47+
enum E2 : int;
48+
49+
enum class E3;
50+
};
51+
52+
template<typename T>
53+
struct S0<T>::S1; // expected-error{{cannot have a nested name specifier}}
54+
55+
template<>
56+
struct S0<int>::S1;
57+
58+
template<typename T>
59+
template<typename U>
60+
struct S0<T>::S2; // expected-error{{cannot have a nested name specifier}}
61+
62+
template<typename T>
63+
template<typename U>
64+
struct S0<T>::S2<U*>;
65+
66+
template<>
67+
template<>
68+
struct S0<int>::S2<bool>;
69+
70+
template<>
71+
template<typename U>
72+
struct S0<int>::S2;
73+
74+
struct S3::S4; // expected-error{{cannot have a nested name specifier}}
75+
76+
template<typename T>
77+
struct S3::S5; // expected-error{{cannot have a nested name specifier}}
78+
79+
struct S3::S4 f0();
80+
enum S0<long>::E0 f1();
81+
enum S0<long>::E1 f2();
82+
enum S3::E2 f3();
83+
enum S3::E3 f4();
84+
85+
using A0 = struct S3::S4;
86+
using A1 = enum S0<long>::E0;
87+
using A2 = enum S0<long>::E1;
88+
using A3 = enum S3::E2;
89+
using A4 = enum S3::E3;
90+
}

clang/test/CXX/drs/dr16xx.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ namespace dr1638 { // dr1638: 3.1
9999

100100
enum class A<unsigned>::E;
101101
// since-cxx11-error@-1 {{template specialization requires 'template<>'}}
102-
// since-cxx11-error@-2 {{forward declaration of enum class cannot have a nested name specifier}}
103102
template enum class A<unsigned>::E;
104103
// since-cxx11-error@-1 {{enumerations cannot be explicitly instantiated}}
105104
enum class A<unsigned>::E *e;

clang/test/CXX/module/module.interface/p2-2.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct X {
1515
};
1616

1717
export template <typename T> struct X<T>::iterator; // expected-error {{cannot export 'iterator' as it is not at namespace scope}}
18+
// expected-error@-1 {{forward declaration of struct cannot have a nested name specifier}}
1819
export template <typename T> void X<T>::foo(); // expected-error {{cannot export 'foo' as it is not at namespace scope}}
1920
export template <typename T> template <typename U> U X<T>::bar(); // expected-error {{cannot export 'bar' as it is not at namespace scope}}
2021

@@ -28,10 +29,13 @@ export struct Y {
2829
};
2930

3031
export struct Y::iterator; // expected-error {{cannot export 'iterator' as it is not at namespace scope}}
32+
// expected-error@-1 {{forward declaration of struct cannot have a nested name specifier}}
3133
export void Y::foo(); // expected-error {{cannot export 'foo' as it is not at namespace scope}}
3234
export template <typename U> U Y::bar(); // expected-error {{cannot export 'bar' as it is not at namespace scope}}
3335

3436
export {
3537
template <typename T> struct X<T>::iterator; // expected-error {{cannot export 'iterator' as it is not at namespace scope}}
38+
// expected-error@-1 {{forward declaration of struct cannot have a nested name specifier}}
3639
struct Y::iterator; // expected-error {{cannot export 'iterator' as it is not at namespace scope}}
40+
// expected-error@-1 {{forward declaration of struct cannot have a nested name specifier}}
3741
}

clang/test/SemaCXX/enum-scoped.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ namespace test5 {
146146
namespace test6 {
147147
enum A : unsigned;
148148
struct A::a; // expected-error {{incomplete type 'test6::A' named in nested name specifier}}
149+
// expected-error@-1{{forward declaration of struct cannot have a nested name specifier}}
149150
enum A::b; // expected-error {{incomplete type 'test6::A' named in nested name specifier}}
151+
// expected-error@-1{{forward declaration of enum cannot have a nested name specifier}}
150152
int A::c; // expected-error {{incomplete type 'test6::A' named in nested name specifier}}
151153
void A::d(); // expected-error {{incomplete type 'test6::A' named in nested name specifier}}
152154
void test() {

clang/test/SemaCXX/nested-name-spec.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ A::C c1;
6565
struct A::C c2;
6666
struct S : public A::C {};
6767
struct A::undef; // expected-error {{no struct named 'undef' in namespace 'A'}}
68-
68+
// expected-error@-1 {{forward declaration of struct cannot have a nested name specifier}}
6969
namespace A2 {
7070
typedef int INT;
7171
struct RC;
@@ -280,9 +280,11 @@ template<typename T>
280280
struct A {
281281
protected:
282282
struct B;
283-
struct B::C; // expected-error {{requires a template parameter list}} \
284-
// expected-error {{no struct named 'C'}} \
285-
// expected-error{{non-friend class member 'C' cannot have a qualified name}}
283+
struct B::C;
284+
// expected-error@-1 {{requires a template parameter list}}
285+
// expected-error@-2 {{no struct named 'C'}}
286+
// expected-error@-3 {{non-friend class member 'C' cannot have a qualified name}}
287+
// expected-error@-4 {{forward declaration of struct cannot have a nested name specifier}}
286288
};
287289

288290
template<typename T>
@@ -292,6 +294,7 @@ struct A2 {
292294
};
293295
template <typename T>
294296
struct A2<T>::B::C; // expected-error {{no struct named 'C'}}
297+
// expected-error@-1 {{forward declaration of struct cannot have a nested name specifier}}
295298
}
296299

297300
namespace PR13033 {

clang/test/SemaTemplate/elaborated-type-specifier.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ namespace PR6915 {
2323
template<typename T>
2424
struct DeclOrDef {
2525
enum T::foo; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
26+
// expected-error@-1{{forward declaration of enum cannot have a nested name specifier}}
2627
enum T::bar { // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
2728
value
2829
};
@@ -31,6 +32,7 @@ struct DeclOrDef {
3132
namespace PR6649 {
3233
template <typename T> struct foo {
3334
class T::bar; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
35+
// expected-error@-1{{forward declaration of class cannot have a nested name specifier}}
3436
class T::bar { int x; }; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
3537
};
3638
}

clang/test/SemaTemplate/qualified-id.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,6 @@ namespace PR12291 {
5252
template <typename V>
5353
template <typename W>
5454
class Outer2<V>::Inner; // expected-error{{nested name specifier 'Outer2<V>::' for declaration does not refer into a class, class template or class template partial specialization}}
55+
// expected-error@-1{{forward declaration of class cannot have a nested name specifier}}
5556
};
5657
}

0 commit comments

Comments
 (0)