Skip to content

Commit 5cd91f8

Browse files
committed
Add a simple CG test, release note, comments for decision variable [dcl.struct.bind]
1 parent 295b817 commit 5cd91f8

File tree

6 files changed

+138
-2
lines changed

6 files changed

+138
-2
lines changed

clang/docs/ReleaseNotes.rst

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

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

79+
- Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.
80+
7981
C++23 Feature Support
8082
^^^^^^^^^^^^^^^^^^^^^
8183

clang/include/clang/AST/DeclCXX.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4290,6 +4290,10 @@ class DecompositionDecl final
42904290
std::move(Bindings));
42914291
}
42924292

4293+
/// The decision variable of a condition that is a structured binding
4294+
/// declaration is specified in [dcl.struct.bind]p4:
4295+
/// If a structured binding declaration appears as a condition, the decision
4296+
/// variable of the condition is e.
42934297
bool isDecisionVariable() const { return IsDecisionVariable; }
42944298

42954299
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,11 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
11101110
// execution of the loop body.
11111111
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
11121112

1113+
if (auto *DD =
1114+
dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
1115+
DD && DD->isDecisionVariable())
1116+
EmitDecompositionVarInit(*DD);
1117+
11131118
// while(1) is common, avoid extra exit blocks. Be sure
11141119
// to correctly handle break/continue though.
11151120
llvm::ConstantInt *C = dyn_cast<llvm::ConstantInt>(BoolCondVal);
@@ -1343,6 +1348,12 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
13431348
// C99 6.8.5p2/p4: The first substatement is executed if the expression
13441349
// compares unequal to 0. The condition must be a scalar type.
13451350
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
1351+
1352+
if (auto *DD =
1353+
dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
1354+
DD && DD->isDecisionVariable())
1355+
EmitDecompositionVarInit(*DD);
1356+
13461357
llvm::MDNode *Weights =
13471358
createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody()));
13481359
if (!Weights && CGM.getCodeGenOpts().OptimizationLevel)
@@ -2240,6 +2251,11 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
22402251
if (S.getConditionVariable())
22412252
EmitDecl(*S.getConditionVariable());
22422253

2254+
if (auto *DD =
2255+
dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
2256+
DD && DD->isDecisionVariable())
2257+
EmitDecompositionVarInit(*DD);
2258+
22432259
// At this point, we are no longer "within" a switch instance, so
22442260
// we can temporarily enforce this to ensure that any embedded case
22452261
// statements are not emitted.
@@ -2271,6 +2287,11 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
22712287
EmitDecl(*S.getConditionVariable());
22722288
llvm::Value *CondV = EmitScalarExpr(S.getCond());
22732289

2290+
if (auto *DD =
2291+
dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
2292+
DD && DD->isDecisionVariable())
2293+
EmitDecompositionVarInit(*DD);
2294+
22742295
// Create basic block to hold stuff that comes after switch
22752296
// statement. We also need to create a default block now so that
22762297
// explicit case ranges tests can have a place to jump to on

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7770,7 +7770,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
77707770
} else if (D.isDecompositionDeclarator()) {
77717771
NewVD = DecompositionDecl::Create(
77727772
Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), R, TInfo, SC,
7773-
Bindings, D.getContext() == DeclaratorContext::Condition);
7773+
Bindings, /*IsDecisionVariable=*/D.getContext() ==
7774+
DeclaratorContext::Condition);
77747775
} else
77757776
NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(),
77767777
D.getIdentifierLoc(), II, R, TInfo, SC);

clang/test/CodeGen/p0963r3.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm %s -o - | FileCheck %s
2+
// expected-no-diagnostics
3+
4+
namespace std {
5+
6+
template <typename T> struct tuple_size;
7+
8+
template <int, typename> struct tuple_element;
9+
10+
} // namespace std
11+
12+
namespace Case1 {
13+
14+
struct S {
15+
int a, b;
16+
bool called_operator_bool = false;
17+
18+
operator bool() {
19+
called_operator_bool = true;
20+
return a != b;
21+
}
22+
23+
template <int I> int get() {
24+
if (!called_operator_bool)
25+
return a + b;
26+
return I == 0 ? a : b;
27+
}
28+
};
29+
30+
} // namespace Case1
31+
32+
template <> struct std::tuple_size<Case1::S> {
33+
static const int value = 2;
34+
};
35+
36+
template <int I> struct std::tuple_element<I, Case1::S> {
37+
using type = int;
38+
};
39+
40+
namespace Case1 {
41+
42+
void foo() {
43+
if (S s(1, 2); auto [a, b] = s) {
44+
__builtin_assume(a == 1);
45+
__builtin_assume(b == 2);
46+
}
47+
// CHECK: %[[call:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
48+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
49+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
50+
// CHECK: br i1 %[[call]], label {{.*}}, label {{.*}}
51+
52+
if (auto [a, b] = S(1, 2)) {
53+
__builtin_assume(a == 1);
54+
__builtin_assume(b == 2);
55+
}
56+
// CHECK: %[[call2:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
57+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
58+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
59+
// CHECK: br i1 %[[call2]], label {{.*}}, label {{.*}}
60+
61+
if (S s(3, 4); auto& [a, b] = s) {
62+
__builtin_assume(a == 3);
63+
__builtin_assume(b == 4);
64+
}
65+
// CHECK: %[[call3:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
66+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
67+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
68+
// CHECK: br i1 %[[call3]], label {{.*}}, label {{.*}}
69+
70+
while (auto [i, j] = S(5, 6))
71+
break;
72+
73+
// CHECK: while.cond{{.*}}:
74+
// CHECK: %[[call4:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
75+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
76+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
77+
// CHECK: br i1 %[[call4]], label {{.*}}, label {{.*}}
78+
79+
S s(7, 8);
80+
while (auto& [i, j] = s)
81+
break;
82+
83+
// CHECK: while.cond{{.*}}:
84+
// CHECK: %[[call5:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
85+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
86+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
87+
// CHECK: br i1 %[[call5]], label {{.*}}, label {{.*}}
88+
89+
for (int k = 0; auto [i, j] = S(24, 42); ++k)
90+
break;
91+
92+
// CHECK: for.cond{{.*}}:
93+
// CHECK: %[[call6:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
94+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
95+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
96+
// CHECK: br i1 %[[call6]], label {{.*}}, label {{.*}}
97+
98+
for (S s(114, 514); auto& [i, j] = s; ++i)
99+
break;
100+
101+
// CHECK: for.cond{{.*}}:
102+
// CHECK: %[[call7:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
103+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
104+
// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
105+
// CHECK: br i1 %[[call7]], label {{.*}}, label {{.*}}
106+
}
107+
108+
} // namespace Case1

clang/www/cxx_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
223223
<tr>
224224
<td>Structured binding declaration as a condition</td>
225225
<td><a href="https://wg21.link/P0963R3">P0963R3</a></td>
226-
<td class="none" align="center">No</td>
226+
<td class="unreleased" align="center">Clang 21</td>
227227
</tr>
228228
<!--Poland, Fall 2024-->
229229
<tr>

0 commit comments

Comments
 (0)