From: Erik Pilkington Date: Thu, 5 Apr 2018 00:12:05 +0000 (+0000) Subject: [AST] Don't track lambda captures when checking a potential constant expression. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6137fb61ced60b179afcbf610bfb230af6232ed8;p=clang [AST] Don't track lambda captures when checking a potential constant expression. Fixes PR36054. Differential revision: https://reviews.llvm.org/D45194 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@329244 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 84ac4daeef..6de55f10dc 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -4309,9 +4309,15 @@ static bool HandleFunctionCall(SourceLocation CallLoc, This->moveInto(Result); return true; } else if (MD && isLambdaCallOperator(MD)) { - // We're in a lambda; determine the lambda capture field maps. - MD->getParent()->getCaptureFields(Frame.LambdaCaptureFields, - Frame.LambdaThisCaptureField); + // We're in a lambda; determine the lambda capture field maps unless we're + // just constexpr checking a lambda's call operator. constexpr checking is + // done before the captures have been added to the closure object (unless + // we're inferring constexpr-ness), so we don't have access to them in this + // case. But since we don't need the captures to constexpr check, we can + // just ignore them. + if (!Info.checkingPotentialConstantExpression()) + MD->getParent()->getCaptureFields(Frame.LambdaCaptureFields, + Frame.LambdaThisCaptureField); } StmtResult Ret = {Result, ResultSlot}; @@ -5201,10 +5207,17 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { // to within 'E' actually represents a lambda-capture that maps to a // data-member/field within the closure object, and if so, evaluate to the // field or what the field refers to. - if (Info.CurrentCall && isLambdaCallOperator(Info.CurrentCall->Callee)) { + if (Info.CurrentCall && isLambdaCallOperator(Info.CurrentCall->Callee) && + isa(E) && + cast(E)->refersToEnclosingVariableOrCapture()) { + // We don't always have a complete capture-map when checking or inferring if + // the function call operator meets the requirements of a constexpr function + // - but we don't need to evaluate the captures to determine constexprness + // (dcl.constexpr C++17). + if (Info.checkingPotentialConstantExpression()) + return false; + if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(VD)) { - if (Info.checkingPotentialConstantExpression()) - return false; // Start with 'Result' referring to the complete closure object... Result = *Info.CurrentCall->This; // ... then update it to refer to the field of the closure object diff --git a/test/SemaCXX/cxx1z-constexpr-lambdas.cpp b/test/SemaCXX/cxx1z-constexpr-lambdas.cpp index 4a98a1b06c..3d5509eedf 100644 --- a/test/SemaCXX/cxx1z-constexpr-lambdas.cpp +++ b/test/SemaCXX/cxx1z-constexpr-lambdas.cpp @@ -270,4 +270,37 @@ namespace ns1_test_lvalue_type { } // end ns test_lambda_is_cce +namespace PR36054 { +constexpr int fn() { + int Capture = 42; + return [=]() constexpr { return Capture; }(); +} + +static_assert(fn() == 42, ""); + +template +constexpr int tfn() { + int Capture = 42; + return [=]() constexpr { return Capture; }(); +} + +static_assert(tfn() == 42, ""); + +constexpr int gfn() { + int Capture = 42; + return [=](auto P) constexpr { return Capture + P; }(58); +} + +static_assert(gfn() == 100, ""); + +constexpr bool OtherCaptures() { + int Capture = 42; + constexpr auto Outer = [](auto P) constexpr { return 42 + P; }; + auto Inner = [&](auto O) constexpr { return O(58) + Capture; }; + return Inner(Outer) == 142; +} + +static_assert(OtherCaptures(), ""); +} // namespace PR36054 + #endif // ndef CPP14_AND_EARLIER