Skip to content

[clang] Implemeent consteval for captured structured bindings. #147615

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 14 additions & 14 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8951,39 +8951,39 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info,
}

bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
const NamedDecl *D = E->getDecl();
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);
}
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<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(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