Parsing of C++0x lambda expressions, from John Freeman with help from
David Blaikie!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136876 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index 3764a40..27b4f85 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -487,6 +487,15 @@
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]]'">;
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 83b0cd4..af3f40e 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1186,6 +1186,17 @@
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 @@
//===--------------------------------------------------------------------===//
// C++0x 8: Function declaration trailing-return-type
- TypeResult ParseTrailingReturnType();
+ TypeResult ParseTrailingReturnType(SourceRange &Range);
//===--------------------------------------------------------------------===//
// C++ 2.13.5: C++ Boolean Literals
diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h
index ad96c45..cdd7dba 100644
--- a/include/clang/Sema/DeclSpec.h
+++ b/include/clang/Sema/DeclSpec.h
@@ -1828,6 +1828,53 @@
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
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index f946f79..1e318fe 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -3745,7 +3745,10 @@
// 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();
}
}
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 344a433..df2c5f8 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -2442,7 +2442,7 @@
/// 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 @@
//
// struct X is parsed as class definition because of the trailing
// brace.
-
- SourceRange Range;
return ParseTypeName(&Range);
}
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 3cd1f39..42d56c3 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -1115,6 +1115,16 @@
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;
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index d88356c..757d86e 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -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 @@
}
+/// 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.
///
diff --git a/test/Parser/cxx0x-attributes.cpp b/test/Parser/cxx0x-attributes.cpp
index f65e290..0285ee1 100644
--- a/test/Parser/cxx0x-attributes.cpp
+++ b/test/Parser/cxx0x-attributes.cpp
@@ -17,7 +17,7 @@
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}}
diff --git a/test/Parser/cxx0x-lambda-expressions.cpp b/test/Parser/cxx0x-lambda-expressions.cpp
new file mode 100644
index 0000000..54281e3
--- /dev/null
+++ b/test/Parser/cxx0x-lambda-expressions.cpp
@@ -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;
+ }
+
+};
+
diff --git a/test/Parser/objc-messaging-neg-1.m b/test/Parser/objc-messaging-neg-1.m
index 4ddadb8..bb496e9 100644
--- a/test/Parser/objc-messaging-neg-1.m
+++ b/test/Parser/objc-messaging-neg-1.m
@@ -9,4 +9,5 @@
[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}}
}
diff --git a/test/Parser/objcxx-lambda-expressions-neg.mm b/test/Parser/objcxx-lambda-expressions-neg.mm
new file mode 100644
index 0000000..864cc6b
--- /dev/null
+++ b/test/Parser/objcxx-lambda-expressions-neg.mm
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+int main() {
+ []{}; // expected-error {{expected expression}}
+}
diff --git a/test/Parser/objcxx0x-lambda-expressions.mm b/test/Parser/objcxx0x-lambda-expressions.mm
new file mode 100644
index 0000000..d100e2e
--- /dev/null
+++ b/test/Parser/objcxx0x-lambda-expressions.mm
@@ -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] () {};
+ }
+
+};
+