From: Douglas Gregor Date: Thu, 9 Feb 2012 00:47:04 +0000 (+0000) Subject: Various interrelated cleanups for lambdas: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=503384f731b5abcbf870b0a5224eb920e631db0a;p=clang Various interrelated cleanups for lambdas: - Complete the lambda class when we finish the lambda expression (previously, it was left in the "being completed" state) - Actually return the LambdaExpr object and bind to the resulting temporary when needed. - Detect when cleanups are needed while capturing a variable into a lambda (e.g., due to default arguments in the copy constructor), and make sure those cleanups apply for the whole of the lambda expression. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150123 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index d1e47b2b2f..91f468b3e5 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -290,15 +290,20 @@ public: /// explicit captures. unsigned NumExplicitCaptures; + /// \brief Whether this is a mutable lambda. bool Mutable; /// \brief Whether the (empty) parameter list is explicit. bool ExplicitParams; + /// \brief Whether any of the capture expressions requires cleanups. + bool ExprNeedsCleanups; + LambdaScopeInfo(DiagnosticsEngine &Diag, CXXRecordDecl *Lambda, CXXMethodDecl *CallOperator) : CapturingScopeInfo(Diag, ImpCap_None), Lambda(Lambda), - CallOperator(CallOperator), NumExplicitCaptures(0), Mutable(false) + CallOperator(CallOperator), NumExplicitCaptures(0), Mutable(false), + ExprNeedsCleanups(false) { Kind = SK_Lambda; } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 378930a0e5..69731e45ef 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -4185,7 +4185,10 @@ void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC) { BinaryOperator *BO = dyn_cast(E); bool IsLogicalOperator = BO && BO->isLogicalOp(); for (Stmt::child_range I = E->children(); I; ++I) { - Expr *ChildExpr = cast(*I); + Expr *ChildExpr = dyn_cast(*I); + if (!ChildExpr) + continue; + if (IsLogicalOperator && isa(ChildExpr->IgnoreParenImpCasts())) // Ignore checking string literals that are in logical operators. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index accf85171e..aed6eaf4e3 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9652,7 +9652,8 @@ void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, Expr *copyExpr = 0; const RecordType *rtype; if (isLambda) { - CXXRecordDecl *Lambda = cast(CSI)->Lambda; + LambdaScopeInfo *LSI = cast(CSI); + CXXRecordDecl *Lambda = LSI->Lambda; QualType FieldType; if (byRef) { // C++11 [expr.prim.lambda]p15: @@ -9704,6 +9705,11 @@ void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, // // FIXME: Introduce an initialization entity for lambda captures. // FIXME: Totally broken for arrays. + + // Introduce a new evaluation context for the initialization, so that + // temporaries introduced as part of the capture + PushExpressionEvaluationContext(PotentiallyEvaluated); + Expr *Ref = new (Context) DeclRefExpr(var, type.getNonReferenceType(), VK_LValue, loc); InitializedEntity InitEntity @@ -9717,6 +9723,17 @@ void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, if (!Result.isInvalid()) copyExpr = Result.take(); } + + // If this initialization requires any cleanups (e.g., due to a + // default argument to a copy constructor), note that for the + // lambda. + if (ExprNeedsCleanups) + LSI->ExprNeedsCleanups = true; + + // Exit the expression evaluation context used for the capture. + CleanupVarDeclMarking(); + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); } else if (!byRef && getLangOptions().CPlusPlus && (rtype = type.getNonReferenceType()->getAs())) { // The capture logic needs the destructor, so make sure we mark it. diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 3e9e210244..b90f5e3d63 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -103,13 +103,11 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, Method->setAccess(AS_public); Class->addDecl(Method); Method->setLexicalDeclContext(DC); // FIXME: Minor hack. - + + // Attributes on the lambda apply to the method. ProcessDeclAttributes(CurScope, Method, ParamInfo); - // Enter a new evaluation context to insulate the block from any - // cleanups from the enclosing full-expression. - PushExpressionEvaluationContext(PotentiallyEvaluated); - + // Introduce the function call operator as the current declaration context. PushDeclContext(CurScope, Method); // Introduce the lambda scope. @@ -255,6 +253,9 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, // FIXME: Check return type is complete, !isObjCObjectType + // Enter a new evaluation context to insulate the block from any + // cleanups from the enclosing full-expression. + PushExpressionEvaluationContext(PotentiallyEvaluated); } void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope) { @@ -273,8 +274,6 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, DiscardCleanupsInEvaluationContext(); PopExpressionEvaluationContext(); - // FIXME: End-of-lambda checking - // Collect information from the lambda scope. llvm::SmallVector Captures; llvm::SmallVector CaptureInits; @@ -282,11 +281,13 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, CXXRecordDecl *Class; SourceRange IntroducerRange; bool ExplicitParams; + bool LambdaExprNeedsCleanups; { LambdaScopeInfo *LSI = getCurLambda(); Class = LSI->Lambda; IntroducerRange = LSI->IntroducerRange; ExplicitParams = LSI->ExplicitParams; + LambdaExprNeedsCleanups = LSI->ExprNeedsCleanups; // Translate captures. for (unsigned I = 0, N = LSI->Captures.size(); I != N; ++I) { @@ -331,16 +332,25 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, break; } + // Finalize the lambda class. + SmallVector Fields(Class->field_begin(), Class->field_end()); + ActOnFields(0, Class->getLocation(), Class, Fields, + SourceLocation(), SourceLocation(), 0); + CheckCompletedCXXClass(Class); + // C++ [expr.prim.lambda]p7: // The lambda-expression's compound-statement yields the // function-body (8.4) of the function call operator [...]. ActOnFinishFunctionBody(LSI->CallOperator, Body, /*IsInstantation=*/false); } + if (LambdaExprNeedsCleanups) + ExprNeedsCleanups = true; + Expr *Lambda = LambdaExpr::Create(Context, Class, IntroducerRange, CaptureDefault, Captures, ExplicitParams, CaptureInits, Body->getLocEnd()); - (void)Lambda; Diag(StartLoc, diag::err_lambda_unsupported); - return ExprError(); + + return MaybeBindToTemporary(Lambda); } diff --git a/test/CXX/class/class.local/p1-0x.cpp b/test/CXX/class/class.local/p1-0x.cpp index 8916295825..b8e1f68000 100644 --- a/test/CXX/class/class.local/p1-0x.cpp +++ b/test/CXX/class/class.local/p1-0x.cpp @@ -7,12 +7,12 @@ void f() { int& x2 = x; // expected-error{{reference to local variable 'x' declared in enclosing function 'f'}} int cc = c; }; - []() mutable { // expected-error {{not supported yet}} + (void)[]() mutable { // expected-error {{not supported yet}} int x = 3; // expected-note{{'x' declared here}} struct C { int& x2 = x; // expected-error{{reference to local variable 'x' declared in enclosing lambda expression}} }; - } + }; C(); } diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p10.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p10.cpp index 497307ff75..6f09c53e57 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p10.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p10.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -std=c++11 %s -verify -int GlobalVar; // expected-note 2{{declared here}} +int GlobalVar; // expected-note {{declared here}} namespace N { int AmbiguousVar; // expected-note {{candidate}} @@ -16,9 +16,11 @@ class X0 { virtual X0& Overload(float); void explicit_capture() { - [&Overload] () {}; // expected-error {{does not name a variable}} expected-error {{not supported yet}} - [&GlobalVar] () {}; // expected-error {{does not have automatic storage duration}} expected-error {{not supported yet}} - [&AmbiguousVar] () {} // expected-error {{reference to 'AmbiguousVar' is ambiguous}} expected-error {{not supported yet}} - [&Globalvar] () {}; // expected-error {{use of undeclared identifier 'Globalvar'; did you mean 'GlobalVar}} + int variable; // expected-note {{declared here}} + (void)[&Overload] () {}; // expected-error {{does not name a variable}} expected-error {{not supported yet}} + (void)[&GlobalVar] () {}; // expected-error {{does not have automatic storage duration}} expected-error {{not supported yet}} + (void)[&AmbiguousVar] () {}; // expected-error {{reference to 'AmbiguousVar' is ambiguous}} expected-error {{not supported yet}} + (void)[&Variable] () {}; // expected-error {{use of undeclared identifier 'Variable'; did you mean 'variable'}} \ + // expected-error{{lambda expressions are not supported yet}} } }; diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp index 0bbb9ae744..4c876d7480 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp @@ -6,10 +6,26 @@ class NonCopyable { void capture_by_copy(NonCopyable nc, NonCopyable &ncr) { // FIXME: error messages should talk about capture - [nc] { }; // expected-error{{field of type 'NonCopyable' has private copy constructor}} \ + (void)[nc] { }; // expected-error{{field of type 'NonCopyable' has private copy constructor}} \ // expected-error{{lambda expressions are not supported yet}} - [ncr] { }; // expected-error{{field of type 'NonCopyable' has private copy constructor}} \ + (void)[ncr] { }; // expected-error{{field of type 'NonCopyable' has private copy constructor}} \ // expected-error{{lambda expressions are not supported yet}} } +struct NonTrivial { + NonTrivial(); + NonTrivial(const NonTrivial &); + ~NonTrivial(); +}; + +struct CopyCtorDefault { + CopyCtorDefault(const CopyCtorDefault&, NonTrivial nt = NonTrivial()); + + void foo() const; +}; + +void capture_with_default_args(CopyCtorDefault cct) { + (void)[=] () -> void { cct.foo(); }; // expected-error{{lambda expressions are not supported yet}} +} + // FIXME: arrays! diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p15.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p15.cpp index 7264fa18d0..e17d8b5101 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p15.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p15.cpp @@ -5,6 +5,6 @@ class NonCopyable { }; void capture_by_ref(NonCopyable nc, NonCopyable &ncr) { - [&nc] () -> void {}; // expected-error{{lambda expressions are not supported yet}} - [&ncr] () -> void {}; // expected-error{{lambda expressions are not supported yet}} + (void)[&nc] () -> void {}; // expected-error{{lambda expressions are not supported yet}} + (void)[&ncr] () -> void {}; // expected-error{{lambda expressions are not supported yet}} } diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp index a6fca9b2c4..93ba6f61c7 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++11 %s -verify int test_default_args() { - [](int i = 5, // expected-error{{default arguments can only be specified for parameters in a function declaration}} \ + (void)[](int i = 5, // expected-error{{default arguments can only be specified for parameters in a function declaration}} \ // expected-error{{lambda expressions are not supported yet}} int j = 17) {}; // expected-error{{default arguments can only be specified for parameters in a function declaration}} } diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p7.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p7.cpp index 3c5ac220a1..e816426cbf 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p7.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p7.cpp @@ -2,7 +2,7 @@ // Check that analysis-based warnings work in lambda bodies. void analysis_based_warnings() { - []() -> int { }; // expected-warning{{control reaches end of non-void function}} \ + (void)[]() -> int { }; // expected-warning{{control reaches end of non-void function}} \ // expected-error{{lambda expressions are not supported yet}} } @@ -12,32 +12,32 @@ int &check_const_int(int&); float &check_const_int(const int&); void test_capture_constness(int i, const int ic) { - [i,ic] ()->void { // expected-error{{lambda expressions are not supported yet}} + (void)[i,ic] ()->void { // expected-error{{lambda expressions are not supported yet}} float &fr1 = check_const_int(i); float &fr2 = check_const_int(ic); }; - [=] ()->void { // expected-error{{lambda expressions are not supported yet}} + (void)[=] ()->void { // expected-error{{lambda expressions are not supported yet}} float &fr1 = check_const_int(i); float &fr2 = check_const_int(ic); }; - [i,ic] () mutable ->void { // expected-error{{lambda expressions are not supported yet}} + (void)[i,ic] () mutable ->void { // expected-error{{lambda expressions are not supported yet}} int &ir = check_const_int(i); float &fr = check_const_int(ic); }; - [=] () mutable ->void { // expected-error{{lambda expressions are not supported yet}} + (void)[=] () mutable ->void { // expected-error{{lambda expressions are not supported yet}} int &ir = check_const_int(i); float &fr = check_const_int(ic); }; - [&i,&ic] ()->void { // expected-error{{lambda expressions are not supported yet}} + (void)[&i,&ic] ()->void { // expected-error{{lambda expressions are not supported yet}} int &ir = check_const_int(i); float &fr = check_const_int(ic); }; - [&] ()->void { // expected-error{{lambda expressions are not supported yet}} + (void)[&] ()->void { // expected-error{{lambda expressions are not supported yet}} int &ir = check_const_int(i); float &fr = check_const_int(ic); }; diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p8.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p8.cpp index 4bd5760bc4..53d2c75772 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p8.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p8.cpp @@ -4,13 +4,13 @@ class X0 { void explicit_capture() { int foo; - [foo, foo] () {}; // expected-error {{'foo' can appear only once}} expected-error {{not supported yet}} - [this, this] () {}; // expected-error {{'this' can appear only once}} expected-error {{not supported yet}} - [=, foo] () {}; // expected-error {{'&' must precede a capture when}} expected-error {{not supported yet}} - [=, &foo] () {}; // expected-error {{not supported yet}} - [=, this] () {}; // expected-error {{'this' cannot appear}} expected-error {{not supported yet}} - [&, foo] () {}; // expected-error {{not supported yet}} - [&, &foo] () {}; // expected-error {{'&' cannot precede a capture when}} expected-error {{not supported yet}} - [&, this] () {}; // expected-error {{not supported yet}} + (void)[foo, foo] () {}; // expected-error {{'foo' can appear only once}} expected-error {{not supported yet}} + (void)[this, this] () {}; // expected-error {{'this' can appear only once}} expected-error {{not supported yet}} + (void)[=, foo] () {}; // expected-error {{'&' must precede a capture when}} expected-error {{not supported yet}} + (void)[=, &foo] () {}; // expected-error {{not supported yet}} + (void)[=, this] () {}; // expected-error {{'this' cannot appear}} expected-error {{not supported yet}} + (void)[&, foo] () {}; // expected-error {{not supported yet}} + (void)[&, &foo] () {}; // expected-error {{'&' cannot precede a capture when}} expected-error {{not supported yet}} + (void)[&, this] () {}; // expected-error {{not supported yet}} } }; diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index 46c74c3c74..a414fc98e7 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify -fblocks %s +// RUN: %clang_cc1 -std=c++0x -Wno-unused-value -fsyntax-only -verify -fblocks %s namespace std { class type_info; };