Author: Richard Smith Date: 2020-05-08T19:32:00-07:00 New Revision: c90e198107431f64b73686bdce31c293e3380ac7
URL: https://github.com/llvm/llvm-project/commit/c90e198107431f64b73686bdce31c293e3380ac7 DIFF: https://github.com/llvm/llvm-project/commit/c90e198107431f64b73686bdce31c293e3380ac7.diff LOG: 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. Added: Modified: clang/include/clang/Basic/DiagnosticParseKinds.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Parse/Parser.h clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseDeclCXX.cpp clang/lib/Parse/ParseTentative.cpp clang/lib/Sema/SemaDecl.cpp clang/test/CXX/drs/dr15xx.cpp clang/test/CXX/drs/dr19xx.cpp clang/test/CXX/drs/dr21xx.cpp clang/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp clang/test/Parser/c1x-generic-selection.c clang/test/Parser/cxx0x-ambig.cpp clang/test/Parser/cxx0x-decl.cpp clang/test/SemaCXX/enum-bitfield.cpp clang/test/SemaCXX/enum-scoped.cpp clang/test/SemaObjC/enum-fixed-type.m clang/www/cxx_dr_status.html Removed: ################################################################################ diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 552c9187e705..04014780615b 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -105,6 +105,14 @@ def ext_clang_c_enum_fixed_underlying_type : Extension< def warn_cxx98_compat_enum_fixed_underlying_type : Warning< "enumeration types with a fixed underlying type are incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore; +def ext_enum_base_in_type_specifier : ExtWarn< + "non-defining declaration of enumeration with a fixed underlying type is " + "only permitted as a standalone declaration" + "%select{|; missing list of enumerators?}0">, InGroup<DiagGroup<"enum-base">>; +def err_anonymous_enum_bitfield : Error< + "ISO C++ only allows ':' in member enumeration declaration to introduce " + "a fixed underlying type, not an anonymous bit-field">; + def warn_cxx98_compat_alignof : Warning< "alignof expressions are incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b3f6f66a8c56..5ca0982ce5c3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5484,6 +5484,8 @@ def err_bitfield_width_exceeds_type_width : Error< def err_anon_bitfield_width_exceeds_type_width : Error< "width of anonymous bit-field (%0 bits) exceeds %select{width|size}1 " "of its type (%2 bit%s2)">; +def err_anon_bitfield_init : Error< + "anonymous bit-field cannot have a default member initializer">; def err_incorrect_number_of_vector_initializers : Error< "number of elements must be either one or match the size of the vector">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 4a8acf5cd196..cbdf9fede665 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2181,6 +2181,68 @@ class Parser : public CodeCompletionHandler { llvm_unreachable("Missing DeclSpecContext case"); } + /// Whether a defining-type-specifier is permitted in a given context. + enum class AllowDefiningTypeSpec { + /// The grammar doesn't allow a defining-type-specifier here, and we must + /// not parse one (eg, because a '{' could mean something else). + No, + /// The grammar doesn't allow a defining-type-specifier here, but we permit + /// one for error recovery purposes. Sema will reject. + NoButErrorRecovery, + /// The grammar allows a defining-type-specifier here, even though it's + /// always invalid. Sema will reject. + YesButInvalid, + /// The grammar allows a defining-type-specifier here, and one can be valid. + Yes + }; + + /// Is this a context in which we are parsing defining-type-specifiers (and + /// so permit class and enum definitions in addition to non-defining class and + /// enum elaborated-type-specifiers)? + static AllowDefiningTypeSpec + isDefiningTypeSpecifierContext(DeclSpecContext DSC) { + switch (DSC) { + case DeclSpecContext::DSC_normal: + case DeclSpecContext::DSC_class: + case DeclSpecContext::DSC_top_level: + case DeclSpecContext::DSC_alias_declaration: + case DeclSpecContext::DSC_objc_method_result: + return AllowDefiningTypeSpec::Yes; + + case DeclSpecContext::DSC_condition: + case DeclSpecContext::DSC_template_param: + return AllowDefiningTypeSpec::YesButInvalid; + + case DeclSpecContext::DSC_template_type_arg: + case DeclSpecContext::DSC_type_specifier: + return AllowDefiningTypeSpec::NoButErrorRecovery; + + case DeclSpecContext::DSC_trailing: + return AllowDefiningTypeSpec::No; + } + llvm_unreachable("Missing DeclSpecContext case"); + } + + /// Is this a context in which an opaque-enum-declaration can appear? + static bool isOpaqueEnumDeclarationContext(DeclSpecContext DSC) { + switch (DSC) { + case DeclSpecContext::DSC_normal: + case DeclSpecContext::DSC_class: + case DeclSpecContext::DSC_top_level: + return true; + + case DeclSpecContext::DSC_alias_declaration: + case DeclSpecContext::DSC_objc_method_result: + case DeclSpecContext::DSC_condition: + case DeclSpecContext::DSC_template_param: + case DeclSpecContext::DSC_template_type_arg: + case DeclSpecContext::DSC_type_specifier: + case DeclSpecContext::DSC_trailing: + return false; + } + llvm_unreachable("Missing DeclSpecContext case"); + } + /// Is this a context in which we can perform class template argument /// deduction? static bool isClassTemplateDeductionContext(DeclSpecContext DSC) { @@ -2408,17 +2470,14 @@ class Parser : public CodeCompletionHandler { True, False, Ambiguous, Error }; - /// Based only on the given token kind, determine whether we know that - /// we're at the start of an expression or a type-specifier-seq (which may - /// be an expression, in C++). + /// Determine whether we could have an enum-base. /// - /// This routine does not attempt to resolve any of the trick cases, e.g., - /// those involving lookup of identifiers. + /// \p AllowSemi If \c true, then allow a ';' after the enum-base; otherwise + /// only consider this to be an enum-base if the next token is a '{'. /// - /// \returns \c TPR_true if this token starts an expression, \c TPR_false if - /// this token starts a type-specifier-seq, or \c TPR_ambiguous if it cannot - /// tell. - TPResult isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind); + /// \return \c false if this cannot possibly be an enum base; \c true + /// otherwise. + bool isEnumBase(bool AllowSemi); /// isCXXDeclarationSpecifier - Returns TPResult::True if it is a /// declaration specifier, TPResult::False if it is not, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index f7d8619ad667..af5493d91deb 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) { diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index f8c6379eee91..1a82475117ba 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1283,7 +1283,8 @@ bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) { case tok::annot_pragma_ms_pointers_to_members: return true; case tok::colon: - return CouldBeBitfield; // enum E { ... } : 2; + return CouldBeBitfield || // enum E { ... } : 2; + ColonIsSacred; // _Generic(..., enum E : 2); // Microsoft compatibility case tok::kw___cdecl: // struct foo {...} __cdecl x; case tok::kw___fastcall: // struct foo {...} __fastcall x; @@ -1680,7 +1681,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy(); Sema::TagUseKind TUK; - if (DSC == DeclSpecContext::DSC_trailing) + if (isDefiningTypeSpecifierContext(DSC) == AllowDefiningTypeSpec::No) TUK = Sema::TUK_Reference; else if (Tok.is(tok::l_brace) || (getLangOpts().CPlusPlus && Tok.is(tok::colon)) || diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 61a82664bf71..733d309d5ff2 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -428,6 +428,35 @@ struct Parser::ConditionDeclarationOrInitStatementState { } }; +bool Parser::isEnumBase(bool AllowSemi) { + assert(Tok.is(tok::colon) && "should be looking at the ':'"); + + RevertingTentativeParsingAction PA(*this); + ConsumeToken(); + + bool InvalidAsDeclSpec = false; + TPResult R = isCXXDeclarationSpecifier(/*BracedCastResult*/ TPResult::True, + &InvalidAsDeclSpec); + if (R == TPResult::Ambiguous) { + // We either have a decl-specifier followed by '(' or an undeclared + // identifier. + if (TryConsumeDeclarationSpecifier() == TPResult::Error) + return true; + + // If we get to the end of the enum-base, we hit either a '{' or a ';'. + // Don't bother checking the enumerator-list. + if (Tok.is(tok::colon) || (AllowSemi && Tok.is(tok::semi))) + return true; + + // A second decl-specifier unambiguously indicatges an enum-base. + // The grammar permits an arbitrary type-name here, but we need an + // integral type, so no declarator pieces could ever work. + R = isCXXDeclarationSpecifier(TPResult::True, &InvalidAsDeclSpec); + } + + return R != TPResult::False; +} + /// Disambiguates between a declaration in a condition, a /// simple-declaration in an init-statement, and an expression for /// a condition of a if/switch statement. @@ -1067,132 +1096,6 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, return TPResult::Ambiguous; } -Parser::TPResult -Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { - switch (Kind) { - // Obviously starts an expression. - case tok::numeric_constant: - case tok::char_constant: - case tok::wide_char_constant: - case tok::utf8_char_constant: - case tok::utf16_char_constant: - case tok::utf32_char_constant: - case tok::string_literal: - case tok::wide_string_literal: - case tok::utf8_string_literal: - case tok::utf16_string_literal: - case tok::utf32_string_literal: - case tok::l_square: - case tok::l_paren: - case tok::amp: - case tok::ampamp: - case tok::star: - case tok::plus: - case tok::plusplus: - case tok::minus: - case tok::minusminus: - case tok::tilde: - case tok::exclaim: - case tok::kw_sizeof: - case tok::kw___func__: - case tok::kw_const_cast: - case tok::kw_delete: - case tok::kw_dynamic_cast: - case tok::kw_false: - case tok::kw_new: - case tok::kw_operator: - case tok::kw_reinterpret_cast: - case tok::kw_static_cast: - case tok::kw_this: - case tok::kw_throw: - case tok::kw_true: - case tok::kw_typeid: - case tok::kw_alignof: - case tok::kw_noexcept: - case tok::kw_nullptr: - case tok::kw__Alignof: - case tok::kw___null: - case tok::kw___alignof: - case tok::kw___builtin_choose_expr: - case tok::kw___builtin_offsetof: - case tok::kw___builtin_va_arg: - case tok::kw___imag: - case tok::kw___real: - case tok::kw___FUNCTION__: - case tok::kw___FUNCDNAME__: - case tok::kw___FUNCSIG__: - case tok::kw_L__FUNCTION__: - case tok::kw_L__FUNCSIG__: - case tok::kw___PRETTY_FUNCTION__: - case tok::kw___uuidof: - case tok::kw___builtin_unique_stable_name: -#define TYPE_TRAIT(N,Spelling,K) \ - case tok::kw_##Spelling: -#include "clang/Basic/TokenKinds.def" - return TPResult::True; - - // Obviously starts a type-specifier-seq: - case tok::kw_char: - case tok::kw_const: - case tok::kw_double: - case tok::kw__Float16: - case tok::kw___float128: - case tok::kw_enum: - case tok::kw_half: - case tok::kw_float: - case tok::kw_int: - case tok::kw__ExtInt: - case tok::kw_long: - case tok::kw___int64: - case tok::kw___int128: - case tok::kw_restrict: - case tok::kw_short: - case tok::kw_signed: - case tok::kw_struct: - case tok::kw_union: - case tok::kw_unsigned: - case tok::kw_void: - case tok::kw_volatile: - case tok::kw__Bool: - case tok::kw__Complex: - case tok::kw_class: - case tok::kw_typename: - case tok::kw_wchar_t: - case tok::kw_char8_t: - case tok::kw_char16_t: - case tok::kw_char32_t: - case tok::kw__Decimal32: - case tok::kw__Decimal64: - case tok::kw__Decimal128: - case tok::kw___interface: - case tok::kw___thread: - case tok::kw_thread_local: - case tok::kw__Thread_local: - case tok::kw_typeof: - case tok::kw___underlying_type: - case tok::kw___cdecl: - case tok::kw___stdcall: - case tok::kw___fastcall: - case tok::kw___thiscall: - case tok::kw___regcall: - case tok::kw___vectorcall: - case tok::kw___unaligned: - case tok::kw___vector: - case tok::kw___pixel: - case tok::kw___bool: - case tok::kw__Atomic: -#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t: -#include "clang/Basic/OpenCLImageTypes.def" - case tok::kw___unknown_anytype: - return TPResult::False; - - default: - break; - } - - return TPResult::Ambiguous; -} - bool Parser::isTentativelyDeclared(IdentifierInfo *II) { return std::find(TentativelyDeclaredIdentifiers.begin(), TentativelyDeclaredIdentifiers.end(), II) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 5d3314b4f244..3a377adb04f1 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16525,6 +16525,14 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, BitWidth = nullptr; ZeroWidth = false; } + + // Only data members can have in-class initializers. + if (BitWidth && !II && InitStyle) { + Diag(Loc, diag::err_anon_bitfield_init); + InvalidDecl = true; + BitWidth = nullptr; + ZeroWidth = false; + } } // Check that 'mutable' is consistent with the type of the declaration. diff --git a/clang/test/CXX/drs/dr15xx.cpp b/clang/test/CXX/drs/dr15xx.cpp index 55d838e45a57..478a0d7d00dd 100644 --- a/clang/test/CXX/drs/dr15xx.cpp +++ b/clang/test/CXX/drs/dr15xx.cpp @@ -135,6 +135,18 @@ namespace dr1512 { // dr1512: 4 #endif } +namespace dr1514 { // dr1514: 11 +#if __cplusplus >= 201103L + struct S { + enum E : int {}; // expected-note {{previous}} + enum E : int {}; // expected-error {{redefinition}} + }; + S::E se; // OK, complete type, not zero-width bitfield. + + // The behavior in other contexts is superseded by DR1966. +#endif +} + namespace dr1518 { // dr1518: 4 #if __cplusplus >= 201103L struct Z0 { // expected-note 0+ {{candidate}} diff --git a/clang/test/CXX/drs/dr19xx.cpp b/clang/test/CXX/drs/dr19xx.cpp index 4e359681fa8d..38d3ca589eca 100644 --- a/clang/test/CXX/drs/dr19xx.cpp +++ b/clang/test/CXX/drs/dr19xx.cpp @@ -167,6 +167,23 @@ namespace dr1959 { // dr1959: 3.9 #endif } +namespace dr1966 { // dr1966: 11 +#if __cplusplus >= 201103L + struct A { + enum E : int {1}; // expected-error {{expected identifier}} (not bit-field) + }; + auto *p1 = new enum E : int; // expected-error {{only permitted as a standalone declaration}} + auto *p2 = new enum F : int {}; // expected-error {{cannot be defined in a type specifier}} + auto *p3 = true ? new enum G : int {}; // expected-error {{forward reference}} expected-error {{incomplete}} expected-note {{declaration}} + auto h() -> enum E : int {}; // expected-error {{only permitted as a standalone declaration}} + + enum X : enum Y : int {} {}; // expected-error {{cannot be defined in a type specifier}} + struct Q { + enum X : enum Y : int {} {}; // expected-error +{{}} + }; +#endif +} + namespace dr1968 { // dr1968: no #if __cplusplus >= 201103L // FIXME: According to DR1968, both of these should be considered diff --git a/clang/test/CXX/drs/dr21xx.cpp b/clang/test/CXX/drs/dr21xx.cpp index 7f76440de2e5..7e3ffa057ef7 100644 --- a/clang/test/CXX/drs/dr21xx.cpp +++ b/clang/test/CXX/drs/dr21xx.cpp @@ -42,6 +42,15 @@ namespace dr2140 { // dr2140: 9 #endif } +namespace dr2157 { // dr2157: 11 +#if __cplusplus >= 201103L + enum E : int; + struct X { + enum dr2157::E : int(); // expected-error {{only allows ':' in member enumeration declaration to introduce a fixed underlying type}} + }; +#endif +} + namespace dr2170 { // dr2170: 9 #if __cplusplus >= 201103L void f() { diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp index b5cd98828cc9..7ab504cbc9e5 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp @@ -68,11 +68,12 @@ namespace PR10127 { foo m = decltype(foo::bar)::baz; - enum E { - }; + enum E {}; + enum H {}; struct bar { - enum E : decltype(outer())::td_int(4); + enum E : decltype(outer())::td_int(4); // expected-error{{anonymous bit-field}} enum F : decltype(outer())::td_int; enum G : decltype; // expected-error{{expected '(' after 'decltype'}} + enum H : 4; // expected-error {{anonymous bit-field}} }; } diff --git a/clang/test/Parser/c1x-generic-selection.c b/clang/test/Parser/c1x-generic-selection.c index ee23059cc4de..f203a9a5d00a 100644 --- a/clang/test/Parser/c1x-generic-selection.c +++ b/clang/test/Parser/c1x-generic-selection.c @@ -8,3 +8,14 @@ void foo(void) { default: 0, // expected-note {{previous default generic association is here}} default: 0); // expected-error {{duplicate default generic association}} } + +enum E { e }; +int bar(int n) { + // PR45726 + return _Generic(0, enum E: n, default: 0); +} + +int baz(int n) { + // PR39979 + return _Generic(0, enum { e }: n, default: 0); +} diff --git a/clang/test/Parser/cxx0x-ambig.cpp b/clang/test/Parser/cxx0x-ambig.cpp index 71d32b8f51d9..60a5c32d319a 100644 --- a/clang/test/Parser/cxx0x-ambig.cpp +++ b/clang/test/Parser/cxx0x-ambig.cpp @@ -30,7 +30,8 @@ namespace final { struct U final _Alignas(4) {}; // expected-error 3{{}} expected-note {{}} } -// enum versus bitfield mess. +// enum versus bitfield. These are always required to be treated as an +// enum-base, but we disambiguate anyway for better error recovery. namespace bitfield { enum E {}; @@ -44,9 +45,9 @@ namespace bitfield { constexpr T a, b, c, d; struct S1 { - enum E : T ( a = 1, b = 2, c = 3, 4 ); // ok, declares a bitfield + enum E : T ( a = 1, b = 2, c = 3, 4 ); // expected-error {{ISO C++ only allows ':' in member enumeration declaration to introduce a fixed underlying type, not an anonymous bit-field}} }; - // This could be a bit-field. + // Enum definition, not a bit-field. struct S2 { enum E : T { a = 1, b = 2, c = 3, 4 }; // expected-error {{non-integral type}} expected-error {{expected identifier}} }; diff --git a/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp index 3c1c3602691b..de6dfae07941 100644 --- a/clang/test/Parser/cxx0x-decl.cpp +++ b/clang/test/Parser/cxx0x-decl.cpp @@ -17,6 +17,49 @@ auto g() -> enum E { return E(); } +namespace EnumBase { + enum E {}; + // PR19810: The ': E' here is not an enum-base, and the ':' is not a typo for '::'. + E e = true ? *new enum E : E {}; + // PR45726: This ':' is not an enum-base. + static_assert(_Generic(e, enum E : int{}, int: 1) == 0); // expected-error {{C11 extension}} + static_assert(_Generic(1, enum E : int{}, int: 1) == 1); // expected-error {{C11 extension}} +} + +namespace OpaqueEnumDecl { + enum E : int; // ok + + // PR44941 + enum E : int n; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration}} + typedef enum E : int T; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration}} + typedef enum E : int T; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration}} + namespace Inner { + typedef enum E : int T; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration}} + } + + // GCC incorrectly accepts this one + using T = enum E : int; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration}} + + // PR19810 comment#2 + int x[sizeof(enum E : int)]; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration}} + + namespace PR24297 { + enum struct E a; // expected-error {{must use 'enum' not 'enum class'}} FIXME: we used 'enum struct' + enum class F b; // FIXME: invalid, no prior declaration of 'enum F' and in any case we cannot use 'class' here + enum G : int c; // expected-error {{only permitted as a standalone declaration}} + enum struct H : int d; // expected-error {{only permitted as a standalone declaration}} + enum class I : int e; // expected-error {{only permitted as a standalone declaration}} + enum X x; // expected-error {{ISO C++ forbids forward reference}} expected-error {{incomplete}} expected-note {{forward declaration}} + + enum struct E *pa; // expected-error {{must use 'enum' not 'enum class'}} FIXME: we used 'enum struct' + enum class F *pb; // expected-error {{must use 'enum' not 'enum class'}} + enum G : int *pc; // expected-error {{only permitted as a standalone declaration}} expected-error {{'int *' is an invalid underlying type}} + enum struct H : int *pd; // expected-error {{only permitted as a standalone declaration}} expected-error {{'int *' is an invalid underlying type}} FIXME: expected-error {{must use 'enum' not 'enum class'}} + enum class I : int *pe; // expected-error {{only permitted as a standalone declaration}} expected-error {{'int *' is an invalid underlying type}} FIXME: expected-error {{must use 'enum' not 'enum class'}} + enum Y *py; // expected-error {{ISO C++ forbids forward reference}} + } +} + int decltype(f())::*ptr_mem_decltype; class ExtraSemiAfterMemFn { diff --git a/clang/test/SemaCXX/enum-bitfield.cpp b/clang/test/SemaCXX/enum-bitfield.cpp index 676ae44b37fe..e78ca5dfa2fc 100644 --- a/clang/test/SemaCXX/enum-bitfield.cpp +++ b/clang/test/SemaCXX/enum-bitfield.cpp @@ -6,15 +6,15 @@ struct Z {}; typedef int Integer; struct X { - enum E : 1; + enum E : 1; // expected-error{{anonymous bit-field}} enum E : Z; // expected-error{{invalid underlying type}} enum E2 : int; enum E3 : Integer; }; struct Y { - enum E : int(2); - enum E : Z(); // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'Z'}} + enum E : int(2); // expected-error{{anonymous bit-field}} + enum E : Z(); // expected-error{{anonymous bit-field}} expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'Z'}} }; namespace pr18587 { diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp index 1fcafb1dd0fd..34707b894488 100644 --- a/clang/test/SemaCXX/enum-scoped.cpp +++ b/clang/test/SemaCXX/enum-scoped.cpp @@ -102,6 +102,7 @@ enum : long { }; enum : long x; // expected-error{{unnamed enumeration must be a definition}} \ +// expected-warning{{only permitted as a standalone declaration}} \ // expected-warning{{declaration does not declare anything}} void PR9333() { diff --git a/clang/test/SemaObjC/enum-fixed-type.m b/clang/test/SemaObjC/enum-fixed-type.m index b4135a555a23..d991ac3e2d08 100644 --- a/clang/test/SemaObjC/enum-fixed-type.m +++ b/clang/test/SemaObjC/enum-fixed-type.m @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -pedantic -verify %s -// RUN: %clang_cc1 -fsyntax-only -verify -xc %s +// RUN: %clang_cc1 -fsyntax-only -verify=expected,c -xc %s #ifdef __OBJC__ #if !__has_feature(objc_fixed_enum) @@ -23,8 +23,8 @@ struct X { enum Color : 4; enum Color field1: 4; - enum Other : Integer field2; - enum Other : Integer field3 : 4; + enum Other : Integer field2; // c-warning {{only permitted as a standalone}} + enum Other : Integer field3 : 4; // c-warning {{only permitted as a standalone}} enum : Integer { Blah, Blarg } field4 : 4; }; diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 3f0102889c67..defdf677ae01 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -8899,7 +8899,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://wg21.link/cwg1514">1514</a></td> <td>C++14</td> <td>Ambiguity between enumeration definition and zero-length bit-field</td> - <td class="none" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 11</td> </tr> <tr id="1515"> <td><a href="https://wg21.link/cwg1515">1515</a></td> @@ -11611,7 +11611,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://wg21.link/cwg1966">1966</a></td> <td>CD4</td> <td>Colon following enumeration <I>elaborated-type-specifier</I></td> - <td class="none" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 11</td> </tr> <tr id="1967"> <td><a href="https://wg21.link/cwg1967">1967</a></td> @@ -12757,7 +12757,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://wg21.link/cwg2157">2157</a></td> <td>CD4</td> <td>Further disambiguation of enumeration <I>elaborated-type-specifier</I></td> - <td class="none" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 11</td> </tr> <tr class="open" id="2158"> <td><a href="https://wg21.link/cwg2158">2158</a></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits