diff options
author | Richard Smith <richard@metafoo.co.uk> | 2020-05-08 19:24:18 -0700 |
---|---|---|
committer | Richard Smith <richard@metafoo.co.uk> | 2020-05-08 19:32:00 -0700 |
commit | c90e198107431f64b73686bdce31c293e3380ac7 (patch) | |
tree | 927aae2095e064266ba799a942dd3d928c4b231e /clang/lib/Parse/ParseDecl.cpp | |
parent | 49b32d80416288b6eb8e26f76c40a8e32c20a361 (diff) | |
download | llvm-c90e198107431f64b73686bdce31c293e3380ac7.zip llvm-c90e198107431f64b73686bdce31c293e3380ac7.tar.gz llvm-c90e198107431f64b73686bdce31c293e3380ac7.tar.bz2 |
Fix parsing of enum-base to follow C++11 rules.
Previously we implemented non-standard disambiguation rules to
distinguish an enum-base from a bit-field but otherwise treated a :
after an elaborated-enum-specifier as introducing an enum-base. That
misparses various examples (anywhere an elaborated-type-specifier can
appear followed by a colon, such as within a ternary operator or
_Generic).
We now implement the C++11 rules, with the old cases accepted as
extensions where that seemed reasonable. These amount to:
* an enum-base must always be accompanied by an enum definition (except
in a standalone declaration of the form 'enum E : T;')
* in a member-declaration, 'enum E :' always introduces an enum-base,
never a bit-field
* in a type-specifier (or similar context), 'enum E :' is not
permitted; the colon means whatever else it would mean in that
context.
Fixed underlying types for enums are also permitted in Objective-C and
under MS extensions, plus as a language extension in all other modes.
The behavior in ObjC and MS extensions modes is unchanged (but the
bit-field disambiguation is a bit better); remaining language modes
follow the C++11 rules.
Fixes PR45726, PR39979, PR19810, PR44941, and most of PR24297, plus C++
core issues 1514 and 1966.
Diffstat (limited to 'clang/lib/Parse/ParseDecl.cpp')
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 142 |
1 files changed, 70 insertions, 72 deletions
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index f7d8619..af5493d 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4443,14 +4443,20 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization); SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag); - // Enum definitions should not be parsed in a trailing-return-type. - bool AllowDeclaration = DSC != DeclSpecContext::DSC_trailing; + // Determine whether this declaration is permitted to have an enum-base. + AllowDefiningTypeSpec AllowEnumSpecifier = + isDefiningTypeSpecifierContext(DSC); + bool CanBeOpaqueEnumDeclaration = + DS.isEmpty() && isOpaqueEnumDeclarationContext(DSC); + bool CanHaveEnumBase = (getLangOpts().CPlusPlus11 || getLangOpts().ObjC || + getLangOpts().MicrosoftExt) && + (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes || + CanBeOpaqueEnumDeclaration); CXXScopeSpec &SS = DS.getTypeSpecScope(); if (getLangOpts().CPlusPlus) { - // "enum foo : bar;" is not a potential typo for "enum foo::bar;" - // if a fixed underlying type is allowed. - ColonProtectionRAIIObject X(*this, AllowDeclaration); + // "enum foo : bar;" is not a potential typo for "enum foo::bar;". + ColonProtectionRAIIObject X(*this); CXXScopeSpec Spec; if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr, @@ -4471,9 +4477,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, SS = Spec; } - // Must have either 'enum name' or 'enum {...}'. + // Must have either 'enum name' or 'enum {...}' or (rarely) 'enum : T { ... }'. if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace) && - !(AllowDeclaration && Tok.is(tok::colon))) { + Tok.isNot(tok::colon)) { Diag(Tok, diag::err_expected_either) << tok::identifier << tok::l_brace; // Skip the rest of this declarator, up until the comma or semicolon. @@ -4503,78 +4509,61 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, diagsFromTag.done(); TypeResult BaseType; + SourceRange BaseRange; - // Parse the fixed underlying type. bool CanBeBitfield = getCurScope()->getFlags() & Scope::ClassScope; - if (AllowDeclaration && Tok.is(tok::colon)) { - bool PossibleBitfield = false; - if (CanBeBitfield) { - // If we're in class scope, this can either be an enum declaration with - // an underlying type, or a declaration of a bitfield member. We try to - // use a simple disambiguation scheme first to catch the common cases - // (integer literal, sizeof); if it's still ambiguous, we then consider - // anything that's a simple-type-specifier followed by '(' as an - // expression. This suffices because function types are not valid - // underlying types anyway. - EnterExpressionEvaluationContext Unevaluated( - Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); - TPResult TPR = isExpressionOrTypeSpecifierSimple(NextToken().getKind()); - // If the next token starts an expression, we know we're parsing a - // bit-field. This is the common case. - if (TPR == TPResult::True) - PossibleBitfield = true; - // If the next token starts a type-specifier-seq, it may be either a - // a fixed underlying type or the start of a function-style cast in C++; - // lookahead one more token to see if it's obvious that we have a - // fixed underlying type. - else if (TPR == TPResult::False && - GetLookAheadToken(2).getKind() == tok::semi) { - // Consume the ':'. - ConsumeToken(); - } else { - // We have the start of a type-specifier-seq, so we have to perform - // tentative parsing to determine whether we have an expression or a - // type. - TentativeParsingAction TPA(*this); - - // Consume the ':'. - ConsumeToken(); - // If we see a type specifier followed by an open-brace, we have an - // ambiguity between an underlying type and a C++11 braced - // function-style cast. Resolve this by always treating it as an - // underlying type. - // FIXME: The standard is not entirely clear on how to disambiguate in - // this case. - if ((getLangOpts().CPlusPlus && - isCXXDeclarationSpecifier(TPResult::True) != TPResult::True) || - (!getLangOpts().CPlusPlus && !isDeclarationSpecifier(true))) { - // We'll parse this as a bitfield later. - PossibleBitfield = true; - TPA.Revert(); - } else { - // We have a type-specifier-seq. - TPA.Commit(); - } - } - } else { - // Consume the ':'. - ConsumeToken(); - } - - if (!PossibleBitfield) { - SourceRange Range; - BaseType = ParseTypeName(&Range); + // Parse the fixed underlying type. + if (Tok.is(tok::colon)) { + // This might be an enum-base or part of some unrelated enclosing context. + // + // 'enum E : base' is permitted in two circumstances: + // + // 1) As a defining-type-specifier, when followed by '{'. + // 2) As the sole constituent of a complete declaration -- when DS is empty + // and the next token is ';'. + // + // The restriction to defining-type-specifiers is important to allow parsing + // a ? new enum E : int{} + // _Generic(a, enum E : int{}) + // properly. + // + // One additional consideration applies: + // + // C++ [dcl.enum]p1: + // A ':' following "enum nested-name-specifier[opt] identifier" within + // the decl-specifier-seq of a member-declaration is parsed as part of + // an enum-base. + // + // Other lamguage modes supporting enumerations with fixed underlying types + // do not have clear rules on this, so we disambiguate to determine whether + // the tokens form a bit-field width or an enum-base. + + if (CanBeBitfield && !isEnumBase(CanBeOpaqueEnumDeclaration)) { + // Outside C++11, do not interpret the tokens as an enum-base if they do + // not make sense as one. In C++11, it's an error if this happens. + if (getLangOpts().CPlusPlus11 && !getLangOpts().ObjC && + !getLangOpts().MicrosoftExt) + Diag(Tok.getLocation(), diag::err_anonymous_enum_bitfield); + } else if (CanHaveEnumBase || !ColonIsSacred) { + SourceLocation ColonLoc = ConsumeToken(); + + BaseType = ParseTypeName(&BaseRange); + BaseRange.setBegin(ColonLoc); if (!getLangOpts().ObjC) { if (getLangOpts().CPlusPlus11) - Diag(StartLoc, diag::warn_cxx98_compat_enum_fixed_underlying_type); + Diag(ColonLoc, diag::warn_cxx98_compat_enum_fixed_underlying_type) + << BaseRange; else if (getLangOpts().CPlusPlus) - Diag(StartLoc, diag::ext_cxx11_enum_fixed_underlying_type); + Diag(ColonLoc, diag::ext_cxx11_enum_fixed_underlying_type) + << BaseRange; else if (getLangOpts().MicrosoftExt) - Diag(StartLoc, diag::ext_ms_c_enum_fixed_underlying_type); + Diag(ColonLoc, diag::ext_ms_c_enum_fixed_underlying_type) + << BaseRange; else - Diag(StartLoc, diag::ext_clang_c_enum_fixed_underlying_type); + Diag(ColonLoc, diag::ext_clang_c_enum_fixed_underlying_type) + << BaseRange; } } } @@ -4590,9 +4579,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // enum foo {..}; void bar() { enum foo x; } <- use of old foo. // Sema::TagUseKind TUK; - if (!AllowDeclaration) { + if (AllowEnumSpecifier == AllowDefiningTypeSpec::No) TUK = Sema::TUK_Reference; - } else if (Tok.is(tok::l_brace)) { + else if (Tok.is(tok::l_brace)) { if (DS.isFriendSpecified()) { Diag(Tok.getLocation(), diag::err_friend_decl_defines_type) << SourceRange(DS.getFriendSpecLoc()); @@ -4623,6 +4612,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, diagsFromTag.redelay(); } + // A C++11 enum-base can only appear as part of an enum definition or an + // opaque-enum-declaration. MSVC and ObjC permit an enum-base anywhere. + if (BaseType.isUsable() && TUK != Sema::TUK_Definition && + !getLangOpts().ObjC && !getLangOpts().MicrosoftExt && + !(CanBeOpaqueEnumDeclaration && Tok.is(tok::semi))) { + Diag(BaseRange.getBegin(), diag::ext_enum_base_in_type_specifier) + << (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes) << BaseRange; + } + MultiTemplateParamsArg TParams; if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate && TUK != Sema::TUK_Reference) { |