diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8b4f9229c4463..24f296b0f7ce0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -145,6 +145,8 @@ C++20 Feature Support - Fixed a crash with a defaulted spaceship (``<=>``) operator when the class contains a member declaration of vector type. Vector types cannot yet be compared directly, so this causes the operator to be deleted. (#GH137452) +- Implement constant evaluation of lambdas that capture structured bindings. + (#GH145956) C++17 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 60c658a8d8f99..fc6e8843b5ca7 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8951,25 +8951,14 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info, } bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { - const NamedDecl *D = E->getDecl(); - if (isa(D)) - return Success(cast(D)); - if (const VarDecl *VD = dyn_cast(D)) - return VisitVarDecl(E, VD); - if (const BindingDecl *BD = dyn_cast(D)) - return Visit(BD->getBinding()); - return Error(E); -} + const ValueDecl *D = E->getDecl(); -bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { // If we are within a lambda's call operator, check whether the 'VD' referred // 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) && - isa(E) && - cast(E)->refersToEnclosingVariableOrCapture()) { + 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 @@ -8977,13 +8966,24 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { if (Info.checkingPotentialConstantExpression()) return false; - if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(VD)) { + if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(D)) { const auto *MD = cast(Info.CurrentCall->Callee); return HandleLambdaCapture(Info, E, Result, MD, FD, FD->getType()->isReferenceType()); } } + if (isa(D)) + return Success(cast(D)); + if (const VarDecl *VD = dyn_cast(D)) + return VisitVarDecl(E, VD); + if (const BindingDecl *BD = dyn_cast(D)) + return Visit(BD->getBinding()); + return Error(E); +} + +bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { CallStackFrame *Frame = nullptr; unsigned Version = 0; if (VD->hasLocalStorage()) { diff --git a/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp b/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp index 0c20dd9dc58c6..33a6039459484 100644 --- a/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp +++ b/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp @@ -373,3 +373,31 @@ static_assert( } #endif + +#ifndef CPP14_AND_EARLIER +namespace GH145956 { + constexpr int f() { + struct Pair { int first; int second; }; + Pair p = {1, 2}; + auto const& [key, value] = p; + return [&] { return key; }(); +#if __cpp_constexpr < 202002L + // expected-warning@-2 {{captured structured bindings are a C++20 extension}} + // expected-note@-4 {{'key' declared here}} +#endif + } + static_assert(f() == 1); + constexpr auto retlambda() { + struct Pair { int first; int second; }; + Pair p = {1, 2}; + auto const& [key, value] = p; + return [=] { return key; }; +#if __cpp_constexpr < 202002L + // expected-warning@-2 {{captured structured bindings are a C++20 extension}} + // expected-note@-4 {{'key' declared here}} +#endif + } + constexpr auto lambda = retlambda(); + static_assert(lambda() == 1); +} +#endif