Skip to content

Commit f421875

Browse files
authored
[Clang] Implement P0963R3 "Structured binding declaration as a condition" (#130228)
This implements the R2 semantics of P0963. The R1 semantics, as outlined in the paper, were introduced in Clang 6. In addition to that, the paper proposes swapping the evaluation order of condition expressions and the initialization of binding declarations (i.e. std::tuple-like decompositions).
1 parent f120b0d commit f421875

File tree

17 files changed

+409
-112
lines changed

17 files changed

+409
-112
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,53 +1604,54 @@ More information could be found `here <https://clang.llvm.org/docs/Modules.html>
16041604
Language Extensions Back-ported to Previous Standards
16051605
=====================================================
16061606

1607-
============================================ ================================ ============= =============
1608-
Feature Feature Test Macro Introduced In Backported To
1609-
============================================ ================================ ============= =============
1610-
variadic templates __cpp_variadic_templates C++11 C++03
1611-
Alias templates __cpp_alias_templates C++11 C++03
1612-
Non-static data member initializers __cpp_nsdmi C++11 C++03
1613-
Range-based ``for`` loop __cpp_range_based_for C++11 C++03
1614-
RValue references __cpp_rvalue_references C++11 C++03
1615-
Attributes __cpp_attributes C++11 C++03
1616-
Lambdas __cpp_lambdas C++11 C++03
1617-
Generalized lambda captures __cpp_init_captures C++14 C++03
1618-
Generic lambda expressions __cpp_generic_lambdas C++14 C++03
1619-
variable templates __cpp_variable_templates C++14 C++03
1620-
Binary literals __cpp_binary_literals C++14 C++03
1621-
Relaxed constexpr __cpp_constexpr C++14 C++11
1622-
Static assert with no message __cpp_static_assert >= 201411L C++17 C++11
1623-
Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03
1624-
``if constexpr`` __cpp_if_constexpr C++17 C++11
1625-
fold expressions __cpp_fold_expressions C++17 C++03
1626-
Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03
1627-
Attributes on enums __cpp_enumerator_attributes C++17 C++03
1628-
Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03
1629-
Hexadecimal floating literals __cpp_hex_float C++17 C++03
1630-
``inline`` variables __cpp_inline_variables C++17 C++03
1631-
Attributes on namespaces __cpp_namespace_attributes C++17 C++11
1632-
Structured bindings __cpp_structured_bindings C++17 C++03
1633-
template template arguments __cpp_template_template_args C++17 C++03
1634-
Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03
1635-
``static operator[]`` __cpp_multidimensional_subscript C++20 C++03
1636-
Designated initializers __cpp_designated_initializers C++20 C++03
1637-
Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03
1638-
``using enum`` __cpp_using_enum C++20 C++03
1639-
``if consteval`` __cpp_if_consteval C++23 C++20
1640-
``static operator()`` __cpp_static_call_operator C++23 C++03
1641-
Attributes on Lambda-Expressions C++23 C++11
1642-
Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
1643-
Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
1644-
Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
1645-
Pack Indexing __cpp_pack_indexing C++26 C++03
1646-
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
1647-
Variadic Friends __cpp_variadic_friend C++26 C++03
1648-
-------------------------------------------- -------------------------------- ------------- -------------
1649-
Designated initializers (N494) C99 C89
1650-
Array & element qualification (N2607) C23 C89
1651-
Attributes (N2335) C23 C89
1652-
``#embed`` (N3017) C23 C89, C++
1653-
============================================ ================================ ============= =============
1607+
============================================= ================================ ============= =============
1608+
Feature Feature Test Macro Introduced In Backported To
1609+
============================================= ================================ ============= =============
1610+
variadic templates __cpp_variadic_templates C++11 C++03
1611+
Alias templates __cpp_alias_templates C++11 C++03
1612+
Non-static data member initializers __cpp_nsdmi C++11 C++03
1613+
Range-based ``for`` loop __cpp_range_based_for C++11 C++03
1614+
RValue references __cpp_rvalue_references C++11 C++03
1615+
Attributes __cpp_attributes C++11 C++03
1616+
Lambdas __cpp_lambdas C++11 C++03
1617+
Generalized lambda captures __cpp_init_captures C++14 C++03
1618+
Generic lambda expressions __cpp_generic_lambdas C++14 C++03
1619+
variable templates __cpp_variable_templates C++14 C++03
1620+
Binary literals __cpp_binary_literals C++14 C++03
1621+
Relaxed constexpr __cpp_constexpr C++14 C++11
1622+
Static assert with no message __cpp_static_assert >= 201411L C++17 C++11
1623+
Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03
1624+
``if constexpr`` __cpp_if_constexpr C++17 C++11
1625+
fold expressions __cpp_fold_expressions C++17 C++03
1626+
Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03
1627+
Attributes on enums __cpp_enumerator_attributes C++17 C++03
1628+
Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03
1629+
Hexadecimal floating literals __cpp_hex_float C++17 C++03
1630+
``inline`` variables __cpp_inline_variables C++17 C++03
1631+
Attributes on namespaces __cpp_namespace_attributes C++17 C++11
1632+
Structured bindings __cpp_structured_bindings C++17 C++03
1633+
template template arguments __cpp_template_template_args C++17 C++03
1634+
Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03
1635+
``static operator[]`` __cpp_multidimensional_subscript C++20 C++03
1636+
Designated initializers __cpp_designated_initializers C++20 C++03
1637+
Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03
1638+
``using enum`` __cpp_using_enum C++20 C++03
1639+
``if consteval`` __cpp_if_consteval C++23 C++20
1640+
``static operator()`` __cpp_static_call_operator C++23 C++03
1641+
Attributes on Lambda-Expressions C++23 C++11
1642+
Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
1643+
Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
1644+
Structured binding declaration as a condition __cpp_structured_bindings C++26 C++98
1645+
Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
1646+
Pack Indexing __cpp_pack_indexing C++26 C++03
1647+
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
1648+
Variadic Friends __cpp_variadic_friend C++26 C++03
1649+
--------------------------------------------- -------------------------------- ------------- -------------
1650+
Designated initializers (N494) C99 C89
1651+
Array & element qualification (N2607) C23 C89
1652+
Attributes (N2335) C23 C89
1653+
``#embed`` (N3017) C23 C89, C++
1654+
============================================= ================================ ============= =============
16541655

16551656
Builtin type aliases
16561657
====================

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ C++2c Feature Support
7979

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

82+
- Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.
83+
8284
C++23 Feature Support
8385
^^^^^^^^^^^^^^^^^^^^^
8486

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,12 @@ def warn_cxx14_compat_decomp_decl : Warning<
529529
def ext_decomp_decl : ExtWarn<
530530
"decomposition declarations are a C++17 extension">, InGroup<CXX17>;
531531
def ext_decomp_decl_cond : ExtWarn<
532-
"ISO C++17 does not permit structured binding declaration in a condition">,
533-
InGroup<DiagGroup<"binding-in-condition">>;
532+
"structured binding declaration in a condition is a C++2c extenstion">,
533+
InGroup<CXX26>;
534+
def warn_cxx26_decomp_decl_cond : Warning<
535+
"structured binding declaration in a condition is incompatible with "
536+
"C++ standards before C++2c">,
537+
InGroup<CXXPre26Compat>, DefaultIgnore;
534538
def err_decomp_decl_spec : Error<
535539
"decomposition declaration cannot be declared "
536540
"%plural{1:'%1'|:with '%1' specifiers}0">;

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5093,7 +5093,7 @@ template <class Emitter> bool Compiler<Emitter>::visitStmt(const Stmt *S) {
50935093
case Stmt::CompoundStmtClass:
50945094
return visitCompoundStmt(cast<CompoundStmt>(S));
50955095
case Stmt::DeclStmtClass:
5096-
return visitDeclStmt(cast<DeclStmt>(S));
5096+
return visitDeclStmt(cast<DeclStmt>(S), /*EvaluateConditionDecl=*/true);
50975097
case Stmt::ReturnStmtClass:
50985098
return visitReturnStmt(cast<ReturnStmt>(S));
50995099
case Stmt::IfStmtClass:
@@ -5147,7 +5147,18 @@ bool Compiler<Emitter>::visitCompoundStmt(const CompoundStmt *S) {
51475147
}
51485148

51495149
template <class Emitter>
5150-
bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
5150+
bool Compiler<Emitter>::maybeEmitDeferredVarInit(const VarDecl *VD) {
5151+
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
5152+
for (auto *BD : DD->bindings())
5153+
if (auto *KD = BD->getHoldingVar(); KD && !this->visitVarDecl(KD))
5154+
return false;
5155+
}
5156+
return true;
5157+
}
5158+
5159+
template <class Emitter>
5160+
bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
5161+
bool EvaluateConditionDecl) {
51515162
for (const auto *D : DS->decls()) {
51525163
if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl, BaseUsingDecl,
51535164
FunctionDecl, NamespaceAliasDecl, UsingDirectiveDecl>(D))
@@ -5160,13 +5171,8 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
51605171
return false;
51615172

51625173
// Register decomposition decl holding vars.
5163-
if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
5164-
for (auto *BD : DD->bindings())
5165-
if (auto *KD = BD->getHoldingVar()) {
5166-
if (!this->visitVarDecl(KD))
5167-
return false;
5168-
}
5169-
}
5174+
if (EvaluateConditionDecl && !this->maybeEmitDeferredVarInit(VD))
5175+
return false;
51705176
}
51715177

51725178
return true;
@@ -5249,6 +5255,9 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
52495255
return false;
52505256
}
52515257

5258+
if (!this->maybeEmitDeferredVarInit(IS->getConditionVariable()))
5259+
return false;
5260+
52525261
if (const Stmt *Else = IS->getElse()) {
52535262
LabelTy LabelElse = this->getLabel();
52545263
LabelTy LabelEnd = this->getLabel();
@@ -5294,6 +5303,10 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
52945303

52955304
if (!this->visitBool(Cond))
52965305
return false;
5306+
5307+
if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
5308+
return false;
5309+
52975310
if (!this->jumpFalse(EndLabel))
52985311
return false;
52995312

@@ -5375,6 +5388,9 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
53755388
return false;
53765389
}
53775390

5391+
if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
5392+
return false;
5393+
53785394
if (Body && !this->visitStmt(Body))
53795395
return false;
53805396

@@ -5497,6 +5513,9 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
54975513
if (!this->emitSetLocal(CondT, CondVar, S))
54985514
return false;
54995515

5516+
if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
5517+
return false;
5518+
55005519
CaseMap CaseLabels;
55015520
// Create labels and comparison ops for all case statements.
55025521
for (const SwitchCase *SC = S->getSwitchCaseList(); SC;

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
213213

214214
// Statements.
215215
bool visitCompoundStmt(const CompoundStmt *S);
216-
bool visitDeclStmt(const DeclStmt *DS);
216+
bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl = false);
217217
bool visitReturnStmt(const ReturnStmt *RS);
218218
bool visitIfStmt(const IfStmt *IS);
219219
bool visitWhileStmt(const WhileStmt *S);
@@ -389,6 +389,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
389389
bool compileUnionAssignmentOperator(const CXXMethodDecl *MD);
390390

391391
bool checkLiteralType(const Expr *E);
392+
bool maybeEmitDeferredVarInit(const VarDecl *VD);
392393

393394
protected:
394395
/// Variable to storage mapping.

clang/lib/AST/ExprConstant.cpp

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5227,20 +5227,41 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
52275227
return true;
52285228
}
52295229

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

5233+
static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
5234+
bool EvaluateConditionDecl = false) {
5235+
bool OK = true;
52335236
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
52345237
OK &= EvaluateVarDecl(Info, VD);
52355238

5236-
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D))
5237-
for (auto *BD : DD->flat_bindings())
5238-
if (auto *VD = BD->getHoldingVar())
5239-
OK &= EvaluateDecl(Info, VD);
5239+
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
5240+
EvaluateConditionDecl && DD)
5241+
OK &= EvaluateDecompositionDeclInit(Info, DD);
5242+
5243+
return OK;
5244+
}
5245+
5246+
static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
5247+
const DecompositionDecl *DD) {
5248+
bool OK = true;
5249+
for (auto *BD : DD->flat_bindings())
5250+
if (auto *VD = BD->getHoldingVar())
5251+
OK &= EvaluateDecl(Info, VD, /*EvaluateConditionDecl=*/true);
52405252

52415253
return OK;
52425254
}
52435255

5256+
static bool MaybeEvaluateDeferredVarDeclInit(EvalInfo &Info,
5257+
const VarDecl *VD) {
5258+
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
5259+
if (!EvaluateDecompositionDeclInit(Info, DD))
5260+
return false;
5261+
}
5262+
return true;
5263+
}
5264+
52445265
static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) {
52455266
assert(E->isValueDependent());
52465267
if (Info.noteSideEffect())
@@ -5260,6 +5281,8 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
52605281
return false;
52615282
if (!EvaluateAsBooleanCondition(Cond, Result, Info))
52625283
return false;
5284+
if (!MaybeEvaluateDeferredVarDeclInit(Info, CondDecl))
5285+
return false;
52635286
return Scope.destroy();
52645287
}
52655288

@@ -5344,6 +5367,9 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
53445367
if (!EvaluateInteger(SS->getCond(), Value, Info))
53455368
return ESR_Failed;
53465369

5370+
if (!MaybeEvaluateDeferredVarDeclInit(Info, SS->getConditionVariable()))
5371+
return ESR_Failed;
5372+
53475373
if (!CondScope.destroy())
53485374
return ESR_Failed;
53495375
}
@@ -5568,7 +5594,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
55685594
return ESR_Failed;
55695595
// Each declaration initialization is its own full-expression.
55705596
FullExpressionRAII Scope(Info);
5571-
if (!EvaluateDecl(Info, D) && !Info.noteFailure())
5597+
if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) &&
5598+
!Info.noteFailure())
55725599
return ESR_Failed;
55735600
if (!Scope.destroy())
55745601
return ESR_Failed;

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ using namespace CodeGen;
4848
static_assert(clang::Sema::MaximumAlignment <= llvm::Value::MaximumAlignment,
4949
"Clang max alignment greater than what LLVM supports?");
5050

51-
void CodeGenFunction::EmitDecl(const Decl &D) {
51+
void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
5252
switch (D.getKind()) {
5353
case Decl::BuiltinTemplate:
5454
case Decl::TranslationUnit:
@@ -152,7 +152,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
152152
return;
153153
case Decl::UsingPack:
154154
for (auto *Using : cast<UsingPackDecl>(D).expansions())
155-
EmitDecl(*Using);
155+
EmitDecl(*Using, /*EvaluateConditionDecl=*/EvaluateConditionDecl);
156156
return;
157157
case Decl::UsingDirective: // using namespace X; [C++]
158158
if (CGDebugInfo *DI = getDebugInfo())
@@ -164,10 +164,8 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
164164
assert(VD.isLocalVarDecl() &&
165165
"Should not see file-scope variables inside a function!");
166166
EmitVarDecl(VD);
167-
if (auto *DD = dyn_cast<DecompositionDecl>(&VD))
168-
for (auto *B : DD->flat_bindings())
169-
if (auto *HD = B->getHoldingVar())
170-
EmitVarDecl(*HD);
167+
if (EvaluateConditionDecl)
168+
MaybeEmitDeferredVarDeclInit(&VD);
171169

172170
return;
173171
}
@@ -2059,6 +2057,14 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
20592057
/*IsAutoInit=*/false);
20602058
}
20612059

2060+
void CodeGenFunction::MaybeEmitDeferredVarDeclInit(const VarDecl *VD) {
2061+
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
2062+
for (auto *B : DD->flat_bindings())
2063+
if (auto *HD = B->getHoldingVar())
2064+
EmitVarDecl(*HD);
2065+
}
2066+
}
2067+
20622068
/// Emit an expression as an initializer for an object (variable, field, etc.)
20632069
/// at the given location. The expression is not necessarily the normal
20642070
/// initializer for the object, and the address is not necessarily

0 commit comments

Comments
 (0)