From c7c97144afcb8a7ceeadd3d56ee0b25acd43856b Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Wed, 4 Jan 2012 02:40:39 +0000 Subject: [PATCH] Stub out the Sema interface for lambda expressions, and change the parser to use it. Unconditionally error on lambda expressions because they don't work in any meaningful way yet. llvm-svn: 147515 --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/Sema.h | 17 ++++++ clang/lib/Parse/ParseExpr.cpp | 7 ++- clang/lib/Parse/ParseExprCXX.cpp | 59 +++++++++++++------ clang/lib/Sema/SemaExprCXX.cpp | 35 +++++++++++ clang/test/Parser/cxx0x-attributes.cpp | 2 +- .../test/Parser/cxx0x-lambda-expressions.cpp | 14 ++--- .../Parser/objcxx0x-lambda-expressions.mm | 12 ++-- clang/test/SemaCXX/cxx98-compat.cpp | 3 +- 9 files changed, 118 insertions(+), 33 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7e5fff8ab466..d5047506cc98 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3984,6 +3984,8 @@ def err_throw_incomplete_ptr : Error< def err_return_in_constructor_handler : Error< "return in the catch of a function try block of a constructor is illegal">; +def err_lambda_unsupported : Error<"lambda expressions are not supported yet">; + def err_operator_arrow_circular : Error< "circular pointer delegation detected">; def err_pseudo_dtor_base_not_scalar : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 086bf23dab0a..940ae2da9898 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3449,6 +3449,23 @@ public: /// initializer for the declaration 'Dcl'. void ActOnCXXExitDeclInitializer(Scope *S, Decl *Dcl); + /// ActOnLambdaStart - This callback is invoked when a lambda expression is + /// started. + void ActOnLambdaStart(SourceLocation StartLoc, Scope *CurScope); + + /// ActOnLambdaArguments - This callback allows processing of lambda arguments. + /// If there are no arguments, this is still invoked. + void ActOnLambdaArguments(Declarator &ParamInfo, Scope *CurScope); + + /// ActOnLambdaError - If there is an error parsing a lambda, this callback + /// is invoked to pop the information about the lambda from the action impl. + void ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope); + + /// ActOnLambdaExpr - This is called when the body of a lambda expression + /// was successfully completed. + ExprResult ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, + Scope *CurScope); + // ParseObjCStringLiteral - Parse Objective-C string literals. ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, Expr **Strings, diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 0dd48a29469e..7eac98417257 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1132,8 +1132,13 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::l_square: if (getLang().CPlusPlus0x) { if (getLang().ObjC1) { + // C++11 lambda expressions and Objective-C message sends both start with a + // square bracket. There are three possibilities here: + // we have a valid lambda expression, we have an invalid lambda + // expression, or we have something that doesn't appear to be a lambda. + // If we're in the last case, we fall back to ParseObjCMessageExpression. Res = TryParseLambdaExpression(); - if (Res.isInvalid()) + if (!Res.isInvalid() && !Res.get()) Res = ParseObjCMessageExpression(); break; } diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index bd97fcee7c5a..901a537f5ed7 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -14,6 +14,7 @@ #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" #include "RAIIObjectsForParser.h" +#include "clang/Basic/PrettyStackTrace.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ParsedTemplate.h" @@ -563,6 +564,9 @@ ExprResult Parser::ParseLambdaExpression() { if (DiagID) { Diag(Tok, DiagID.getValue()); SkipUntil(tok::r_square); + SkipUntil(tok::l_brace); + SkipUntil(tok::r_brace); + return ExprError(); } return ParseLambdaExpressionAfterIntroducer(Intro); @@ -591,14 +595,21 @@ ExprResult Parser::TryParseLambdaExpression() { return ParseLambdaExpression(); } - // If lookahead indicates this is an Objective-C message... + // If lookahead indicates an ObjC message send... + // [identifier identifier if (Next.is(tok::identifier) && After.is(tok::identifier)) { - return ExprError(); + return ExprEmpty(); } + // Here, we're stuck: lambda introducers and Objective-C message sends are + // unambiguous, but it requires arbitrary lookhead. [a,b,c,d,e,f,g] is a + // lambda, and [a,b,c,d,e,f,g h] is a Objective-C message send. Instead of + // writing two routines to parse a lambda introducer, just try to parse + // a lambda introducer first, and fall back if that fails. + // (TryParseLambdaIntroducer never produces any diagnostic output.) LambdaIntroducer Intro; if (TryParseLambdaIntroducer(Intro)) - return ExprError(); + return ExprEmpty(); return ParseLambdaExpressionAfterIntroducer(Intro); } @@ -694,11 +705,17 @@ bool Parser::TryParseLambdaIntroducer(LambdaIntroducer &Intro) { /// expression. ExprResult Parser::ParseLambdaExpressionAfterIntroducer( LambdaIntroducer &Intro) { - Diag(Intro.Range.getBegin(), diag::warn_cxx98_compat_lambda); + SourceLocation LambdaBeginLoc = Intro.Range.getBegin(); + Diag(LambdaBeginLoc, diag::warn_cxx98_compat_lambda); + + PrettyStackTraceLoc CrashInfo(PP.getSourceManager(), LambdaBeginLoc, + "lambda expression parsing"); + + Actions.ActOnLambdaStart(LambdaBeginLoc, getCurScope()); // Parse lambda-declarator[opt]. DeclSpec DS(AttrFactory); - Declarator D(DS, Declarator::PrototypeContext); + Declarator D(DS, Declarator::BlockLiteralContext); if (Tok.is(tok::l_paren)) { ParseScope PrototypeScope(this, @@ -775,24 +792,32 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( DeclLoc, DeclEndLoc, D, TrailingReturnType), Attr, DeclEndLoc); + + // Inform sema that we are starting a block. + Actions.ActOnLambdaArguments(D, getCurScope()); } // 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); - - ParseCompoundStatementBody(); - - BodyScope.Exit(); - } else { + if (!Tok.is(tok::l_brace)) { Diag(Tok, diag::err_expected_lambda_body); + Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope()); + return ExprError(); } - return ExprEmpty(); + // 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(); + + if (!Stmt.isInvalid()) + return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.take(), + getCurScope()); + + Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope()); + return ExprError(); } /// ParseCXXCasts - This handles the various ways to cast expressions to another diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index eaa330b183a9..67e1c2fb099e 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4774,3 +4774,38 @@ Sema::CheckMicrosoftIfExistsSymbol(Scope *S, SourceLocation KeywordLoc, return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo); } +//===----------------------------------------------------------------------===// +// Lambdas. +//===----------------------------------------------------------------------===// + +void Sema::ActOnLambdaStart(SourceLocation StartLoc, Scope *CurScope) { + // FIXME: Add lambda-scope + // FIXME: Build lambda-decl + // FIXME: PushDeclContext + + // Enter a new evaluation context to insulate the block from any + // cleanups from the enclosing full-expression. + PushExpressionEvaluationContext(PotentiallyEvaluated); +} + +void Sema::ActOnLambdaArguments(Declarator &ParamInfo, Scope *CurScope) { + // FIXME: Implement +} + +void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope) { + // Leave the expression-evaluation context. + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + // Leave the context of the lambda. + // FIXME: PopDeclContext + // FIXME: Pop lambda-scope +} + +ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, + Stmt *Body, Scope *CurScope) { + // FIXME: Implement + Diag(StartLoc, diag::err_lambda_unsupported); + ActOnLambdaError(StartLoc, CurScope); + return ExprError(); +} diff --git a/clang/test/Parser/cxx0x-attributes.cpp b/clang/test/Parser/cxx0x-attributes.cpp index e762b57a93d6..ff9118a0e157 100644 --- a/clang/test/Parser/cxx0x-attributes.cpp +++ b/clang/test/Parser/cxx0x-attributes.cpp @@ -20,7 +20,7 @@ int scope_attr [[foo::]]; // expected-error {{expected identifier}} unsigned [[]] int attr_in_decl_spec; // expected-error {{expected unqualified-id}} int & [[]] ref_attr = after_attr; // expected-error {{an attribute list cannot appear here}} class foo { - void after_const_attr () const [[]]; // expected-error {{expected body of lambda expression}} expected-error {{array has incomplete element type 'void'}} + void after_const_attr () const [[]]; // expected-error {{expected body of lambda expression}} }; extern "C++" [[]] { } // expected-error {{an attribute list cannot appear here}} [[]] template void before_template_attr (); // expected-error {{an attribute list cannot appear here}} diff --git a/clang/test/Parser/cxx0x-lambda-expressions.cpp b/clang/test/Parser/cxx0x-lambda-expressions.cpp index 4fa4e6f1b99e..df8d804931d0 100644 --- a/clang/test/Parser/cxx0x-lambda-expressions.cpp +++ b/clang/test/Parser/cxx0x-lambda-expressions.cpp @@ -12,13 +12,13 @@ class C { [&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] () {}; + [] {}; // expected-error {{lambda expressions are not supported yet}} + [=] (int i) {}; // expected-error {{lambda expressions are not supported yet}} + [&] (int) mutable -> void {}; // expected-error {{lambda expressions are not supported yet}} + [foo,bar] () { return 3; }; // expected-error {{lambda expressions are not supported yet}} + [=,&foo] () {}; // expected-error {{lambda expressions are not supported yet}} + [&,foo] () {}; // expected-error {{lambda expressions are not supported yet}} + [this] () {}; // expected-error {{lambda expressions are not supported yet}} return 1; } diff --git a/clang/test/Parser/objcxx0x-lambda-expressions.mm b/clang/test/Parser/objcxx0x-lambda-expressions.mm index 937464918b6a..b9a96d6b2df2 100644 --- a/clang/test/Parser/objcxx0x-lambda-expressions.mm +++ b/clang/test/Parser/objcxx0x-lambda-expressions.mm @@ -11,15 +11,15 @@ class C { []; // 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 {}; + [] {}; // expected-error {{lambda expressions are not supported yet}} + [=] (int i) {}; // expected-error {{lambda expressions are not supported yet}} + [&] (int) mutable -> void {}; // expected-error {{lambda expressions are not supported yet}} // 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] () {}; + [foo,bar] () { return 3; }; // expected-error {{void function 'f' should not return a value}} expected-error {{lambda expressions are not supported yet}} + [=,&foo] () {}; // expected-error {{lambda expressions are not supported yet}} + [this] () {}; // expected-error {{lambda expressions are not supported yet}} } }; diff --git a/clang/test/SemaCXX/cxx98-compat.cpp b/clang/test/SemaCXX/cxx98-compat.cpp index 051176bf9c32..56e64d23a6ea 100644 --- a/clang/test/SemaCXX/cxx98-compat.cpp +++ b/clang/test/SemaCXX/cxx98-compat.cpp @@ -34,7 +34,8 @@ namespace TemplateParsing { } void Lambda() { - []{}; // expected-warning {{lambda expressions are incompatible with C++98}} + // FIXME: Enable when lambdas are minimally working. + //[]{}; // FIXME-warning {{lambda expressions are incompatible with C++98}} } int InitList() {