forked from OSchip/llvm-project
Parsing of C++0x lambda expressions, from John Freeman with help from
David Blaikie! llvm-svn: 136876
This commit is contained in:
parent
96df560ce1
commit
db0b9f1264
|
@ -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]]'">;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
int main() {
|
||||
[]{}; // expected-error {{expected expression}}
|
||||
}
|
|
@ -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] () {};
|
||||
}
|
||||
|
||||
};
|
||||
|
Loading…
Reference in New Issue