Index: include/clang/AST/LambdaCapture.h =================================================================== --- include/clang/AST/LambdaCapture.h +++ include/clang/AST/LambdaCapture.h @@ -97,6 +97,8 @@ !(DeclAndBits.getInt() & Capture_This); } + bool capturesByCopy() const { return DeclAndBits.getInt() & Capture_ByCopy; } + /// \brief Retrieve the declaration of the local variable being /// captured. /// Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7406,6 +7406,8 @@ "returning block that lives on the local stack">; def note_ref_var_local_bind : Note< "binding reference variable %0 here">; +def note_ref_var_lambda_local_bind : Note< + "capturing local variable in lambda %0 here">; // Check for initializing a member variable with the address or a reference to // a constructor parameter. Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -6585,8 +6585,13 @@ if (lhsType->isPointerType() || (!S.getLangOpts().ObjCAutoRefCount && lhsType->isBlockPointerType())) { stackE = EvalAddr(RetValExp, refVars, /*ParentDecl=*/nullptr); + } else if (lhsType->isReferenceType()) { stackE = EvalVal(RetValExp, refVars, /*ParentDecl=*/nullptr); + + } else if (CXXRecordDecl *LambdaDecl = lhsType->getAsCXXRecordDecl()) { + if (LambdaDecl->isLambda()) + stackE = EvalVal(RetValExp, refVars, /*ParentDecl=*/nullptr); } if (!stackE) @@ -6641,8 +6646,12 @@ // show the range of the expression. SourceRange range = (i < e - 1) ? refVars[i + 1]->getSourceRange() : stackE->getSourceRange(); - S.Diag(VD->getLocation(), diag::note_ref_var_local_bind) - << VD->getDeclName() << range; + + unsigned Diag = VD->getType()->isReferenceType() + ? diag::note_ref_var_local_bind + : diag::note_ref_var_lambda_local_bind; + + S.Diag(VD->getLocation(), Diag) << VD->getDeclName() << range; } } @@ -6697,15 +6706,18 @@ if (DR->refersToEnclosingVariableOrCapture()) return nullptr; - if (const VarDecl *V = dyn_cast(DR->getDecl())) + if (const VarDecl *V = dyn_cast(DR->getDecl())) { // If this is a reference variable, follow through to the expression that // it points to. + auto *RD = V->getType()->getAsCXXRecordDecl(); if (V->hasLocalStorage() && - V->getType()->isReferenceType() && V->hasInit()) { + (V->getType()->isReferenceType() || (RD && RD->isLambda())) && + V->hasInit()) { // Add the reference variable to the "trail". refVars.push_back(DR); return EvalAddr(V->getInit(), refVars, ParentDecl); } + } return nullptr; } @@ -6830,7 +6842,7 @@ static const Expr *EvalVal(const Expr *E, SmallVectorImpl &refVars, const Decl *ParentDecl) { - do { + for (;;) { // We should only be called for evaluating non-pointer expressions, or // expressions with a pointer type that are not used as references but // instead @@ -6871,7 +6883,8 @@ return DR; if (V->hasLocalStorage()) { - if (!V->getType()->isReferenceType()) + auto *RD = V->getType()->getAsCXXRecordDecl(); + if (!V->getType()->isReferenceType() && !(RD && RD->isLambda())) return DR; // Reference variable, follow through to the expression that @@ -6958,6 +6971,52 @@ return Result; return E; + case Stmt::CXXConstructExprClass: { + auto *CE = cast(E); + + // We only check this node to find a construction of a C++ lambda. + if (!CE->getConstructor()->getParent()->isLambda()) + return nullptr; + + assert(CE->getNumArgs() == 1 && "Lambda constructor without 1 argument?"); + + E = CE->getArg(0)->IgnoreImplicit(); + break; + } + + case Stmt::LambdaExprClass: { + const LambdaExpr *Lambda = cast(E); + + SmallVector oldRefVars(refVars.begin(), + refVars.end()); + + auto init = Lambda->capture_init_begin(); + auto cap = Lambda->capture_begin(), cap_end = Lambda->capture_end(); + + for (; cap != cap_end; ++cap, ++init) { + if (!cap->capturesVariable()) + continue; + + // There are 2 cases we want to catch here: + // 1. Capturing a local by reference, ie: + // return [&local] {}; + if (!cap->capturesByCopy() && + cap->getCapturedVar()->hasLocalStorage()) { + if (auto *Res = EvalVal(*init, refVars, ParentDecl)) + return Res; + } + // 2. Capturing a pointer to a local through an init-capture. ie: + // return [ptr = &local] {}; + else if (refVars.empty() && (*init)->getType()->isPointerType()) + if (auto *Res = EvalAddr(*init, refVars, ParentDecl)) + return Res; + + refVars = oldRefVars; + } + + return nullptr; + } + default: // Check that we don't return or take the address of a reference to a // temporary. This is only useful in C++. @@ -6967,7 +7026,7 @@ // Everything else: we simply don't reason about them. return nullptr; } - } while (true); + } } void Index: test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp =================================================================== --- test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp +++ test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp @@ -182,14 +182,14 @@ int d[sizeof(a) == sizeof(c) || sizeof(c) == sizeof(b) ? 2 : 1]; f(x, d); f(y, d); - f(z, d); + f(z, d); // expected-warning {{address of stack memory associated with local variable 'z' returned}} decltype(a) A = a; decltype(b) B = b; const int &i = cx.i; }; }; }; - auto M = L(3)(3.5); + auto M = L(3)(3.5); // expected-note{{in instantiation}} M(3.14); } } Index: test/SemaCXX/cxx1y-init-captures.cpp =================================================================== --- test/SemaCXX/cxx1y-init-captures.cpp +++ test/SemaCXX/cxx1y-init-captures.cpp @@ -117,10 +117,11 @@ { // will need to capture x in outer lambda const T x = 10; //expected-note {{declared}} auto L = [z = x](char a) { //expected-note {{begins}} - auto M = [&y = x](T b) { //expected-error {{cannot be implicitly captured}} + auto M = [&y = x](T b) { //expected-error {{cannot be implicitly captured}} expected-note{{capturing}} return y; }; - return M; + // FIXME: We shouldn't emit this warning here. + return M; // expected-warning{{stack memory}} }; } @@ -146,10 +147,11 @@ { // will need to capture x in outer lambda const int x = 10; //expected-note 2{{declared}} auto L = [z = x](char a) { //expected-note 2{{begins}} - auto M = [&y = x](T b) { //expected-error 2{{cannot be implicitly captured}} + auto M = [&y = x](T b) { //expected-error 2{{cannot be implicitly captured}} expected-note{{capturing local}} return y; }; - return M; + // FIXME: We shouldn't emit this warning here + return M; // expected-warning{{stack memory}} }; } { Index: test/SemaCXX/return-lambda-stack-addr.cpp =================================================================== --- test/SemaCXX/return-lambda-stack-addr.cpp +++ test/SemaCXX/return-lambda-stack-addr.cpp @@ -0,0 +1,127 @@ +// RUN: %clang_cc1 -std=c++14 -verify -fsyntax-only %s + +auto basic() { + int local; + + return [&]() { + (void)local; // expected-warning{{address of stack memory associated with local variable 'local' returned}} + }; +} + +auto by_copy() { + int local; + + return [=]() { + (void)local; // no warning + }; +} + +auto nested() { + int local; + + return [=] { + return [&]() { (void)local; }; // no warning + }; +} + +auto nested2() { + int local; + + return [&] { + (void)[=] { + (void)local; // expected-warning{{address of stack memory associated with local variable 'local' returned}} + }; + }; +} + +auto nested3() { + int local; + return [&] { + return [&] { + (void)local; // expected-warning{{address of stack memory associated with local variable 'local' returned}} + }; + }; +} + +struct function { + template + function(T) {} +}; + +function to_function() { + int local; + return [&] { (void)local; }; +} + +auto lambda = [] { + int local; + return [&] { return local; }; // expected-warning{{address of stack memory associated with local variable 'local' returned}} +}; + +auto ref_copy() { + int x; + int &y = x; + + return [=] { (void)y; }; +} + +auto ref_ref() { + int x; + int &y = x; // expected-note{{binding reference variable 'y' here}} + return [&] { (void)y; }; // expected-warning{{address of stack memory associated with local variable 'x' returned}} +} + +auto param(int p) { + return [&] { (void)p; }; // expected-warning{{address of stack memory associated with local variable 'p' returned}} +} + +auto return_var() { + int local; + auto lam = [&local] {}; // expected-note 4 {{capturing local variable in lambda 'lam' here}} + + auto &lam_ref = lam; // expected-note 2 {{binding reference variable 'lam_ref' here}} + + if (0) + return lam; // expected-warning{{address of stack memory associated with local variable 'local' returned}} + else if (0) + return *&lam; // expected-warning{{address of stack memory associated with local variable 'local' returned}} + else if (0) + return lam_ref; // expected-warning{{address of stack memory associated with local variable 'local' returned}} + else + return *&lam_ref; // expected-warning{{address of stack memory associated with local variable 'local' returned}} +} + +auto return_ptr_no_warn() { + int local; + auto lam = [ptr = &local]{}; + return lam; // no warning +} + +auto return_ptr_warn() { + int local; + return [ptr = &local]{}; // expected-warning{{address of stack memory associated with local variable 'local' returned}} +} + +auto mutable_lambda_warn() { + int local; + auto mut = [&local]() mutable {}; // expected-note{{capturing local variable in lambda 'mut' here}} + return mut; // expected-warning{{address of stack memory associated with local variable 'local' returned}} +} + +auto mutable_lambda_warn2() { + int local; + return [cap = &local]() mutable {}; // expected-warning{{address of stack memory associated with local variable 'local' returned}} +} + +auto mutable_lambda_no_warn() { + int local; + auto mut = [cap = &local]() mutable {}; + return mut; // no warning +} + +auto ref_ptr() { + int local; + auto lam = [&local] {}; // expected-note{{capturing local variable in lambda 'lam' here}} + auto *const &lolwhat = &lam; // expected-note{{binding reference variable 'lolwhat' here}} + return *lolwhat; // expected-warning{{address of stack memory associated with local variable 'local' returned}} +}