Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang][Sema] Allow elaborated-type-specifiers that declare member class template explict specializations #78720

Merged
merged 1 commit into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ Improvements to Clang's time-trace

Bug Fixes in This Version
-------------------------
- Clang now accepts elaborated-type-specifiers that explicitly specialize
a member class template for an implicit instantiation of a class template.

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
3 changes: 1 addition & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -7148,8 +7148,7 @@ def warn_standalone_specifier : Warning<"'%0' ignored on this declaration">,
def ext_standalone_specifier : ExtWarn<"'%0' is not permitted on a declaration "
"of a type">, InGroup<MissingDeclarations>;
def err_standalone_class_nested_name_specifier : Error<
"forward declaration of %select{class|struct|interface|union|enum|enum class|enum struct}0 cannot "
"have a nested name specifier">;
"forward declaration of %0 cannot have a nested name specifier">;
def err_typecheck_sclass_func : Error<"illegal storage class on function">;
def err_static_block_func : Error<
"function declared in block scope cannot have 'static' storage class">;
Expand Down
46 changes: 23 additions & 23 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5207,25 +5207,6 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
return ActOnFriendTypeDecl(S, DS, TemplateParams);
}

const CXXScopeSpec &SS = DS.getTypeSpecScope();
bool IsExplicitSpecialization =
!TemplateParams.empty() && TemplateParams.back()->size() == 0;
if (Tag && SS.isNotEmpty() && !Tag->isCompleteDefinition() &&
!IsExplicitInstantiation && !IsExplicitSpecialization &&
!isa<ClassTemplatePartialSpecializationDecl>(Tag)) {
// Per C++ [dcl.type.elab]p1, a class declaration cannot have a
// nested-name-specifier unless it is an explicit instantiation
// or an explicit specialization.
//
// FIXME: We allow class template partial specializations here too, per the
// obvious intent of DR1819.
//
// Per C++ [dcl.enum]p1, an opaque-enum-declaration can't either.
Diag(SS.getBeginLoc(), diag::err_standalone_class_nested_name_specifier)
<< GetDiagnosticTypeSpecifierID(DS) << SS.getRange();
return nullptr;
}

// Track whether this decl-specifier declares anything.
bool DeclaresAnything = true;

Expand Down Expand Up @@ -17222,10 +17203,29 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
// for non-C++ cases.
if (TemplateParameterLists.size() > 0 ||
(SS.isNotEmpty() && TUK != TUK_Reference)) {
if (TemplateParameterList *TemplateParams =
MatchTemplateParametersToScopeSpecifier(
KWLoc, NameLoc, SS, nullptr, TemplateParameterLists,
TUK == TUK_Friend, isMemberSpecialization, Invalid)) {
TemplateParameterList *TemplateParams =
MatchTemplateParametersToScopeSpecifier(
KWLoc, NameLoc, SS, nullptr, TemplateParameterLists,
TUK == TUK_Friend, isMemberSpecialization, Invalid);

// C++23 [dcl.type.elab] p2:
// If an elaborated-type-specifier is the sole constituent of a
// declaration, the declaration is ill-formed unless it is an explicit
// specialization, an explicit instantiation or it has one of the
// following forms: [...]
// C++23 [dcl.enum] p1:
// If the enum-head-name of an opaque-enum-declaration contains a
// nested-name-specifier, the declaration shall be an explicit
// specialization.
//
// FIXME: Class template partial specializations can be forward declared
// per CWG2213, but the resolution failed to allow qualified forward
// declarations. This is almost certainly unintentional, so we allow them.
if (TUK == TUK_Declaration && SS.isNotEmpty() && !isMemberSpecialization)
Diag(SS.getBeginLoc(), diag::err_standalone_class_nested_name_specifier)
<< TypeWithKeyword::getTagTypeKindName(Kind) << SS.getRange();

if (TemplateParams) {
if (Kind == TagTypeKind::Enum) {
Diag(KWLoc, diag::err_enum_template);
return true;
Expand Down
1 change: 1 addition & 0 deletions clang/test/CXX/class.access/p4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ namespace test21 {
template <class T> class A<T>::Inner {};
class B {
template <class T> class A<T>::Inner; // expected-error{{non-friend class member 'Inner' cannot have a qualified name}}
// expected-error@-1{{forward declaration of class cannot have a nested name specifier}}
};

void test() {
Expand Down
30 changes: 30 additions & 0 deletions clang/test/CXX/dcl.dcl/dcl.enum/p1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %clang_cc1 -verify %s -std=c++11

template<typename T>
struct S0 {
enum E0 : int;

enum class E1;
};

struct S3 {
enum E2 : int;

enum class E3;
};

template<typename T>
enum S0<T>::E0 : int; // expected-error{{cannot have a nested name specifier}}

template<>
enum S0<int>::E0 : int;

template<typename T>
enum class S0<T>::E1; // expected-error{{cannot have a nested name specifier}}

template<>
enum class S0<int>::E1;

enum S3::E2 : int; // expected-error{{cannot have a nested name specifier}}

enum class S3::E3; // expected-error{{cannot have a nested name specifier}}
66 changes: 65 additions & 1 deletion clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,75 @@ template<> struct N::B<int>;
template struct N::B<float>;

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

template<typename T> struct D::A; // expected-error {{cannot have a nested name specifier}}
template<typename T> struct D::A<T*>; // FIXME: This is technically ill-formed, but that's not the intent.
template<> struct D::A<int>;
template struct D::A<float>;

namespace qualified_decl {
template<typename T>
struct S0 {
struct S1;

template<typename U>
struct S2;

enum E0 : int;

enum class E1;
};

struct S3 {
struct S4;

template<typename T>
struct S5;

enum E2 : int;

enum class E3;
};

template<typename T>
struct S0<T>::S1; // expected-error{{cannot have a nested name specifier}}

template<>
struct S0<int>::S1;

template<typename T>
template<typename U>
struct S0<T>::S2; // expected-error{{cannot have a nested name specifier}}

template<typename T>
template<typename U>
struct S0<T>::S2<U*>;

template<>
template<>
struct S0<int>::S2<bool>;

template<>
template<typename U>
struct S0<int>::S2;

struct S3::S4; // expected-error{{cannot have a nested name specifier}}

template<typename T>
struct S3::S5; // expected-error{{cannot have a nested name specifier}}

struct S3::S4 f0();
enum S0<long>::E0 f1();
enum S0<long>::E1 f2();
enum S3::E2 f3();
enum S3::E3 f4();

using A0 = struct S3::S4;
using A1 = enum S0<long>::E0;
using A2 = enum S0<long>::E1;
using A3 = enum S3::E2;
using A4 = enum S3::E3;
}
1 change: 0 additions & 1 deletion clang/test/CXX/drs/dr16xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ namespace dr1638 { // dr1638: 3.1

enum class A<unsigned>::E;
// since-cxx11-error@-1 {{template specialization requires 'template<>'}}
// since-cxx11-error@-2 {{forward declaration of enum class cannot have a nested name specifier}}
template enum class A<unsigned>::E;
// since-cxx11-error@-1 {{enumerations cannot be explicitly instantiated}}
enum class A<unsigned>::E *e;
Expand Down
4 changes: 4 additions & 0 deletions clang/test/CXX/module/module.interface/p2-2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct X {
};

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

Expand All @@ -28,10 +29,13 @@ export struct Y {
};

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

export {
template <typename T> struct X<T>::iterator; // expected-error {{cannot export 'iterator' as it is not at namespace scope}}
// expected-error@-1 {{forward declaration of struct cannot have a nested name specifier}}
struct Y::iterator; // expected-error {{cannot export 'iterator' as it is not at namespace scope}}
// expected-error@-1 {{forward declaration of struct cannot have a nested name specifier}}
}
2 changes: 2 additions & 0 deletions clang/test/SemaCXX/enum-scoped.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ namespace test5 {
namespace test6 {
enum A : unsigned;
struct A::a; // expected-error {{incomplete type 'test6::A' named in nested name specifier}}
// expected-error@-1{{forward declaration of struct cannot have a nested name specifier}}
enum A::b; // expected-error {{incomplete type 'test6::A' named in nested name specifier}}
// expected-error@-1{{forward declaration of enum cannot have a nested name specifier}}
int A::c; // expected-error {{incomplete type 'test6::A' named in nested name specifier}}
void A::d(); // expected-error {{incomplete type 'test6::A' named in nested name specifier}}
void test() {
Expand Down
11 changes: 7 additions & 4 deletions clang/test/SemaCXX/nested-name-spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ A::C c1;
struct A::C c2;
struct S : public A::C {};
struct A::undef; // expected-error {{no struct named 'undef' in namespace 'A'}}

// expected-error@-1 {{forward declaration of struct cannot have a nested name specifier}}
namespace A2 {
typedef int INT;
struct RC;
Expand Down Expand Up @@ -280,9 +280,11 @@ template<typename T>
struct A {
protected:
struct B;
struct B::C; // expected-error {{requires a template parameter list}} \
// expected-error {{no struct named 'C'}} \
// expected-error{{non-friend class member 'C' cannot have a qualified name}}
struct B::C;
// expected-error@-1 {{requires a template parameter list}}
// expected-error@-2 {{no struct named 'C'}}
// expected-error@-3 {{non-friend class member 'C' cannot have a qualified name}}
// expected-error@-4 {{forward declaration of struct cannot have a nested name specifier}}
};

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

namespace PR13033 {
Expand Down
2 changes: 2 additions & 0 deletions clang/test/SemaTemplate/elaborated-type-specifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace PR6915 {
template<typename T>
struct DeclOrDef {
enum T::foo; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
// expected-error@-1{{forward declaration of enum cannot have a nested name specifier}}
enum T::bar { // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
value
};
Expand All @@ -31,6 +32,7 @@ struct DeclOrDef {
namespace PR6649 {
template <typename T> struct foo {
class T::bar; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
// expected-error@-1{{forward declaration of class cannot have a nested name specifier}}
class T::bar { int x; }; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
};
}
Expand Down
1 change: 1 addition & 0 deletions clang/test/SemaTemplate/qualified-id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ namespace PR12291 {
template <typename V>
template <typename W>
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}}
// expected-error@-1{{forward declaration of class cannot have a nested name specifier}}
};
}
Loading