From 31939e39db2ca3f26868f1f961be0e6c9026a45c Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Fri, 11 Nov 2016 12:36:20 +0000 Subject: [PATCH] Fix for PR28523: unexpected compilation error. Clang emits error message for the following code: ``` template void parallel_loop(F &&f) { f(0); } int main() { int x; parallel_loop([&](auto y) { { x = y; }; }); } ``` $ clang++ --std=gnu++14 clang_test.cc -o clang_test clang_test.cc:9:7: error: reference to local variable 'x' declared in enclosing function 'main' x = y; ^ clang_test.cc:2:48: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()' requested here template void parallel_loop(F &&f) { f(0); } ^ clang_test.cc:6:3: note: in instantiation of function template specialization 'parallel_loop<(lambda at clang_test.cc:6:17)>' requested here parallel_loop([&](auto y) { ^ clang_test.cc:5:7: note: 'x' declared here int x; ^ 1 error generated. Patch fixes this issue. llvm-svn: 286584 --- clang/include/clang/Sema/Sema.h | 6 ++++-- clang/lib/Sema/Sema.cpp | 12 ++++++++++-- clang/lib/Sema/SemaExpr.cpp | 3 ++- clang/lib/Sema/SemaExprCXX.cpp | 16 +++++++++++++--- clang/lib/Sema/SemaLambda.cpp | 11 +++++++---- clang/test/CodeGenCXX/PR28523.cpp | 19 +++++++++++++++++++ 6 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 clang/test/CodeGenCXX/PR28523.cpp diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 23ec47d..f18a6ee 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1228,8 +1228,10 @@ public: /// \brief Retrieve the current block, if any. sema::BlockScopeInfo *getCurBlock(); - /// \brief Retrieve the current lambda scope info, if any. - sema::LambdaScopeInfo *getCurLambda(); + /// Retrieve the current lambda scope info, if any. + /// \param IgnoreCapturedRegions true if should find the top-most lambda scope + /// info ignoring all inner captured regions scope infos. + sema::LambdaScopeInfo *getCurLambda(bool IgnoreCapturedRegions = false); /// \brief Retrieve the current generic lambda info, if any. sema::LambdaScopeInfo *getCurGenericLambda(); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index c2622ec..250e31b 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1201,11 +1201,19 @@ BlockScopeInfo *Sema::getCurBlock() { return CurBSI; } -LambdaScopeInfo *Sema::getCurLambda() { +LambdaScopeInfo *Sema::getCurLambda(bool IgnoreCapturedRegions) { if (FunctionScopes.empty()) return nullptr; - auto CurLSI = dyn_cast(FunctionScopes.back()); + auto I = FunctionScopes.rbegin(); + if (IgnoreCapturedRegions) { + auto E = FunctionScopes.rend(); + while (I != E && isa(*I)) + ++I; + if (I == E) + return nullptr; + } + auto *CurLSI = dyn_cast(*I); if (CurLSI && CurLSI->Lambda && !CurLSI->Lambda->Encloses(CurContext)) { // We have switched contexts due to template instantiation. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 114dba3..fae8bf0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -14079,7 +14079,8 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc, (SemaRef.CurContext != Var->getDeclContext() && Var->getDeclContext()->isFunctionOrMethod() && Var->hasLocalStorage()); if (RefersToEnclosingScope) { - if (LambdaScopeInfo *const LSI = SemaRef.getCurLambda()) { + if (LambdaScopeInfo *const LSI = + SemaRef.getCurLambda(/*IgnoreCapturedRegions=*/true)) { // If a variable could potentially be odr-used, defer marking it so // until we finish analyzing the full expression for any // lvalue-to-rvalue diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 0dab27a..634954c 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -6879,8 +6879,14 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures( assert(!S.isUnevaluatedContext()); assert(S.CurContext->isDependentContext()); - assert(CurrentLSI->CallOperator == S.CurContext && +#ifndef NDEBUG + DeclContext *DC = S.CurContext; + while (DC && isa(DC)) + DC = DC->getParent(); + assert( + CurrentLSI->CallOperator == DC && "The current call operator must be synchronized with Sema's CurContext"); +#endif // NDEBUG const bool IsFullExprInstantiationDependent = FE->isInstantiationDependent(); @@ -7346,7 +7352,8 @@ ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC, // and then the full-expression +n + ({ 0; }); ends, but it's too late // for us to see that we need to capture n after all. - LambdaScopeInfo *const CurrentLSI = getCurLambda(); + LambdaScopeInfo *const CurrentLSI = + getCurLambda(/*IgnoreCapturedRegions=*/true); // FIXME: PR 17877 showed that getCurLambda() can return a valid pointer // even if CurContext is not a lambda call operator. Refer to that Bug Report // for an example of the code that might cause this asynchrony. @@ -7361,7 +7368,10 @@ ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC, // constructor/destructor. // - Teach the handful of places that iterate over FunctionScopes to // stop at the outermost enclosing lexical scope." - const bool IsInLambdaDeclContext = isLambdaCallOperator(CurContext); + DeclContext *DC = CurContext; + while (DC && isa(DC)) + DC = DC->getParent(); + const bool IsInLambdaDeclContext = isLambdaCallOperator(DC); if (IsInLambdaDeclContext && CurrentLSI && CurrentLSI->hasPotentialCaptures() && !FullExpr.isInvalid()) CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(FE, CurrentLSI, diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index da4ba8b..fb3ac07 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -66,17 +66,20 @@ getStackIndexOfNearestEnclosingCaptureReadyLambda( // Label failure to capture. const Optional NoLambdaIsCaptureReady; + // Ignore all inner captured regions. + unsigned CurScopeIndex = FunctionScopes.size() - 1; + while (CurScopeIndex > 0 && isa( + FunctionScopes[CurScopeIndex])) + --CurScopeIndex; assert( - isa( - FunctionScopes[FunctionScopes.size() - 1]) && + isa(FunctionScopes[CurScopeIndex]) && "The function on the top of sema's function-info stack must be a lambda"); - + // If VarToCapture is null, we are attempting to capture 'this'. const bool IsCapturingThis = !VarToCapture; const bool IsCapturingVariable = !IsCapturingThis; // Start with the current lambda at the top of the stack (highest index). - unsigned CurScopeIndex = FunctionScopes.size() - 1; DeclContext *EnclosingDC = cast(FunctionScopes[CurScopeIndex])->CallOperator; diff --git a/clang/test/CodeGenCXX/PR28523.cpp b/clang/test/CodeGenCXX/PR28523.cpp new file mode 100644 index 0000000..4c3a81c --- /dev/null +++ b/clang/test/CodeGenCXX/PR28523.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -std=c++14 -verify -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck %s +// expected-no-diagnostics + +template void parallel_loop(F &&f) { f(0); } + +//CHECK-LABEL: @main +int main() { +// CHECK: [[X_ADDR:%.+]] = alloca i32, + int x; +// CHECK: getelementptr inbounds +// CHECK: store i32* [[X_ADDR]], i32** % +// CHECK: call + parallel_loop([&](auto y) { +#pragma clang __debug captured + { + x = y; + }; + }); +} -- 2.7.4