Parsing of C++0x lambda expressions, from John Freeman with help from

David Blaikie!

llvm-svn: 136876
This commit is contained in:
Douglas Gregor 2011-08-04 15:30:47 +00:00
parent 96df560ce1
commit db0b9f1264
12 changed files with 404 additions and 6 deletions

View File

@ -487,6 +487,15 @@ def err_paren_sizeof_parameter_pack : Error<
def err_sizeof_parameter_pack : Error<
"expected parenthesized parameter pack name in 'sizeof...' expression">;
// C++0x lambda expressions
def err_expected_comma_or_rsquare : Error<
"expected ',' or ']' in lambda capture list">;
def err_this_captured_by_reference : Error<
"'this' cannot be captured by reference">;
def err_expected_capture : Error<
"expected variable name or 'this' in lambda capture list">;
def err_expected_lambda_body : Error<"expected body of lambda expression">;
// Availability attribute
def err_expected_version : Error<
"expected a version of the form 'major[.minor[.subminor]]'">;

View File

@ -1185,6 +1185,17 @@ private:
bool *MayBePseudoDestructor = 0,
bool IsTypename = false);
//===--------------------------------------------------------------------===//
// C++0x 5.1.2: Lambda expressions
// [...] () -> type {...}
ExprResult ParseLambdaExpression();
ExprResult TryParseLambdaExpression();
llvm::Optional<unsigned> ParseLambdaIntroducer(LambdaIntroducer &Intro);
bool TryParseLambdaIntroducer(LambdaIntroducer &Intro);
ExprResult ParseLambdaExpressionAfterIntroducer(
LambdaIntroducer &Intro);
//===--------------------------------------------------------------------===//
// C++ 5.2p1: C++ Casts
ExprResult ParseCXXCasts();
@ -1226,7 +1237,7 @@ private:
//===--------------------------------------------------------------------===//
// C++0x 8: Function declaration trailing-return-type
TypeResult ParseTrailingReturnType();
TypeResult ParseTrailingReturnType(SourceRange &Range);
//===--------------------------------------------------------------------===//
// C++ 2.13.5: C++ Boolean Literals

View File

@ -1828,6 +1828,53 @@ private:
SourceLocation LastLocation;
};
/// LambdaCaptureDefault - The default, if any, capture method for a
/// lambda expression.
enum LambdaCaptureDefault {
LCD_None,
LCD_ByCopy,
LCD_ByRef
};
/// LambdaCaptureKind - The different capture forms in a lambda
/// introducer: 'this' or a copied or referenced variable.
enum LambdaCaptureKind {
LCK_This,
LCK_ByCopy,
LCK_ByRef
};
/// LambdaCapture - An individual capture in a lambda introducer.
struct LambdaCapture {
LambdaCaptureKind Kind;
SourceLocation Loc;
IdentifierInfo* Id;
LambdaCapture(LambdaCaptureKind Kind,
SourceLocation Loc,
IdentifierInfo* Id = 0)
: Kind(Kind), Loc(Loc), Id(Id)
{}
};
/// LambdaIntroducer - Represents a complete lambda introducer.
struct LambdaIntroducer {
SourceRange Range;
LambdaCaptureDefault Default;
llvm::SmallVector<LambdaCapture, 4> Captures;
LambdaIntroducer()
: Default(LCD_None) {}
/// addCapture - Append a capture in a lambda introducer.
void addCapture(LambdaCaptureKind Kind,
SourceLocation Loc,
IdentifierInfo* Id = 0) {
Captures.push_back(LambdaCapture(Kind, Loc, Id));
}
};
} // end namespace clang
#endif

View File

@ -3745,7 +3745,10 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
// Parse trailing-return-type[opt].
if (getLang().CPlusPlus0x && Tok.is(tok::arrow)) {
TrailingReturnType = ParseTrailingReturnType().get();
SourceRange Range;
TrailingReturnType = ParseTrailingReturnType(Range).get();
if (Range.getEnd().isValid())
EndLoc = Range.getEnd();
}
}

View File

@ -2442,7 +2442,7 @@ ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification(
/// ParseTrailingReturnType - Parse a trailing return type on a new-style
/// function declaration.
TypeResult Parser::ParseTrailingReturnType() {
TypeResult Parser::ParseTrailingReturnType(SourceRange &Range) {
assert(Tok.is(tok::arrow) && "expected arrow");
ConsumeToken();
@ -2454,8 +2454,6 @@ TypeResult Parser::ParseTrailingReturnType() {
//
// struct X is parsed as class definition because of the trailing
// brace.
SourceRange Range;
return ParseTypeName(&Range);
}

View File

@ -1115,6 +1115,16 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
NotCastExpr, isTypeCast);
}
case tok::l_square:
if (getLang().CPlusPlus0x) {
if (getLang().ObjC1) {
Res = TryParseLambdaExpression();
if (Res.isInvalid())
Res = ParseObjCMessageExpression();
break;
}
Res = ParseLambdaExpression();
break;
}
if (getLang().ObjC1) {
Res = ParseObjCMessageExpression();
break;

View File

@ -15,6 +15,7 @@
#include "clang/Parse/Parser.h"
#include "RAIIObjectsForParser.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ParsedTemplate.h"
#include "llvm/Support/ErrorHandling.h"
@ -504,6 +505,266 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
}
/// ParseLambdaExpression - Parse a C++0x lambda expression.
///
/// lambda-expression:
/// lambda-introducer lambda-declarator[opt] compound-statement
///
/// lambda-introducer:
/// '[' lambda-capture[opt] ']'
///
/// lambda-capture:
/// capture-default
/// capture-list
/// capture-default ',' capture-list
///
/// capture-default:
/// '&'
/// '='
///
/// capture-list:
/// capture
/// capture-list ',' capture
///
/// capture:
/// identifier
/// '&' identifier
/// 'this'
///
/// lambda-declarator:
/// '(' parameter-declaration-clause ')' attribute-specifier[opt]
/// 'mutable'[opt] exception-specification[opt]
/// trailing-return-type[opt]
///
ExprResult Parser::ParseLambdaExpression() {
// Parse lambda-introducer.
LambdaIntroducer Intro;
llvm::Optional<unsigned> DiagID(ParseLambdaIntroducer(Intro));
if (DiagID) {
Diag(Tok, DiagID.getValue());
SkipUntil(tok::r_square);
}
return ParseLambdaExpressionAfterIntroducer(Intro);
}
/// TryParseLambdaExpression - Use lookahead and potentially tentative
/// parsing to determine if we are looking at a C++0x lambda expression, and parse
/// it if we are.
///
/// If we are not looking at a lambda expression, returns ExprError().
ExprResult Parser::TryParseLambdaExpression() {
assert(getLang().CPlusPlus0x
&& Tok.is(tok::l_square)
&& "Not at the start of a possible lambda expression.");
const Token Next = NextToken(), After = GetLookAheadToken(2);
// If lookahead indicates this is a lambda...
if (Next.is(tok::r_square) || // []
Next.is(tok::equal) || // [=
(Next.is(tok::amp) && // [&] or [&,
(After.is(tok::r_square) ||
After.is(tok::comma))) ||
(Next.is(tok::identifier) && // [identifier]
After.is(tok::r_square))) {
return ParseLambdaExpression();
}
// If lookahead indicates this is an Objective-C message...
if (Next.is(tok::identifier) && After.is(tok::identifier)) {
return ExprError();
}
LambdaIntroducer Intro;
if (TryParseLambdaIntroducer(Intro))
return ExprError();
return ParseLambdaExpressionAfterIntroducer(Intro);
}
/// ParseLambdaExpression - Parse a lambda introducer.
///
/// Returns a DiagnosticID if it hit something unexpected.
llvm::Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) {
typedef llvm::Optional<unsigned> DiagResult;
assert(Tok.is(tok::l_square) && "Lambda expressions begin with '['.");
Intro.Range.setBegin(ConsumeBracket());
bool first = true;
// Parse capture-default.
if (Tok.is(tok::amp) &&
(NextToken().is(tok::comma) || NextToken().is(tok::r_square))) {
Intro.Default = LCD_ByRef;
ConsumeToken();
first = false;
} else if (Tok.is(tok::equal)) {
Intro.Default = LCD_ByCopy;
ConsumeToken();
first = false;
}
while (Tok.isNot(tok::r_square)) {
if (!first) {
if (Tok.isNot(tok::comma))
return DiagResult(diag::err_expected_comma_or_rsquare);
ConsumeToken();
}
first = false;
// Parse capture.
LambdaCaptureKind Kind = LCK_ByCopy;
SourceLocation Loc;
IdentifierInfo* Id = 0;
if (Tok.is(tok::kw_this)) {
Kind = LCK_This;
Loc = ConsumeToken();
} else {
if (Tok.is(tok::amp)) {
Kind = LCK_ByRef;
ConsumeToken();
}
if (Tok.is(tok::identifier)) {
Id = Tok.getIdentifierInfo();
Loc = ConsumeToken();
} else if (Tok.is(tok::kw_this)) {
// FIXME: If we want to suggest a fixit here, will need to return more
// than just DiagnosticID. Perhaps full DiagnosticBuilder that can be
// Clear()ed to prevent emission in case of tentative parsing?
return DiagResult(diag::err_this_captured_by_reference);
} else {
return DiagResult(diag::err_expected_capture);
}
}
Intro.addCapture(Kind, Loc, Id);
}
Intro.Range.setEnd(MatchRHSPunctuation(tok::r_square,
Intro.Range.getBegin()));
return DiagResult();
}
/// TryParseLambdaExpression - Tentatively parse a lambda introducer.
///
/// Returns true if it hit something unexpected.
bool Parser::TryParseLambdaIntroducer(LambdaIntroducer &Intro) {
TentativeParsingAction PA(*this);
llvm::Optional<unsigned> DiagID(ParseLambdaIntroducer(Intro));
if (DiagID) {
PA.Revert();
return true;
}
PA.Commit();
return false;
}
/// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda
/// expression.
ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
LambdaIntroducer &Intro) {
// Parse lambda-declarator[opt].
DeclSpec DS(AttrFactory);
Declarator D(DS, Declarator::PrototypeContext);
if (Tok.is(tok::l_paren)) {
ParseScope PrototypeScope(this,
Scope::FunctionPrototypeScope |
Scope::DeclScope);
SourceLocation DeclLoc = ConsumeParen(), DeclEndLoc;
// Parse parameter-declaration-clause.
ParsedAttributes Attr(AttrFactory);
llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo;
SourceLocation EllipsisLoc;
if (Tok.isNot(tok::r_paren))
ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc);
DeclEndLoc = MatchRHSPunctuation(tok::r_paren, DeclLoc);
// Parse 'mutable'[opt].
SourceLocation MutableLoc;
if (Tok.is(tok::kw_mutable)) {
MutableLoc = ConsumeToken();
DeclEndLoc = MutableLoc;
}
// Parse exception-specification[opt].
ExceptionSpecificationType ESpecType = EST_None;
SourceRange ESpecRange;
llvm::SmallVector<ParsedType, 2> DynamicExceptions;
llvm::SmallVector<SourceRange, 2> DynamicExceptionRanges;
ExprResult NoexceptExpr;
ESpecType = MaybeParseExceptionSpecification(ESpecRange,
DynamicExceptions,
DynamicExceptionRanges,
NoexceptExpr);
if (ESpecType != EST_None)
DeclEndLoc = ESpecRange.getEnd();
// Parse attribute-specifier[opt].
MaybeParseCXX0XAttributes(Attr, &DeclEndLoc);
// Parse trailing-return-type[opt].
ParsedType TrailingReturnType;
if (Tok.is(tok::arrow)) {
SourceRange Range;
TrailingReturnType = ParseTrailingReturnType(Range).get();
if (Range.getEnd().isValid())
DeclEndLoc = Range.getEnd();
}
PrototypeScope.Exit();
D.AddTypeInfo(DeclaratorChunk::getFunction(/*hasProto=*/true,
/*isVariadic=*/EllipsisLoc.isValid(),
EllipsisLoc,
ParamInfo.data(), ParamInfo.size(),
DS.getTypeQualifiers(),
/*RefQualifierIsLValueRef=*/true,
/*RefQualifierLoc=*/SourceLocation(),
MutableLoc,
ESpecType, ESpecRange.getBegin(),
DynamicExceptions.data(),
DynamicExceptionRanges.data(),
DynamicExceptions.size(),
NoexceptExpr.isUsable() ?
NoexceptExpr.get() : 0,
DeclLoc, DeclEndLoc, D,
TrailingReturnType),
Attr, DeclEndLoc);
}
// Parse compound-statement.
if (Tok.is(tok::l_brace)) {
// FIXME: Rename BlockScope -> ClosureScope if we decide to continue using
// it.
ParseScope BodyScope(this, Scope::BlockScope | Scope::FnScope |
Scope::BreakScope | Scope::ContinueScope |
Scope::DeclScope);
StmtResult Stmt(ParseCompoundStatementBody());
BodyScope.Exit();
} else {
Diag(Tok, diag::err_expected_lambda_body);
}
return ExprEmpty();
}
/// ParseCXXCasts - This handles the various ways to cast expressions to another
/// type.
///

View File

@ -17,7 +17,7 @@ int comma_attr [[,]]; // expected-error {{expected identifier}}
int scope_attr [[foo::]]; // expected-error {{expected identifier}}
int & [[]] ref_attr = after_attr; // expected-error {{an attribute list cannot appear here}}
class foo {
void after_const_attr () const [[]]; // expected-error {{expected expression}}
void after_const_attr () const [[]]; // expected-error {{expected body of lambda expression}} expected-error {{array has incomplete element type 'void'}}
};
extern "C++" [[]] { } // expected-error {{an attribute list cannot appear here}}
[[]] template <typename T> void before_template_attr (); // expected-error {{an attribute list cannot appear here}}

View File

@ -0,0 +1,27 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
class C {
int f() {
int foo, bar;
[]; // expected-error {{expected body of lambda expression}}
[+] {}; // expected-error {{expected variable name or 'this' in lambda capture list}}
[foo+] {}; // expected-error {{expected ',' or ']' in lambda capture list}}
[foo,&this] {}; // expected-error {{'this' cannot be captured by reference}}
[&this] {}; // expected-error {{'this' cannot be captured by reference}}
[&,] {}; // expected-error {{ expected variable name or 'this' in lambda capture list}}
[=,] {}; // expected-error {{ expected variable name or 'this' in lambda capture list}}
[] {};
[=] (int i) {};
[&] (int) mutable -> void {};
[foo,bar] () { return 3; };
[=,&foo] () {};
[&,foo] () {};
[this] () {};
return 1;
}
};

View File

@ -9,4 +9,5 @@ int main() {
[a bla:0 6:7]; // expected-error {{expected ']'}}
[A foo bar]; // expected-error {{expected ':'}}
[A foo bar bar1]; // expected-error {{expected ':'}}
[] {}; // expected-error {{expected expression}}
}

View File

@ -0,0 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
int main() {
[]{}; // expected-error {{expected expression}}
}

View File

@ -0,0 +1,26 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
class C {
void f() {
int foo, bar;
// fail to parse as a lambda introducer, so we get objc message parsing errors instead
[foo,+] {}; // expected-error {{expected expression}}
[]; // expected-error {{expected body of lambda expression}}
[=,foo+] {}; // expected-error {{expected ',' or ']' in lambda capture list}}
[&this] {}; // expected-error {{address expression must be an lvalue}}
[] {};
[=] (int i) {};
[&] (int) mutable -> void {};
// FIXME: this error occurs because we do not yet handle lambda scopes
// properly. I did not anticipate it because I thought it was a semantic (not
// syntactic) check.
[foo,bar] () { return 3; }; // expected-error {{void function 'f' should not return a value}}
[=,&foo] () {};
[this] () {};
}
};