diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 27e86ebab695..079a93fb5b8d 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1828,9 +1828,11 @@ private: /// 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() if a parsing error was - /// encountered. + /// encountered. If it could be a braced C++11 function-style cast, returns + /// BracedCastResult. /// Doesn't consume tokens. - TPResult isCXXDeclarationSpecifier(); + TPResult + isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False()); // "Tentative parsing" functions, used for disambiguation. If a parsing error // is encountered they will return TPResult::Error(). diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 419389002105..4d93eaf85d5c 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3000,9 +3000,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // Consume the ':'. ConsumeToken(); - - if ((getLang().CPlusPlus && - isCXXDeclarationSpecifier() != TPResult::True()) || + + // 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 ((getLang().CPlusPlus && + isCXXDeclarationSpecifier(TPResult::True()) != TPResult::True()) || (!getLang().CPlusPlus && !isDeclarationSpecifier(true))) { // We'll parse this as a bitfield later. PossibleBitfield = true; diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 987ae65d066b..35c418cb7efa 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -233,6 +233,8 @@ Parser::TPResult Parser::TryParseInitDeclaratorList() { /// condition: /// expression /// type-specifier-seq declarator '=' assignment-expression +/// [C++11] type-specifier-seq declarator '=' initializer-clause +/// [C++11] type-specifier-seq declarator braced-init-list /// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt] /// '=' assignment-expression /// @@ -274,6 +276,8 @@ bool Parser::isCXXConditionDeclaration() { if (Tok.is(tok::equal) || Tok.is(tok::kw_asm) || Tok.is(tok::kw___attribute)) TPR = TPResult::True(); + else if (getLang().CPlusPlus0x && Tok.is(tok::l_brace)) + TPR = TPResult::True(); else TPR = TPResult::False(); } @@ -834,7 +838,8 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { /// 'volatile' /// [GNU] restrict /// -Parser::TPResult Parser::isCXXDeclarationSpecifier() { +Parser::TPResult +Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) { switch (Tok.getKind()) { case tok::identifier: // foo::bar // Check for need to substitute AltiVec __vector keyword @@ -849,7 +854,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { return TPResult::Error(); if (Tok.is(tok::identifier)) return TPResult::False(); - return isCXXDeclarationSpecifier(); + return isCXXDeclarationSpecifier(BracedCastResult); case tok::coloncolon: { // ::foo::bar const Token &Next = NextToken(); @@ -863,7 +868,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return TPResult::Error(); - return isCXXDeclarationSpecifier(); + return isCXXDeclarationSpecifier(BracedCastResult); // decl-specifier: // storage-class-specifier @@ -965,7 +970,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { bool isIdentifier = Tok.is(tok::identifier); TPResult TPR = TPResult::False(); if (!isIdentifier) - TPR = isCXXDeclarationSpecifier(); + TPR = isCXXDeclarationSpecifier(BracedCastResult); PA.Revert(); if (isIdentifier || @@ -1005,6 +1010,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { TPResult TPR = TryParseProtocolQualifiers(); bool isFollowedByParen = Tok.is(tok::l_paren); + bool isFollowedByBrace = Tok.is(tok::l_brace); PA.Revert(); @@ -1013,6 +1019,9 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { if (isFollowedByParen) return TPResult::Ambiguous(); + + if (getLang().CPlusPlus0x && isFollowedByBrace) + return BracedCastResult; return TPResult::True(); } @@ -1036,6 +1045,15 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { if (NextToken().is(tok::l_paren)) return TPResult::Ambiguous(); + // This is a function-style cast in all cases we disambiguate other than + // one: + // struct S { + // enum E : int { a = 4 }; // enum + // enum E : int { 4 }; // bit-field + // }; + if (getLang().CPlusPlus0x && NextToken().is(tok::l_brace)) + return BracedCastResult; + if (isStartOfObjCClassMessageMissingOpenBracket()) return TPResult::False(); @@ -1050,6 +1068,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { TPResult TPR = TryParseTypeofSpecifier(); bool isFollowedByParen = Tok.is(tok::l_paren); + bool isFollowedByBrace = Tok.is(tok::l_brace); PA.Revert(); @@ -1059,6 +1078,9 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { if (isFollowedByParen) return TPResult::Ambiguous(); + if (getLang().CPlusPlus0x && isFollowedByBrace) + return BracedCastResult; + return TPResult::True(); } @@ -1219,6 +1241,8 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause() { MaybeParseMicrosoftAttributes(attrs); // decl-specifier-seq + // A parameter-declaration's initializer must be preceded by an '=', so + // decl-specifier-seq '{' is not a parameter in C++11. TPResult TPR = TryParseDeclarationSpecifier(); if (TPR != TPResult::Ambiguous()) return TPR; diff --git a/clang/test/Parser/cxx0x-ambig.cpp b/clang/test/Parser/cxx0x-ambig.cpp new file mode 100644 index 000000000000..c955dc16909d --- /dev/null +++ b/clang/test/Parser/cxx0x-ambig.cpp @@ -0,0 +1,92 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s + +// New exciting ambiguities in C++11 + +// final 'context sensitive' mess. +namespace final { + struct S { int n; }; + namespace N { + int n; + // This defines a class, not a variable, even though it would successfully + // parse as a variable but not as a class. DR1318's wording suggests that + // this disambiguation is only performed on an ambiguity, but that was not + // the intent. + struct S final { + int(n) // expected-error {{expected ';'}} + }; + } +} + +// enum versus bitfield mess. +namespace bitfield { + enum E {}; + + struct T { + constexpr T() {} + constexpr T(int) {} + constexpr T(T, T, T, T) {} + constexpr T operator=(T) { return *this; } + constexpr operator int() { return 4; } + }; + constexpr T a, b, c, d; + + struct S1 { + enum E : T ( a = 1, b = 2, c = 3, 4 ); // ok, declares a bitfield + }; + // This could be a bit-field. + struct S2 { + enum E : T { a = 1, b = 2, c = 3, 4 }; // expected-error {{non-integral type}} expected-error {{expected '}'}} expected-note {{to match}} + }; + struct S3 { + enum E : int { a = 1, b = 2, c = 3, d }; // ok, defines an enum + }; + // Ambiguous. + struct S4 { + enum E : int { a = 1 }; // ok, defines an enum + }; + // This could be a bit-field, but would be ill-formed due to the anonymous + // member being initialized. + struct S5 { + enum E : int { a = 1 } { b = 2 }; // expected-error {{expected member name}} + }; + // This could be a bit-field. + struct S6 { + enum E : int { 1 }; // expected-error {{expected '}'}} expected-note {{to match}} + }; + + struct U { + constexpr operator T() { return T(); } // expected-note 2{{candidate}} + }; + // This could be a bit-field. + struct S7 { + enum E : int { a = U() }; // expected-error {{no viable conversion}} + }; + // This could be a bit-field, and does not conform to the grammar of an + // enum definition, because 'id(U())' is not a constant-expression. + constexpr const U &id(const U &u) { return u; } + struct S8 { + enum E : int { a = id(U()) }; // expected-error {{no viable conversion}} + }; +} + +namespace trailing_return { + typedef int n; + int a; + + struct S { + S(int); + S *operator()() const; + int n; + }; + + namespace N { + void f() { + // This parses as a function declaration, but DR1223 makes the presence of + // 'auto' be used for disambiguation. + S(a)()->n; // ok, expression; expected-warning{{expression result unused}} + auto(a)()->n; // ok, function declaration + using T = decltype(a); + using T = auto() -> n; + } + } +} diff --git a/clang/test/Parser/cxx0x-condition.cpp b/clang/test/Parser/cxx0x-condition.cpp index ce7d1a8aefd6..e45cd866ff21 100644 --- a/clang/test/Parser/cxx0x-condition.cpp +++ b/clang/test/Parser/cxx0x-condition.cpp @@ -27,11 +27,11 @@ void f() { if (S b(n) = 0) {} // expected-error {{a function type is not allowed here}} if (S b(n) == 0) {} // expected-error {{a function type is not allowed here}} expected-error {{did you mean '='?}} - // FIXME: this is legal, and incorrectly rejected, because tentative parsing - // does not yet know about braced function-style casts. - if (S{a}) {} // unexpected-error{{unqualified-id}} - + if (S{a}) {} // ok if (S a{a}) {} // ok if (S a = {a}) {} // ok if (S a == {a}) {} // expected-error {{did you mean '='?}} + + if (S(b){a}) {} // ok + if (S(b) = {a}) {} // ok }