Skip to content

Commit c90e198

Browse files
committed
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.
1 parent 49b32d8 commit c90e198

File tree

18 files changed

+298
-224
lines changed

18 files changed

+298
-224
lines changed

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ def ext_clang_c_enum_fixed_underlying_type : Extension<
105105
def warn_cxx98_compat_enum_fixed_underlying_type : Warning<
106106
"enumeration types with a fixed underlying type are incompatible with C++98">,
107107
InGroup<CXX98Compat>, DefaultIgnore;
108+
def ext_enum_base_in_type_specifier : ExtWarn<
109+
"non-defining declaration of enumeration with a fixed underlying type is "
110+
"only permitted as a standalone declaration"
111+
"%select{|; missing list of enumerators?}0">, InGroup<DiagGroup<"enum-base">>;
112+
def err_anonymous_enum_bitfield : Error<
113+
"ISO C++ only allows ':' in member enumeration declaration to introduce "
114+
"a fixed underlying type, not an anonymous bit-field">;
115+
108116
def warn_cxx98_compat_alignof : Warning<
109117
"alignof expressions are incompatible with C++98">,
110118
InGroup<CXX98Compat>, DefaultIgnore;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5484,6 +5484,8 @@ def err_bitfield_width_exceeds_type_width : Error<
54845484
def err_anon_bitfield_width_exceeds_type_width : Error<
54855485
"width of anonymous bit-field (%0 bits) exceeds %select{width|size}1 "
54865486
"of its type (%2 bit%s2)">;
5487+
def err_anon_bitfield_init : Error<
5488+
"anonymous bit-field cannot have a default member initializer">;
54875489
def err_incorrect_number_of_vector_initializers : Error<
54885490
"number of elements must be either one or match the size of the vector">;
54895491

clang/include/clang/Parse/Parser.h

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,68 @@ class Parser : public CodeCompletionHandler {
21812181
llvm_unreachable("Missing DeclSpecContext case");
21822182
}
21832183

2184+
/// Whether a defining-type-specifier is permitted in a given context.
2185+
enum class AllowDefiningTypeSpec {
2186+
/// The grammar doesn't allow a defining-type-specifier here, and we must
2187+
/// not parse one (eg, because a '{' could mean something else).
2188+
No,
2189+
/// The grammar doesn't allow a defining-type-specifier here, but we permit
2190+
/// one for error recovery purposes. Sema will reject.
2191+
NoButErrorRecovery,
2192+
/// The grammar allows a defining-type-specifier here, even though it's
2193+
/// always invalid. Sema will reject.
2194+
YesButInvalid,
2195+
/// The grammar allows a defining-type-specifier here, and one can be valid.
2196+
Yes
2197+
};
2198+
2199+
/// Is this a context in which we are parsing defining-type-specifiers (and
2200+
/// so permit class and enum definitions in addition to non-defining class and
2201+
/// enum elaborated-type-specifiers)?
2202+
static AllowDefiningTypeSpec
2203+
isDefiningTypeSpecifierContext(DeclSpecContext DSC) {
2204+
switch (DSC) {
2205+
case DeclSpecContext::DSC_normal:
2206+
case DeclSpecContext::DSC_class:
2207+
case DeclSpecContext::DSC_top_level:
2208+
case DeclSpecContext::DSC_alias_declaration:
2209+
case DeclSpecContext::DSC_objc_method_result:
2210+
return AllowDefiningTypeSpec::Yes;
2211+
2212+
case DeclSpecContext::DSC_condition:
2213+
case DeclSpecContext::DSC_template_param:
2214+
return AllowDefiningTypeSpec::YesButInvalid;
2215+
2216+
case DeclSpecContext::DSC_template_type_arg:
2217+
case DeclSpecContext::DSC_type_specifier:
2218+
return AllowDefiningTypeSpec::NoButErrorRecovery;
2219+
2220+
case DeclSpecContext::DSC_trailing:
2221+
return AllowDefiningTypeSpec::No;
2222+
}
2223+
llvm_unreachable("Missing DeclSpecContext case");
2224+
}
2225+
2226+
/// Is this a context in which an opaque-enum-declaration can appear?
2227+
static bool isOpaqueEnumDeclarationContext(DeclSpecContext DSC) {
2228+
switch (DSC) {
2229+
case DeclSpecContext::DSC_normal:
2230+
case DeclSpecContext::DSC_class:
2231+
case DeclSpecContext::DSC_top_level:
2232+
return true;
2233+
2234+
case DeclSpecContext::DSC_alias_declaration:
2235+
case DeclSpecContext::DSC_objc_method_result:
2236+
case DeclSpecContext::DSC_condition:
2237+
case DeclSpecContext::DSC_template_param:
2238+
case DeclSpecContext::DSC_template_type_arg:
2239+
case DeclSpecContext::DSC_type_specifier:
2240+
case DeclSpecContext::DSC_trailing:
2241+
return false;
2242+
}
2243+
llvm_unreachable("Missing DeclSpecContext case");
2244+
}
2245+
21842246
/// Is this a context in which we can perform class template argument
21852247
/// deduction?
21862248
static bool isClassTemplateDeductionContext(DeclSpecContext DSC) {
@@ -2408,17 +2470,14 @@ class Parser : public CodeCompletionHandler {
24082470
True, False, Ambiguous, Error
24092471
};
24102472

2411-
/// Based only on the given token kind, determine whether we know that
2412-
/// we're at the start of an expression or a type-specifier-seq (which may
2413-
/// be an expression, in C++).
2473+
/// Determine whether we could have an enum-base.
24142474
///
2415-
/// This routine does not attempt to resolve any of the trick cases, e.g.,
2416-
/// those involving lookup of identifiers.
2475+
/// \p AllowSemi If \c true, then allow a ';' after the enum-base; otherwise
2476+
/// only consider this to be an enum-base if the next token is a '{'.
24172477
///
2418-
/// \returns \c TPR_true if this token starts an expression, \c TPR_false if
2419-
/// this token starts a type-specifier-seq, or \c TPR_ambiguous if it cannot
2420-
/// tell.
2421-
TPResult isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind);
2478+
/// \return \c false if this cannot possibly be an enum base; \c true
2479+
/// otherwise.
2480+
bool isEnumBase(bool AllowSemi);
24222481

24232482
/// isCXXDeclarationSpecifier - Returns TPResult::True if it is a
24242483
/// declaration specifier, TPResult::False if it is not,

clang/lib/Parse/ParseDecl.cpp

Lines changed: 70 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4443,14 +4443,20 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
44434443
TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
44444444
SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag);
44454445

4446-
// Enum definitions should not be parsed in a trailing-return-type.
4447-
bool AllowDeclaration = DSC != DeclSpecContext::DSC_trailing;
4446+
// Determine whether this declaration is permitted to have an enum-base.
4447+
AllowDefiningTypeSpec AllowEnumSpecifier =
4448+
isDefiningTypeSpecifierContext(DSC);
4449+
bool CanBeOpaqueEnumDeclaration =
4450+
DS.isEmpty() && isOpaqueEnumDeclarationContext(DSC);
4451+
bool CanHaveEnumBase = (getLangOpts().CPlusPlus11 || getLangOpts().ObjC ||
4452+
getLangOpts().MicrosoftExt) &&
4453+
(AllowEnumSpecifier == AllowDefiningTypeSpec::Yes ||
4454+
CanBeOpaqueEnumDeclaration);
44484455

44494456
CXXScopeSpec &SS = DS.getTypeSpecScope();
44504457
if (getLangOpts().CPlusPlus) {
4451-
// "enum foo : bar;" is not a potential typo for "enum foo::bar;"
4452-
// if a fixed underlying type is allowed.
4453-
ColonProtectionRAIIObject X(*this, AllowDeclaration);
4458+
// "enum foo : bar;" is not a potential typo for "enum foo::bar;".
4459+
ColonProtectionRAIIObject X(*this);
44544460

44554461
CXXScopeSpec Spec;
44564462
if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr,
@@ -4471,9 +4477,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
44714477
SS = Spec;
44724478
}
44734479

4474-
// Must have either 'enum name' or 'enum {...}'.
4480+
// Must have either 'enum name' or 'enum {...}' or (rarely) 'enum : T { ... }'.
44754481
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace) &&
4476-
!(AllowDeclaration && Tok.is(tok::colon))) {
4482+
Tok.isNot(tok::colon)) {
44774483
Diag(Tok, diag::err_expected_either) << tok::identifier << tok::l_brace;
44784484

44794485
// Skip the rest of this declarator, up until the comma or semicolon.
@@ -4503,78 +4509,61 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
45034509
diagsFromTag.done();
45044510

45054511
TypeResult BaseType;
4512+
SourceRange BaseRange;
45064513

4507-
// Parse the fixed underlying type.
45084514
bool CanBeBitfield = getCurScope()->getFlags() & Scope::ClassScope;
4509-
if (AllowDeclaration && Tok.is(tok::colon)) {
4510-
bool PossibleBitfield = false;
4511-
if (CanBeBitfield) {
4512-
// If we're in class scope, this can either be an enum declaration with
4513-
// an underlying type, or a declaration of a bitfield member. We try to
4514-
// use a simple disambiguation scheme first to catch the common cases
4515-
// (integer literal, sizeof); if it's still ambiguous, we then consider
4516-
// anything that's a simple-type-specifier followed by '(' as an
4517-
// expression. This suffices because function types are not valid
4518-
// underlying types anyway.
4519-
EnterExpressionEvaluationContext Unevaluated(
4520-
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
4521-
TPResult TPR = isExpressionOrTypeSpecifierSimple(NextToken().getKind());
4522-
// If the next token starts an expression, we know we're parsing a
4523-
// bit-field. This is the common case.
4524-
if (TPR == TPResult::True)
4525-
PossibleBitfield = true;
4526-
// If the next token starts a type-specifier-seq, it may be either a
4527-
// a fixed underlying type or the start of a function-style cast in C++;
4528-
// lookahead one more token to see if it's obvious that we have a
4529-
// fixed underlying type.
4530-
else if (TPR == TPResult::False &&
4531-
GetLookAheadToken(2).getKind() == tok::semi) {
4532-
// Consume the ':'.
4533-
ConsumeToken();
4534-
} else {
4535-
// We have the start of a type-specifier-seq, so we have to perform
4536-
// tentative parsing to determine whether we have an expression or a
4537-
// type.
4538-
TentativeParsingAction TPA(*this);
4539-
4540-
// Consume the ':'.
4541-
ConsumeToken();
45424515

4543-
// If we see a type specifier followed by an open-brace, we have an
4544-
// ambiguity between an underlying type and a C++11 braced
4545-
// function-style cast. Resolve this by always treating it as an
4546-
// underlying type.
4547-
// FIXME: The standard is not entirely clear on how to disambiguate in
4548-
// this case.
4549-
if ((getLangOpts().CPlusPlus &&
4550-
isCXXDeclarationSpecifier(TPResult::True) != TPResult::True) ||
4551-
(!getLangOpts().CPlusPlus && !isDeclarationSpecifier(true))) {
4552-
// We'll parse this as a bitfield later.
4553-
PossibleBitfield = true;
4554-
TPA.Revert();
4555-
} else {
4556-
// We have a type-specifier-seq.
4557-
TPA.Commit();
4558-
}
4559-
}
4560-
} else {
4561-
// Consume the ':'.
4562-
ConsumeToken();
4563-
}
4564-
4565-
if (!PossibleBitfield) {
4566-
SourceRange Range;
4567-
BaseType = ParseTypeName(&Range);
4516+
// Parse the fixed underlying type.
4517+
if (Tok.is(tok::colon)) {
4518+
// This might be an enum-base or part of some unrelated enclosing context.
4519+
//
4520+
// 'enum E : base' is permitted in two circumstances:
4521+
//
4522+
// 1) As a defining-type-specifier, when followed by '{'.
4523+
// 2) As the sole constituent of a complete declaration -- when DS is empty
4524+
// and the next token is ';'.
4525+
//
4526+
// The restriction to defining-type-specifiers is important to allow parsing
4527+
// a ? new enum E : int{}
4528+
// _Generic(a, enum E : int{})
4529+
// properly.
4530+
//
4531+
// One additional consideration applies:
4532+
//
4533+
// C++ [dcl.enum]p1:
4534+
// A ':' following "enum nested-name-specifier[opt] identifier" within
4535+
// the decl-specifier-seq of a member-declaration is parsed as part of
4536+
// an enum-base.
4537+
//
4538+
// Other lamguage modes supporting enumerations with fixed underlying types
4539+
// do not have clear rules on this, so we disambiguate to determine whether
4540+
// the tokens form a bit-field width or an enum-base.
4541+
4542+
if (CanBeBitfield && !isEnumBase(CanBeOpaqueEnumDeclaration)) {
4543+
// Outside C++11, do not interpret the tokens as an enum-base if they do
4544+
// not make sense as one. In C++11, it's an error if this happens.
4545+
if (getLangOpts().CPlusPlus11 && !getLangOpts().ObjC &&
4546+
!getLangOpts().MicrosoftExt)
4547+
Diag(Tok.getLocation(), diag::err_anonymous_enum_bitfield);
4548+
} else if (CanHaveEnumBase || !ColonIsSacred) {
4549+
SourceLocation ColonLoc = ConsumeToken();
4550+
4551+
BaseType = ParseTypeName(&BaseRange);
4552+
BaseRange.setBegin(ColonLoc);
45684553

45694554
if (!getLangOpts().ObjC) {
45704555
if (getLangOpts().CPlusPlus11)
4571-
Diag(StartLoc, diag::warn_cxx98_compat_enum_fixed_underlying_type);
4556+
Diag(ColonLoc, diag::warn_cxx98_compat_enum_fixed_underlying_type)
4557+
<< BaseRange;
45724558
else if (getLangOpts().CPlusPlus)
4573-
Diag(StartLoc, diag::ext_cxx11_enum_fixed_underlying_type);
4559+
Diag(ColonLoc, diag::ext_cxx11_enum_fixed_underlying_type)
4560+
<< BaseRange;
45744561
else if (getLangOpts().MicrosoftExt)
4575-
Diag(StartLoc, diag::ext_ms_c_enum_fixed_underlying_type);
4562+
Diag(ColonLoc, diag::ext_ms_c_enum_fixed_underlying_type)
4563+
<< BaseRange;
45764564
else
4577-
Diag(StartLoc, diag::ext_clang_c_enum_fixed_underlying_type);
4565+
Diag(ColonLoc, diag::ext_clang_c_enum_fixed_underlying_type)
4566+
<< BaseRange;
45784567
}
45794568
}
45804569
}
@@ -4590,9 +4579,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
45904579
// enum foo {..}; void bar() { enum foo x; } <- use of old foo.
45914580
//
45924581
Sema::TagUseKind TUK;
4593-
if (!AllowDeclaration) {
4582+
if (AllowEnumSpecifier == AllowDefiningTypeSpec::No)
45944583
TUK = Sema::TUK_Reference;
4595-
} else if (Tok.is(tok::l_brace)) {
4584+
else if (Tok.is(tok::l_brace)) {
45964585
if (DS.isFriendSpecified()) {
45974586
Diag(Tok.getLocation(), diag::err_friend_decl_defines_type)
45984587
<< SourceRange(DS.getFriendSpecLoc());
@@ -4623,6 +4612,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
46234612
diagsFromTag.redelay();
46244613
}
46254614

4615+
// A C++11 enum-base can only appear as part of an enum definition or an
4616+
// opaque-enum-declaration. MSVC and ObjC permit an enum-base anywhere.
4617+
if (BaseType.isUsable() && TUK != Sema::TUK_Definition &&
4618+
!getLangOpts().ObjC && !getLangOpts().MicrosoftExt &&
4619+
!(CanBeOpaqueEnumDeclaration && Tok.is(tok::semi))) {
4620+
Diag(BaseRange.getBegin(), diag::ext_enum_base_in_type_specifier)
4621+
<< (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes) << BaseRange;
4622+
}
4623+
46264624
MultiTemplateParamsArg TParams;
46274625
if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
46284626
TUK != Sema::TUK_Reference) {

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,8 @@ bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) {
12831283
case tok::annot_pragma_ms_pointers_to_members:
12841284
return true;
12851285
case tok::colon:
1286-
return CouldBeBitfield; // enum E { ... } : 2;
1286+
return CouldBeBitfield || // enum E { ... } : 2;
1287+
ColonIsSacred; // _Generic(..., enum E : 2);
12871288
// Microsoft compatibility
12881289
case tok::kw___cdecl: // struct foo {...} __cdecl x;
12891290
case tok::kw___fastcall: // struct foo {...} __fastcall x;
@@ -1680,7 +1681,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
16801681

16811682
const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
16821683
Sema::TagUseKind TUK;
1683-
if (DSC == DeclSpecContext::DSC_trailing)
1684+
if (isDefiningTypeSpecifierContext(DSC) == AllowDefiningTypeSpec::No)
16841685
TUK = Sema::TUK_Reference;
16851686
else if (Tok.is(tok::l_brace) ||
16861687
(getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||

0 commit comments

Comments
 (0)