Disambiguate between C++11 lambda expressions and C99 array

designators in the parser. In the worst case, this disambiguation
requires tentative parsing just past the closing ']', but for most
cases we'll be able to tell by looking ahead just one token (without
going into the heavyweight tentative parsing machinery).

llvm-svn: 150790
This commit is contained in:
Douglas Gregor 2012-02-17 03:49:44 +00:00
parent 14a941380a
commit a80cae11f6
3 changed files with 92 additions and 9 deletions

View File

@ -1473,6 +1473,7 @@ private:
return ParseAssignmentExpression();
return ParseBraceInitializer();
}
bool MayBeDesignationStart();
ExprResult ParseBraceInitializer();
ExprResult ParseInitializerWithPotentialDesignator();

View File

@ -21,15 +21,86 @@
using namespace clang;
/// MayBeDesignationStart - Return true if this token might be the start of a
/// designator. If we can tell it is impossible that it is a designator, return
/// false.
static bool MayBeDesignationStart(tok::TokenKind K, Preprocessor &PP) {
switch (K) {
default: return false;
/// MayBeDesignationStart - Return true if the current token might be the start
/// of a designator. If we can tell it is impossible that it is a designator,
/// return false.
bool Parser::MayBeDesignationStart() {
switch (Tok.getKind()) {
default:
return false;
case tok::period: // designator: '.' identifier
case tok::l_square: // designator: array-designator
return true;
case tok::l_square: { // designator: array-designator
if (!PP.getLangOptions().CPlusPlus0x)
return true;
// C++11 lambda expressions and C99 designators can be ambiguous all the
// way through the closing ']' and to the next character. Handle the easy
// cases here, and fall back to tentative parsing if those fail.
switch (PP.LookAhead(0).getKind()) {
case tok::equal:
case tok::r_square:
// Definitely starts a lambda expression.
return false;
case tok::amp:
case tok::kw_this:
case tok::identifier:
// We have to do additional analysis, because these could be the
// start of a constant expression or a lambda capture list.
break;
default:
// Anything not mentioned above cannot occur following a '[' in a
// lambda expression.
return true;
}
// Parse up to (at most) the token after the closing ']' to determine
// whether this is a C99 designator or a lambda.
TentativeParsingAction Tentative(*this);
ConsumeBracket();
while (true) {
switch (Tok.getKind()) {
case tok::equal:
case tok::amp:
case tok::identifier:
case tok::kw_this:
// These tokens can occur in a capture list or a constant-expression.
// Keep looking.
ConsumeToken();
continue;
case tok::comma:
// Since a comma cannot occur in a constant-expression, this must
// be a lambda.
Tentative.Revert();
return false;
case tok::r_square: {
// Once we hit the closing square bracket, we look at the next
// token. If it's an '=', this is a designator. Otherwise, it's a
// lambda expression. This decision favors lambdas over the older
// GNU designator syntax, which allows one to omit the '=', but is
// consistent with GCC.
ConsumeBracket();
tok::TokenKind Kind = Tok.getKind();
Tentative.Revert();
return Kind == tok::equal;
}
default:
// Anything else cannot occur in a lambda capture list, so it
// must be a designator.
Tentative.Revert();
return true;
}
}
return true;
}
case tok::identifier: // designation: identifier ':'
return PP.LookAhead(0).is(tok::colon);
}
@ -356,7 +427,7 @@ ExprResult Parser::ParseBraceInitializer() {
// If we know that this cannot be a designation, just parse the nested
// initializer directly.
ExprResult SubElt;
if (MayBeDesignationStart(Tok.getKind(), PP))
if (MayBeDesignationStart())
SubElt = ParseInitializerWithPotentialDesignator();
else
SubElt = ParseInitializer();
@ -439,7 +510,7 @@ bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
// If we know that this cannot be a designation, just parse the nested
// initializer directly.
ExprResult SubElt;
if (MayBeDesignationStart(Tok.getKind(), PP))
if (MayBeDesignationStart())
SubElt = ParseInitializerWithPotentialDesignator();
else
SubElt = ParseInitializer();

View File

@ -25,5 +25,16 @@ class C {
return 1;
}
void designator_or_lambda() {
typedef int T;
const int b = 0;
const int c = 1;
int a1[1] = {[b] (T()) {}}; // expected-error{{no viable conversion from 'C::<lambda}}
int a2[1] = {[b] = 1 };
int a3[1] = {[b,c] = 1 }; // expected-error{{expected body of lambda expression}}
int a4[1] = {[&b] = 1 }; // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'const int *'}}
int a5[3] = { []{return 0;}() };
int a6[1] = {[this] = 1 }; // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'C *'}}
}
};