Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^
Expand Down
27 changes: 14 additions & 13 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8952,38 +8952,39 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info,

bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
const NamedDecl *D = E->getDecl();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const NamedDecl *D = E->getDecl();
const ValueDecl *D = E->getDecl();

That way we can avoid casts further down

if (isa<FunctionDecl, MSGuidDecl, TemplateParamObjectDecl,
UnnamedGlobalConstantDecl>(D))
return Success(cast<ValueDecl>(D));
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
return VisitVarDecl(E, VD);
if (const BindingDecl *BD = dyn_cast<BindingDecl>(D))
return Visit(BD->getBinding());
return Error(E);
}

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<DeclRefExpr>(E) &&
cast<DeclRefExpr>(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
// (dcl.constexpr C++17).
if (Info.checkingPotentialConstantExpression())
return false;

if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(VD)) {
if (auto *FD =
Info.CurrentCall->LambdaCaptureFields.lookup(cast<ValueDecl>(D))) {
const auto *MD = cast<CXXMethodDecl>(Info.CurrentCall->Callee);
return HandleLambdaCapture(Info, E, Result, MD, FD,
FD->getType()->isReferenceType());
}
}

if (isa<FunctionDecl, MSGuidDecl, TemplateParamObjectDecl,
UnnamedGlobalConstantDecl>(D))
return Success(cast<ValueDecl>(D));
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
return VisitVarDecl(E, VD);
if (const BindingDecl *BD = dyn_cast<BindingDecl>(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()) {
Expand Down
28 changes: 28 additions & 0 deletions clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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