Skip to content

Commit aa27d4e

Browse files
[clang] Implement consteval for captured structured bindings. (#147615)
127bf44 implemented most of the infrastructure for capturing structured bindings in lambdas, but missed one piece: constant evaluation of such lambdas. Refactor the code to handle this case. Fixes #145956.
1 parent 889ac87 commit aa27d4e

File tree

3 files changed

+44
-14
lines changed

3 files changed

+44
-14
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ C++20 Feature Support
145145
- Fixed a crash with a defaulted spaceship (``<=>``) operator when the class
146146
contains a member declaration of vector type. Vector types cannot yet be
147147
compared directly, so this causes the operator to be deleted. (#GH137452)
148+
- Implement constant evaluation of lambdas that capture structured bindings.
149+
(#GH145956)
148150

149151
C++17 Feature Support
150152
^^^^^^^^^^^^^^^^^^^^^

clang/lib/AST/ExprConstant.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8951,39 +8951,39 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info,
89518951
}
89528952

89538953
bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
8954-
const NamedDecl *D = E->getDecl();
8955-
if (isa<FunctionDecl, MSGuidDecl, TemplateParamObjectDecl,
8956-
UnnamedGlobalConstantDecl>(D))
8957-
return Success(cast<ValueDecl>(D));
8958-
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
8959-
return VisitVarDecl(E, VD);
8960-
if (const BindingDecl *BD = dyn_cast<BindingDecl>(D))
8961-
return Visit(BD->getBinding());
8962-
return Error(E);
8963-
}
8954+
const ValueDecl *D = E->getDecl();
89648955

8965-
bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
89668956
// If we are within a lambda's call operator, check whether the 'VD' referred
89678957
// to within 'E' actually represents a lambda-capture that maps to a
89688958
// data-member/field within the closure object, and if so, evaluate to the
89698959
// field or what the field refers to.
89708960
if (Info.CurrentCall && isLambdaCallOperator(Info.CurrentCall->Callee) &&
8971-
isa<DeclRefExpr>(E) &&
8972-
cast<DeclRefExpr>(E)->refersToEnclosingVariableOrCapture()) {
8961+
E->refersToEnclosingVariableOrCapture()) {
89738962
// We don't always have a complete capture-map when checking or inferring if
89748963
// the function call operator meets the requirements of a constexpr function
89758964
// - but we don't need to evaluate the captures to determine constexprness
89768965
// (dcl.constexpr C++17).
89778966
if (Info.checkingPotentialConstantExpression())
89788967
return false;
89798968

8980-
if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(VD)) {
8969+
if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(D)) {
89818970
const auto *MD = cast<CXXMethodDecl>(Info.CurrentCall->Callee);
89828971
return HandleLambdaCapture(Info, E, Result, MD, FD,
89838972
FD->getType()->isReferenceType());
89848973
}
89858974
}
89868975

8976+
if (isa<FunctionDecl, MSGuidDecl, TemplateParamObjectDecl,
8977+
UnnamedGlobalConstantDecl>(D))
8978+
return Success(cast<ValueDecl>(D));
8979+
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
8980+
return VisitVarDecl(E, VD);
8981+
if (const BindingDecl *BD = dyn_cast<BindingDecl>(D))
8982+
return Visit(BD->getBinding());
8983+
return Error(E);
8984+
}
8985+
8986+
bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
89878987
CallStackFrame *Frame = nullptr;
89888988
unsigned Version = 0;
89898989
if (VD->hasLocalStorage()) {

clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,3 +373,31 @@ static_assert(
373373
}
374374

375375
#endif
376+
377+
#ifndef CPP14_AND_EARLIER
378+
namespace GH145956 {
379+
constexpr int f() {
380+
struct Pair { int first; int second; };
381+
Pair p = {1, 2};
382+
auto const& [key, value] = p;
383+
return [&] { return key; }();
384+
#if __cpp_constexpr < 202002L
385+
// expected-warning@-2 {{captured structured bindings are a C++20 extension}}
386+
// expected-note@-4 {{'key' declared here}}
387+
#endif
388+
}
389+
static_assert(f() == 1);
390+
constexpr auto retlambda() {
391+
struct Pair { int first; int second; };
392+
Pair p = {1, 2};
393+
auto const& [key, value] = p;
394+
return [=] { return key; };
395+
#if __cpp_constexpr < 202002L
396+
// expected-warning@-2 {{captured structured bindings are a C++20 extension}}
397+
// expected-note@-4 {{'key' declared here}}
398+
#endif
399+
}
400+
constexpr auto lambda = retlambda();
401+
static_assert(lambda() == 1);
402+
}
403+
#endif

0 commit comments

Comments
 (0)