forked from OSchip/llvm-project
Update parser's disambiguation to cope with braced function-style casts in
C++11, and with braced-init-list initializers in conditions. This exposed an ambiguity with enum underlying types versus bitfields, which we resolve by treating 'enum E : T {' as always defining an enumeration (even if it would only successfully parse as a bitfield). This appears to be g++ compatible. llvm-svn: 151227
This commit is contained in:
parent
28d4803ade
commit
1e3b0f0614
|
@ -1828,9 +1828,11 @@ private:
|
||||||
/// declaration specifier, TPResult::False() if it is not,
|
/// declaration specifier, TPResult::False() if it is not,
|
||||||
/// TPResult::Ambiguous() if it could be either a decl-specifier or a
|
/// TPResult::Ambiguous() if it could be either a decl-specifier or a
|
||||||
/// function-style cast, and TPResult::Error() if a parsing error was
|
/// 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.
|
/// Doesn't consume tokens.
|
||||||
TPResult isCXXDeclarationSpecifier();
|
TPResult
|
||||||
|
isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False());
|
||||||
|
|
||||||
// "Tentative parsing" functions, used for disambiguation. If a parsing error
|
// "Tentative parsing" functions, used for disambiguation. If a parsing error
|
||||||
// is encountered they will return TPResult::Error().
|
// is encountered they will return TPResult::Error().
|
||||||
|
|
|
@ -3000,9 +3000,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
|
||||||
|
|
||||||
// Consume the ':'.
|
// Consume the ':'.
|
||||||
ConsumeToken();
|
ConsumeToken();
|
||||||
|
|
||||||
if ((getLang().CPlusPlus &&
|
// If we see a type specifier followed by an open-brace, we have an
|
||||||
isCXXDeclarationSpecifier() != TPResult::True()) ||
|
// 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))) {
|
(!getLang().CPlusPlus && !isDeclarationSpecifier(true))) {
|
||||||
// We'll parse this as a bitfield later.
|
// We'll parse this as a bitfield later.
|
||||||
PossibleBitfield = true;
|
PossibleBitfield = true;
|
||||||
|
|
|
@ -233,6 +233,8 @@ Parser::TPResult Parser::TryParseInitDeclaratorList() {
|
||||||
/// condition:
|
/// condition:
|
||||||
/// expression
|
/// expression
|
||||||
/// type-specifier-seq declarator '=' assignment-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]
|
/// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt]
|
||||||
/// '=' assignment-expression
|
/// '=' assignment-expression
|
||||||
///
|
///
|
||||||
|
@ -274,6 +276,8 @@ bool Parser::isCXXConditionDeclaration() {
|
||||||
if (Tok.is(tok::equal) ||
|
if (Tok.is(tok::equal) ||
|
||||||
Tok.is(tok::kw_asm) || Tok.is(tok::kw___attribute))
|
Tok.is(tok::kw_asm) || Tok.is(tok::kw___attribute))
|
||||||
TPR = TPResult::True();
|
TPR = TPResult::True();
|
||||||
|
else if (getLang().CPlusPlus0x && Tok.is(tok::l_brace))
|
||||||
|
TPR = TPResult::True();
|
||||||
else
|
else
|
||||||
TPR = TPResult::False();
|
TPR = TPResult::False();
|
||||||
}
|
}
|
||||||
|
@ -834,7 +838,8 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
|
||||||
/// 'volatile'
|
/// 'volatile'
|
||||||
/// [GNU] restrict
|
/// [GNU] restrict
|
||||||
///
|
///
|
||||||
Parser::TPResult Parser::isCXXDeclarationSpecifier() {
|
Parser::TPResult
|
||||||
|
Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
|
||||||
switch (Tok.getKind()) {
|
switch (Tok.getKind()) {
|
||||||
case tok::identifier: // foo::bar
|
case tok::identifier: // foo::bar
|
||||||
// Check for need to substitute AltiVec __vector keyword
|
// Check for need to substitute AltiVec __vector keyword
|
||||||
|
@ -849,7 +854,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
|
||||||
return TPResult::Error();
|
return TPResult::Error();
|
||||||
if (Tok.is(tok::identifier))
|
if (Tok.is(tok::identifier))
|
||||||
return TPResult::False();
|
return TPResult::False();
|
||||||
return isCXXDeclarationSpecifier();
|
return isCXXDeclarationSpecifier(BracedCastResult);
|
||||||
|
|
||||||
case tok::coloncolon: { // ::foo::bar
|
case tok::coloncolon: { // ::foo::bar
|
||||||
const Token &Next = NextToken();
|
const Token &Next = NextToken();
|
||||||
|
@ -863,7 +868,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
|
||||||
// recurse to handle whatever we get.
|
// recurse to handle whatever we get.
|
||||||
if (TryAnnotateTypeOrScopeToken())
|
if (TryAnnotateTypeOrScopeToken())
|
||||||
return TPResult::Error();
|
return TPResult::Error();
|
||||||
return isCXXDeclarationSpecifier();
|
return isCXXDeclarationSpecifier(BracedCastResult);
|
||||||
|
|
||||||
// decl-specifier:
|
// decl-specifier:
|
||||||
// storage-class-specifier
|
// storage-class-specifier
|
||||||
|
@ -965,7 +970,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
|
||||||
bool isIdentifier = Tok.is(tok::identifier);
|
bool isIdentifier = Tok.is(tok::identifier);
|
||||||
TPResult TPR = TPResult::False();
|
TPResult TPR = TPResult::False();
|
||||||
if (!isIdentifier)
|
if (!isIdentifier)
|
||||||
TPR = isCXXDeclarationSpecifier();
|
TPR = isCXXDeclarationSpecifier(BracedCastResult);
|
||||||
PA.Revert();
|
PA.Revert();
|
||||||
|
|
||||||
if (isIdentifier ||
|
if (isIdentifier ||
|
||||||
|
@ -1005,6 +1010,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
|
||||||
|
|
||||||
TPResult TPR = TryParseProtocolQualifiers();
|
TPResult TPR = TryParseProtocolQualifiers();
|
||||||
bool isFollowedByParen = Tok.is(tok::l_paren);
|
bool isFollowedByParen = Tok.is(tok::l_paren);
|
||||||
|
bool isFollowedByBrace = Tok.is(tok::l_brace);
|
||||||
|
|
||||||
PA.Revert();
|
PA.Revert();
|
||||||
|
|
||||||
|
@ -1013,6 +1019,9 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
|
||||||
|
|
||||||
if (isFollowedByParen)
|
if (isFollowedByParen)
|
||||||
return TPResult::Ambiguous();
|
return TPResult::Ambiguous();
|
||||||
|
|
||||||
|
if (getLang().CPlusPlus0x && isFollowedByBrace)
|
||||||
|
return BracedCastResult;
|
||||||
|
|
||||||
return TPResult::True();
|
return TPResult::True();
|
||||||
}
|
}
|
||||||
|
@ -1036,6 +1045,15 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
|
||||||
if (NextToken().is(tok::l_paren))
|
if (NextToken().is(tok::l_paren))
|
||||||
return TPResult::Ambiguous();
|
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())
|
if (isStartOfObjCClassMessageMissingOpenBracket())
|
||||||
return TPResult::False();
|
return TPResult::False();
|
||||||
|
|
||||||
|
@ -1050,6 +1068,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
|
||||||
|
|
||||||
TPResult TPR = TryParseTypeofSpecifier();
|
TPResult TPR = TryParseTypeofSpecifier();
|
||||||
bool isFollowedByParen = Tok.is(tok::l_paren);
|
bool isFollowedByParen = Tok.is(tok::l_paren);
|
||||||
|
bool isFollowedByBrace = Tok.is(tok::l_brace);
|
||||||
|
|
||||||
PA.Revert();
|
PA.Revert();
|
||||||
|
|
||||||
|
@ -1059,6 +1078,9 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
|
||||||
if (isFollowedByParen)
|
if (isFollowedByParen)
|
||||||
return TPResult::Ambiguous();
|
return TPResult::Ambiguous();
|
||||||
|
|
||||||
|
if (getLang().CPlusPlus0x && isFollowedByBrace)
|
||||||
|
return BracedCastResult;
|
||||||
|
|
||||||
return TPResult::True();
|
return TPResult::True();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1219,6 +1241,8 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause() {
|
||||||
MaybeParseMicrosoftAttributes(attrs);
|
MaybeParseMicrosoftAttributes(attrs);
|
||||||
|
|
||||||
// decl-specifier-seq
|
// 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();
|
TPResult TPR = TryParseDeclarationSpecifier();
|
||||||
if (TPR != TPResult::Ambiguous())
|
if (TPR != TPResult::Ambiguous())
|
||||||
return TPR;
|
return TPR;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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}}
|
||||||
if (S b(n) == 0) {} // expected-error {{a function type is not allowed here}} expected-error {{did you mean '='?}}
|
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
|
if (S{a}) {} // ok
|
||||||
// does not yet know about braced function-style casts.
|
|
||||||
if (S{a}) {} // unexpected-error{{unqualified-id}}
|
|
||||||
|
|
||||||
if (S a{a}) {} // ok
|
if (S a{a}) {} // ok
|
||||||
if (S a = {a}) {} // ok
|
if (S a = {a}) {} // ok
|
||||||
if (S a == {a}) {} // expected-error {{did you mean '='?}}
|
if (S a == {a}) {} // expected-error {{did you mean '='?}}
|
||||||
|
|
||||||
|
if (S(b){a}) {} // ok
|
||||||
|
if (S(b) = {a}) {} // ok
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue