From a61b3e7443056f8d05b24ca4cbea90fe66235d6b Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 1 Dec 2010 17:42:47 +0000 Subject: [PATCH] After parsing a ':' in an enum-specifier within class context, disambiguate between an expression (for a bit-field width) and a type (for a fixed underlying type). Since the disambiguation can be expensive (due to tentative parsing), we perform a simplistic disambiguation based on one-token lookahead before going into the full-blown tentative parsing. Based on a patch by Daniel Wallin. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@120582 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Parser.h | 14 ++++- lib/Parse/ParseDecl.cpp | 54 ++++++++++++++-- lib/Parse/ParseTentative.cpp | 110 +++++++++++++++++++++++++++++++++ test/SemaCXX/enum-bitfield.cpp | 18 ++++++ 4 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 test/SemaCXX/enum-bitfield.cpp diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index e94225813e..5b0b56ac84 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1409,6 +1409,18 @@ private: bool operator!=(const TPResult &RHS) const { return Res != RHS.Res; } }; + /// \brief 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++). + /// + /// This routine does not attempt to resolve any of the trick cases, e.g., + /// those involving lookup of identifiers. + /// + /// \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); + /// isCXXDeclarationSpecifier - Returns TPResult::True() if it is a /// declaration specifier, TPResult::False() if it is not, /// TPResult::Ambiguous() if it could be either a decl-specifier or a @@ -1416,7 +1428,7 @@ private: /// encountered. /// Doesn't consume tokens. TPResult isCXXDeclarationSpecifier(); - + // "Tentative parsing" functions, used for disambiguation. If a parsing error // is encountered they will return TPResult::Error(). // Returning TPResult::True()/False() indicates that the ambiguity was diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index db02c78351..f1fb7b29a1 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1921,7 +1921,6 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, RBraceLoc); } - /// ParseEnumSpecifier /// enum-specifier: [C99 6.7.2.2] /// 'enum' identifier[opt] '{' enumerator-list '}' @@ -2014,10 +2013,57 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, TypeResult BaseType; + // Parse the fixed underlying type. if (getLang().CPlusPlus0x && Tok.is(tok::colon)) { - ConsumeToken(); - SourceRange Range; - BaseType = ParseTypeName(&Range); + bool PossibleBitfield = false; + if (getCurScope()->getFlags() & Scope::ClassScope) { + // 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. + 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 (isCXXDeclarationSpecifier() != TPResult::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); + } } // There are three options here. If we have 'enum foo;', then this is a diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 9bd000c323..997d01076b 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -593,6 +593,116 @@ 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::string_literal: + case tok::wide_string_literal: + case tok::l_square: + case tok::l_paren: + case tok::amp: + 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___null: + case tok::kw___alignof: + case tok::kw___builtin_choose_expr: + case tok::kw___builtin_offsetof: + case tok::kw___builtin_types_compatible_p: + case tok::kw___builtin_va_arg: + case tok::kw___imag: + case tok::kw___real: + case tok::kw___FUNCTION__: + case tok::kw___PRETTY_FUNCTION__: + case tok::kw___has_nothrow_assign: + case tok::kw___has_nothrow_copy: + case tok::kw___has_nothrow_constructor: + case tok::kw___has_trivial_assign: + case tok::kw___has_trivial_copy: + case tok::kw___has_trivial_constructor: + case tok::kw___has_trivial_destructor: + case tok::kw___has_virtual_destructor: + case tok::kw___is_abstract: + case tok::kw___is_base_of: + case tok::kw___is_class: + case tok::kw___is_empty: + case tok::kw___is_enum: + case tok::kw___is_pod: + case tok::kw___is_polymorphic: + case tok::kw___is_union: + case tok::kw___is_literal: + case tok::kw___uuidof: + return TPResult::True(); + + // Obviously starts a type-specifier-seq: + case tok::kw_char: + case tok::kw_const: + case tok::kw_double: + case tok::kw_enum: + case tok::kw_float: + case tok::kw_int: + case tok::kw_long: + 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_char16_t: + case tok::kw_char32_t: + case tok::kw_decltype: + case tok::kw_thread_local: + case tok::kw__Decimal32: + case tok::kw__Decimal64: + case tok::kw__Decimal128: + case tok::kw___thread: + case tok::kw_typeof: + case tok::kw___cdecl: + case tok::kw___stdcall: + case tok::kw___fastcall: + case tok::kw___thiscall: + case tok::kw___vector: + case tok::kw___pixel: + return TPResult::False(); + + default: + break; + } + + return TPResult::Ambiguous(); +} + /// isCXXDeclarationSpecifier - Returns TPResult::True() if it is a declaration /// specifier, TPResult::False() if it is not, TPResult::Ambiguous() if it could /// be either a decl-specifier or a function-style cast, and TPResult::Error() diff --git a/test/SemaCXX/enum-bitfield.cpp b/test/SemaCXX/enum-bitfield.cpp new file mode 100644 index 0000000000..a766116b1c --- /dev/null +++ b/test/SemaCXX/enum-bitfield.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++0x -verify -triple x86_64-apple-darwin %s + +enum E {}; + +struct Z {}; +typedef int Integer; + +struct X { + enum E : 1; + 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{{not an integer constant}} +}; -- 2.40.0