Skip to content

[Clang] Implement P0963R3 "Structured binding declaration as a condition" #130228

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 13 commits into from
Mar 11, 2025
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
95 changes: 48 additions & 47 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1604,53 +1604,54 @@ More information could be found `here <https://clang.llvm.org/docs/Modules.html>
Language Extensions Back-ported to Previous Standards
=====================================================

============================================ ================================ ============= =============
Feature Feature Test Macro Introduced In Backported To
============================================ ================================ ============= =============
variadic templates __cpp_variadic_templates C++11 C++03
Alias templates __cpp_alias_templates C++11 C++03
Non-static data member initializers __cpp_nsdmi C++11 C++03
Range-based ``for`` loop __cpp_range_based_for C++11 C++03
RValue references __cpp_rvalue_references C++11 C++03
Attributes __cpp_attributes C++11 C++03
Lambdas __cpp_lambdas C++11 C++03
Generalized lambda captures __cpp_init_captures C++14 C++03
Generic lambda expressions __cpp_generic_lambdas C++14 C++03
variable templates __cpp_variable_templates C++14 C++03
Binary literals __cpp_binary_literals C++14 C++03
Relaxed constexpr __cpp_constexpr C++14 C++11
Static assert with no message __cpp_static_assert >= 201411L C++17 C++11
Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03
``if constexpr`` __cpp_if_constexpr C++17 C++11
fold expressions __cpp_fold_expressions C++17 C++03
Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03
Attributes on enums __cpp_enumerator_attributes C++17 C++03
Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03
Hexadecimal floating literals __cpp_hex_float C++17 C++03
``inline`` variables __cpp_inline_variables C++17 C++03
Attributes on namespaces __cpp_namespace_attributes C++17 C++11
Structured bindings __cpp_structured_bindings C++17 C++03
template template arguments __cpp_template_template_args C++17 C++03
Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03
``static operator[]`` __cpp_multidimensional_subscript C++20 C++03
Designated initializers __cpp_designated_initializers C++20 C++03
Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03
``using enum`` __cpp_using_enum C++20 C++03
``if consteval`` __cpp_if_consteval C++23 C++20
``static operator()`` __cpp_static_call_operator C++23 C++03
Attributes on Lambda-Expressions C++23 C++11
Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
Pack Indexing __cpp_pack_indexing C++26 C++03
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
Variadic Friends __cpp_variadic_friend C++26 C++03
-------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89
Attributes (N2335) C23 C89
``#embed`` (N3017) C23 C89, C++
============================================ ================================ ============= =============
============================================= ================================ ============= =============
Feature Feature Test Macro Introduced In Backported To
============================================= ================================ ============= =============
variadic templates __cpp_variadic_templates C++11 C++03
Alias templates __cpp_alias_templates C++11 C++03
Non-static data member initializers __cpp_nsdmi C++11 C++03
Range-based ``for`` loop __cpp_range_based_for C++11 C++03
RValue references __cpp_rvalue_references C++11 C++03
Attributes __cpp_attributes C++11 C++03
Lambdas __cpp_lambdas C++11 C++03
Generalized lambda captures __cpp_init_captures C++14 C++03
Generic lambda expressions __cpp_generic_lambdas C++14 C++03
variable templates __cpp_variable_templates C++14 C++03
Binary literals __cpp_binary_literals C++14 C++03
Relaxed constexpr __cpp_constexpr C++14 C++11
Static assert with no message __cpp_static_assert >= 201411L C++17 C++11
Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03
``if constexpr`` __cpp_if_constexpr C++17 C++11
fold expressions __cpp_fold_expressions C++17 C++03
Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03
Attributes on enums __cpp_enumerator_attributes C++17 C++03
Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03
Hexadecimal floating literals __cpp_hex_float C++17 C++03
``inline`` variables __cpp_inline_variables C++17 C++03
Attributes on namespaces __cpp_namespace_attributes C++17 C++11
Structured bindings __cpp_structured_bindings C++17 C++03
template template arguments __cpp_template_template_args C++17 C++03
Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03
``static operator[]`` __cpp_multidimensional_subscript C++20 C++03
Designated initializers __cpp_designated_initializers C++20 C++03
Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03
``using enum`` __cpp_using_enum C++20 C++03
``if consteval`` __cpp_if_consteval C++23 C++20
``static operator()`` __cpp_static_call_operator C++23 C++03
Attributes on Lambda-Expressions C++23 C++11
Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
Structured binding declaration as a condition __cpp_structured_bindings C++26 C++98
Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
Pack Indexing __cpp_pack_indexing C++26 C++03
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
Variadic Friends __cpp_variadic_friend C++26 C++03
--------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89
Attributes (N2335) C23 C89
``#embed`` (N3017) C23 C89, C++
============================================= ================================ ============= =============

Builtin type aliases
====================
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ C++2c Feature Support

- Implemented `P1061R10 Structured Bindings can introduce a Pack <https://wg21.link/P1061R10>`_.

- Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.

C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^

Expand Down
8 changes: 6 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,12 @@ def warn_cxx14_compat_decomp_decl : Warning<
def ext_decomp_decl : ExtWarn<
"decomposition declarations are a C++17 extension">, InGroup<CXX17>;
def ext_decomp_decl_cond : ExtWarn<
"ISO C++17 does not permit structured binding declaration in a condition">,
InGroup<DiagGroup<"binding-in-condition">>;
"structured binding declaration in a condition is a C++2c extenstion">,
InGroup<CXX26>;
def warn_cxx26_decomp_decl_cond : Warning<
"structured binding declaration in a condition is incompatible with "
"C++ standards before C++2c">,
InGroup<CXXPre26Compat>, DefaultIgnore;
def err_decomp_decl_spec : Error<
"decomposition declaration cannot be declared "
"%plural{1:'%1'|:with '%1' specifiers}0">;
Expand Down
37 changes: 28 additions & 9 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5051,7 +5051,7 @@ template <class Emitter> bool Compiler<Emitter>::visitStmt(const Stmt *S) {
case Stmt::CompoundStmtClass:
return visitCompoundStmt(cast<CompoundStmt>(S));
case Stmt::DeclStmtClass:
return visitDeclStmt(cast<DeclStmt>(S));
return visitDeclStmt(cast<DeclStmt>(S), /*EvaluateConditionDecl=*/true);
case Stmt::ReturnStmtClass:
return visitReturnStmt(cast<ReturnStmt>(S));
case Stmt::IfStmtClass:
Expand Down Expand Up @@ -5105,7 +5105,18 @@ bool Compiler<Emitter>::visitCompoundStmt(const CompoundStmt *S) {
}

template <class Emitter>
bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
bool Compiler<Emitter>::maybeEmitDeferredVarInit(const VarDecl *VD) {
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
for (auto *BD : DD->bindings())
if (auto *KD = BD->getHoldingVar(); KD && !this->visitVarDecl(KD))
return false;
}
return true;
}

template <class Emitter>
bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
bool EvaluateConditionDecl) {
for (const auto *D : DS->decls()) {
if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl, BaseUsingDecl,
FunctionDecl, NamespaceAliasDecl, UsingDirectiveDecl>(D))
Expand All @@ -5118,13 +5129,8 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
return false;

// Register decomposition decl holding vars.
if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
for (auto *BD : DD->bindings())
if (auto *KD = BD->getHoldingVar()) {
if (!this->visitVarDecl(KD))
return false;
}
}
if (EvaluateConditionDecl && !this->maybeEmitDeferredVarInit(VD))
return false;
}

return true;
Expand Down Expand Up @@ -5189,6 +5195,9 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
return false;
}

if (!this->maybeEmitDeferredVarInit(IS->getConditionVariable()))
return false;

if (const Stmt *Else = IS->getElse()) {
LabelTy LabelElse = this->getLabel();
LabelTy LabelEnd = this->getLabel();
Expand Down Expand Up @@ -5249,6 +5258,10 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {

if (!this->visitBool(Cond))
return false;

if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
return false;

if (!this->jumpFalse(EndLabel))
return false;

Expand Down Expand Up @@ -5330,6 +5343,9 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
return false;
}

if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
return false;

if (Body && !this->visitStmt(Body))
return false;

Expand Down Expand Up @@ -5452,6 +5468,9 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
if (!this->emitSetLocal(CondT, CondVar, S))
return false;

if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
return false;

CaseMap CaseLabels;
// Create labels and comparison ops for all case statements.
for (const SwitchCase *SC = S->getSwitchCaseList(); SC;
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/ByteCode/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,

// Statements.
bool visitCompoundStmt(const CompoundStmt *S);
bool visitDeclStmt(const DeclStmt *DS);
bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl = false);
bool visitReturnStmt(const ReturnStmt *RS);
bool visitIfStmt(const IfStmt *IS);
bool visitWhileStmt(const WhileStmt *S);
Expand Down Expand Up @@ -386,6 +386,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool compileUnionAssignmentOperator(const CXXMethodDecl *MD);

bool checkLiteralType(const Expr *E);
bool maybeEmitDeferredVarInit(const VarDecl *VD);

protected:
/// Variable to storage mapping.
Expand Down
41 changes: 34 additions & 7 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5218,20 +5218,41 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
return true;
}

static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
bool OK = true;
static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
const DecompositionDecl *DD);

static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
bool EvaluateConditionDecl = false) {
bool OK = true;
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
OK &= EvaluateVarDecl(Info, VD);

if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D))
for (auto *BD : DD->flat_bindings())
if (auto *VD = BD->getHoldingVar())
OK &= EvaluateDecl(Info, VD);
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
EvaluateConditionDecl && DD)
OK &= EvaluateDecompositionDeclInit(Info, DD);

return OK;
}

static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
const DecompositionDecl *DD) {
bool OK = true;
for (auto *BD : DD->flat_bindings())
if (auto *VD = BD->getHoldingVar())
OK &= EvaluateDecl(Info, VD, /*EvaluateConditionDecl=*/true);

return OK;
}

static bool MaybeEvaluateDeferredVarDeclInit(EvalInfo &Info,
const VarDecl *VD) {
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
if (!EvaluateDecompositionDeclInit(Info, DD))
return false;
}
return true;
}

static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) {
assert(E->isValueDependent());
if (Info.noteSideEffect())
Expand All @@ -5251,6 +5272,8 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
return false;
if (!EvaluateAsBooleanCondition(Cond, Result, Info))
return false;
if (!MaybeEvaluateDeferredVarDeclInit(Info, CondDecl))
return false;
return Scope.destroy();
}

Expand Down Expand Up @@ -5335,6 +5358,9 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
if (!EvaluateInteger(SS->getCond(), Value, Info))
return ESR_Failed;

if (!MaybeEvaluateDeferredVarDeclInit(Info, SS->getConditionVariable()))
return ESR_Failed;

if (!CondScope.destroy())
return ESR_Failed;
}
Expand Down Expand Up @@ -5559,7 +5585,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
return ESR_Failed;
// Each declaration initialization is its own full-expression.
FullExpressionRAII Scope(Info);
if (!EvaluateDecl(Info, D) && !Info.noteFailure())
if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) &&
!Info.noteFailure())
return ESR_Failed;
if (!Scope.destroy())
return ESR_Failed;
Expand Down
18 changes: 12 additions & 6 deletions clang/lib/CodeGen/CGDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ using namespace CodeGen;
static_assert(clang::Sema::MaximumAlignment <= llvm::Value::MaximumAlignment,
"Clang max alignment greater than what LLVM supports?");

void CodeGenFunction::EmitDecl(const Decl &D) {
void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
switch (D.getKind()) {
case Decl::BuiltinTemplate:
case Decl::TranslationUnit:
Expand Down Expand Up @@ -152,7 +152,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
return;
case Decl::UsingPack:
for (auto *Using : cast<UsingPackDecl>(D).expansions())
EmitDecl(*Using);
EmitDecl(*Using, /*EvaluateConditionDecl=*/EvaluateConditionDecl);
return;
case Decl::UsingDirective: // using namespace X; [C++]
if (CGDebugInfo *DI = getDebugInfo())
Expand All @@ -164,10 +164,8 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
assert(VD.isLocalVarDecl() &&
"Should not see file-scope variables inside a function!");
EmitVarDecl(VD);
if (auto *DD = dyn_cast<DecompositionDecl>(&VD))
for (auto *B : DD->flat_bindings())
if (auto *HD = B->getHoldingVar())
EmitVarDecl(*HD);
if (EvaluateConditionDecl)
MaybeEmitDeferredVarDeclInit(&VD);

return;
}
Expand Down Expand Up @@ -2057,6 +2055,14 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
/*IsAutoInit=*/false);
}

void CodeGenFunction::MaybeEmitDeferredVarDeclInit(const VarDecl *VD) {
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
for (auto *B : DD->flat_bindings())
if (auto *HD = B->getHoldingVar())
EmitVarDecl(*HD);
}
}

/// Emit an expression as an initializer for an object (variable, field, etc.)
/// at the given location. The expression is not necessarily the normal
/// initializer for the object, and the address is not necessarily
Expand Down
Loading