From: Richard Smith Date: Tue, 10 Jan 2012 01:33:14 +0000 (+0000) Subject: Update C++11 scoped enumeration support to match the final proposal: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bdad7a2e21686296b78dac6190b78d11c996f6d7;p=clang Update C++11 scoped enumeration support to match the final proposal: - reject definitions of enums within friend declarations - require 'enum', not 'enum class', for non-declaring references to scoped enumerations git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147824 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 919d105a51..e4a9fdc3a4 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -535,7 +535,7 @@ def err_ctor_init_missing_comma : Error< "missing ',' between base or member initializers">; // C++ declarations -def err_friend_decl_defines_class : Error< +def err_friend_decl_defines_type : Error< "cannot define a type in a friend declaration">; def err_missing_whitespace_digraph : Error< "found '<::' after a " diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 20a4062a74..d4a07ee896 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1228,6 +1228,9 @@ def err_enum_redeclare_fixed_mismatch : Error< "enumeration previously declared with %select{non|}0fixed underlying type">; def err_enum_redeclare_scoped_mismatch : Error< "enumeration previously declared as %select{un|}0scoped">; +def err_enum_class_reference : Error< + "reference to %select{|scoped }0enumeration must use 'enum' " + "not 'enum class'">; def err_only_enums_have_underlying_types : Error< "only enumeration types have underlying types">; def err_incomplete_type_no_underlying_type : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index b686d2dd43..68016e497f 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1171,7 +1171,8 @@ public: AttributeList *Attr, AccessSpecifier AS, SourceLocation ModulePrivateLoc, MultiTemplateParamsArg TemplateParameterLists, - bool &OwnedDecl, bool &IsDependent, bool ScopedEnum, + bool &OwnedDecl, bool &IsDependent, + SourceLocation ScopedEnumKWLoc, bool ScopedEnumUsesClassTag, TypeResult UnderlyingType); Decl *ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 5c4e898bf6..be5d5ae54c 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2865,15 +2865,14 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, return cutOffParsing(); } - bool IsScopedEnum = false; + SourceLocation ScopedEnumKWLoc; bool IsScopedUsingClassTag = false; if (getLang().CPlusPlus0x && (Tok.is(tok::kw_class) || Tok.is(tok::kw_struct))) { Diag(Tok, diag::warn_cxx98_compat_scoped_enum); - IsScopedEnum = true; IsScopedUsingClassTag = Tok.is(tok::kw_class); - ConsumeToken(); + ScopedEnumKWLoc = ConsumeToken(); } // If attributes exist after tag, parse them. @@ -2922,11 +2921,11 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, NameLoc = ConsumeToken(); } - if (!Name && IsScopedEnum) { + if (!Name && ScopedEnumKWLoc.isValid()) { // C++0x 7.2p2: The optional identifier shall not be omitted in the // declaration of a scoped enumeration. Diag(Tok, diag::err_scoped_enum_missing_identifier); - IsScopedEnum = false; + ScopedEnumKWLoc = SourceLocation(); IsScopedUsingClassTag = false; } @@ -2993,16 +2992,20 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, } } - // There are three options here. If we have 'enum foo;', then this is a - // forward declaration. If we have 'enum foo {...' then this is a - // definition. Otherwise we have something like 'enum foo xyz', a reference. + // There are four options here. If we have 'friend enum foo;' then this is a + // friend declaration, and cannot have an accompanying definition. If we have + // 'enum foo;', then this is a forward declaration. If we have + // 'enum foo {...' then this is a definition. Otherwise we have something + // like 'enum foo xyz', a reference. // // This is needed to handle stuff like this right (C99 6.7.2.3p11): // enum foo {..}; void bar() { enum foo; } <- new foo in bar. // enum foo {..}; void bar() { enum foo x; } <- use of old foo. // Sema::TagUseKind TUK; - if (Tok.is(tok::l_brace)) + if (DS.isFriendSpecified()) + TUK = Sema::TUK_Friend; + else if (Tok.is(tok::l_brace)) TUK = Sema::TUK_Definition; else if (Tok.is(tok::semi)) TUK = Sema::TUK_Declaration; @@ -3036,7 +3039,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, StartLoc, SS, Name, NameLoc, attrs.getList(), AS, DS.getModulePrivateSpecLoc(), MultiTemplateParamsArg(Actions), - Owned, IsDependent, IsScopedEnum, + Owned, IsDependent, ScopedEnumKWLoc, IsScopedUsingClassTag, BaseType); if (IsDependent) { @@ -3075,9 +3078,13 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, DS.SetTypeSpecError(); return; } - - if (Tok.is(tok::l_brace)) + + if (Tok.is(tok::l_brace)) { + if (TUK == Sema::TUK_Friend) + Diag(Tok, diag::err_friend_decl_defines_type) + << SourceRange(DS.getFriendSpecLoc()); ParseEnumBody(StartLoc, TagDecl); + } if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc, NameLoc.isValid() ? NameLoc : StartLoc, diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 05a346353b..285dbbbe07 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1115,7 +1115,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, if (DS.isFriendSpecified()) { // C++ [class.friend]p2: // A class shall not be defined in a friend declaration. - Diag(Tok.getLocation(), diag::err_friend_decl_defines_class) + Diag(Tok.getLocation(), diag::err_friend_decl_defines_type) << SourceRange(DS.getFriendSpecLoc()); // Skip everything up to the semicolon, so that this looks like a proper @@ -1277,8 +1277,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, TagOrTempResult = Actions.ActOnTag(getCurScope(), TagType, TUK, StartLoc, SS, Name, NameLoc, attrs.getList(), AS, DS.getModulePrivateSpecLoc(), - TParams, Owned, IsDependent, false, - false, clang::TypeResult()); + TParams, Owned, IsDependent, + SourceLocation(), false, + clang::TypeResult()); // If ActOnTag said the type was dependent, try again with the // less common call. diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 4a804bfcff..a2bf48e47a 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -7580,7 +7580,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation ModulePrivateLoc, MultiTemplateParamsArg TemplateParameterLists, bool &OwnedDecl, bool &IsDependent, - bool ScopedEnum, bool ScopedEnumUsesClassTag, + SourceLocation ScopedEnumKWLoc, + bool ScopedEnumUsesClassTag, TypeResult UnderlyingType) { // If this is not a definition, it must have a name. assert((Name != 0 || TUK == TUK_Definition) && @@ -7589,6 +7590,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, OwnedDecl = false; TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec); + bool ScopedEnum = ScopedEnumKWLoc.isValid(); // FIXME: Check explicit specializations more carefully. bool isExplicitSpecialization = false; @@ -7919,6 +7921,16 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, if (Kind == TTK_Enum && PrevTagDecl->getTagKind() == TTK_Enum) { const EnumDecl *PrevEnum = cast(PrevTagDecl); + // If this is an elaborated-type-specifier for a scoped enumeration, + // the 'class' keyword is not necessary and not permitted. + if (TUK == TUK_Reference || TUK == TUK_Friend) { + if (ScopedEnum) + Diag(ScopedEnumKWLoc, diag::err_enum_class_reference) + << PrevEnum->isScoped() + << FixItHint::CreateRemoval(ScopedEnumKWLoc); + return PrevTagDecl; + } + // All conflicts with previous declarations are recovered by // returning the previous declaration. if (ScopedEnum != PrevEnum->isScoped()) { diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 44c84f3679..0ca75df591 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -9851,7 +9851,7 @@ Decl *Sema::ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc, Attr, AS_public, /*ModulePrivateLoc=*/SourceLocation(), MultiTemplateParamsArg(), Owned, IsDependent, - /*ScopedEnum=*/false, + /*ScopedEnumKWLoc=*/SourceLocation(), /*ScopedEnumUsesClassTag=*/false, /*UnderlyingType=*/TypeResult()); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index b6babb6f75..bcdf32b5bc 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -6070,7 +6070,7 @@ Sema::ActOnExplicitInstantiation(Scope *S, KWLoc, SS, Name, NameLoc, Attr, AS_none, /*ModulePrivateLoc=*/SourceLocation(), MultiTemplateParamsArg(*this, 0, 0), - Owned, IsDependent, false, false, + Owned, IsDependent, SourceLocation(), false, TypeResult()); assert(!IsDependent && "explicit instantiation of dependent name not yet handled"); diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp index b04e869a48..8d58498802 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -class A {}; // expected-note 3 {{previous use is here}} +class A {}; // expected-note 4 {{previous use is here}} +enum E {}; void a1(struct A); void a2(class A); @@ -12,8 +13,8 @@ class A1 { friend class A; friend union A; // expected-error {{use of 'A' with tag type that does not match previous declaration}} - friend enum A; // expected-error {{ISO C++ forbids forward references to 'enum' types}} \ - // expected-warning {{cannot be a friend}} + friend enum A; // expected-error {{use of 'A' with tag type that does not match previous declaration}} + friend enum E; // expected-warning {{cannot be a friend}} }; template struct B { // expected-note {{previous use is here}} diff --git a/test/FixIt/fixit-cxx0x.cpp b/test/FixIt/fixit-cxx0x.cpp index 9895c1f53c..28a96198bd 100644 --- a/test/FixIt/fixit-cxx0x.cpp +++ b/test/FixIt/fixit-cxx0x.cpp @@ -37,3 +37,12 @@ namespace SemiCommaTypo { f3 override, // expected-error {{expected ';' at end of declaration}} }; } + +namespace ScopedEnum { + enum class E { a }; + + enum class E b = E::a; // expected-error {{must use 'enum' not 'enum class'}} + struct S { + friend enum class E; // expected-error {{must use 'enum' not 'enum class'}} + }; +} diff --git a/test/SemaCXX/enum-scoped.cpp b/test/SemaCXX/enum-scoped.cpp index d9726ac4fc..aed8f08ebb 100644 --- a/test/SemaCXX/enum-scoped.cpp +++ b/test/SemaCXX/enum-scoped.cpp @@ -147,3 +147,30 @@ namespace PR11484 { const int val = 104; enum class test1 { owner_dead = val, }; } + +namespace N2764 { + enum class E { a, b }; + enum E x1 = E::a; // ok + enum class E x2 = E::a; // expected-error {{reference to scoped enumeration must use 'enum' not 'enum class'}} + + enum F { a, b }; + enum F y1 = a; // ok + enum class F y2 = a; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}} + + struct S { + friend enum class E; // expected-error {{reference to scoped enumeration must use 'enum' not 'enum class'}} + friend enum class F; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}} + + friend enum G {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}} + friend enum class H {}; // expected-error {{cannot define a type in a friend declaration}} + + enum A : int; + A a; + } s; + + enum S::A : int {}; + + enum class B; +} + +enum class N2764::B {};