Skip to content

Commit 5a85943

Browse files
committed
[clang][Interp] Implement while and do-while loops
Differential Revision: https://reviews.llvm.org/D135433
1 parent 6fad712 commit 5a85943

File tree

3 files changed

+264
-0
lines changed

3 files changed

+264
-0
lines changed

clang/lib/AST/Interp/ByteCodeStmtGen.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) {
153153
return visitReturnStmt(cast<ReturnStmt>(S));
154154
case Stmt::IfStmtClass:
155155
return visitIfStmt(cast<IfStmt>(S));
156+
case Stmt::WhileStmtClass:
157+
return visitWhileStmt(cast<WhileStmt>(S));
158+
case Stmt::DoStmtClass:
159+
return visitDoStmt(cast<DoStmt>(S));
160+
case Stmt::BreakStmtClass:
161+
return visitBreakStmt(cast<BreakStmt>(S));
162+
case Stmt::ContinueStmtClass:
163+
return visitContinueStmt(cast<ContinueStmt>(S));
156164
case Stmt::NullStmtClass:
157165
return true;
158166
default: {
@@ -262,6 +270,69 @@ bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
262270
return true;
263271
}
264272

273+
template <class Emitter>
274+
bool ByteCodeStmtGen<Emitter>::visitWhileStmt(const WhileStmt *S) {
275+
const Expr *Cond = S->getCond();
276+
const Stmt *Body = S->getBody();
277+
278+
LabelTy CondLabel = this->getLabel(); // Label before the condition.
279+
LabelTy EndLabel = this->getLabel(); // Label after the loop.
280+
LoopScope<Emitter> LS(this, EndLabel, CondLabel);
281+
282+
this->emitLabel(CondLabel);
283+
if (!this->visitBool(Cond))
284+
return false;
285+
if (!this->jumpFalse(EndLabel))
286+
return false;
287+
288+
if (!this->visitStmt(Body))
289+
return false;
290+
if (!this->jump(CondLabel))
291+
return false;
292+
293+
this->emitLabel(EndLabel);
294+
295+
return true;
296+
}
297+
298+
template <class Emitter>
299+
bool ByteCodeStmtGen<Emitter>::visitDoStmt(const DoStmt *S) {
300+
const Expr *Cond = S->getCond();
301+
const Stmt *Body = S->getBody();
302+
303+
LabelTy StartLabel = this->getLabel();
304+
LabelTy EndLabel = this->getLabel();
305+
LabelTy CondLabel = this->getLabel();
306+
LoopScope<Emitter> LS(this, EndLabel, CondLabel);
307+
308+
this->emitLabel(StartLabel);
309+
if (!this->visitStmt(Body))
310+
return false;
311+
this->emitLabel(CondLabel);
312+
if (!this->visitBool(Cond))
313+
return false;
314+
if (!this->jumpTrue(StartLabel))
315+
return false;
316+
this->emitLabel(EndLabel);
317+
return true;
318+
}
319+
320+
template <class Emitter>
321+
bool ByteCodeStmtGen<Emitter>::visitBreakStmt(const BreakStmt *S) {
322+
if (!BreakLabel)
323+
return false;
324+
325+
return this->jump(*BreakLabel);
326+
}
327+
328+
template <class Emitter>
329+
bool ByteCodeStmtGen<Emitter>::visitContinueStmt(const ContinueStmt *S) {
330+
if (!ContinueLabel)
331+
return false;
332+
333+
return this->jump(*ContinueLabel);
334+
}
335+
265336
template <class Emitter>
266337
bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) {
267338
if (!VD->hasLocalStorage()) {

clang/lib/AST/Interp/ByteCodeStmtGen.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ class ByteCodeStmtGen final : public ByteCodeExprGen<Emitter> {
5858
bool visitDeclStmt(const DeclStmt *DS);
5959
bool visitReturnStmt(const ReturnStmt *RS);
6060
bool visitIfStmt(const IfStmt *IS);
61+
bool visitWhileStmt(const WhileStmt *S);
62+
bool visitDoStmt(const DoStmt *S);
63+
bool visitBreakStmt(const BreakStmt *S);
64+
bool visitContinueStmt(const ContinueStmt *S);
6165

6266
/// Compiles a variable declaration.
6367
bool visitVarDecl(const VarDecl *VD);

clang/test/AST/Interp/loops.cpp

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++14 -verify %s
2+
// RUN: %clang_cc1 -std=c++14 -verify=ref %s
3+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify=expected-cpp20 %s
4+
// RUN: %clang_cc1 -std=c++20 -verify=ref %s
5+
6+
// ref-no-diagnostics
7+
// expected-no-diagnostics
8+
9+
namespace WhileLoop {
10+
constexpr int f() {
11+
int i = 0;
12+
while(false) {
13+
i = i + 1;
14+
}
15+
return i;
16+
}
17+
static_assert(f() == 0, "");
18+
19+
20+
constexpr int f2() {
21+
int i = 0;
22+
while(i != 5) {
23+
i = i + 1;
24+
}
25+
return i;
26+
}
27+
static_assert(f2() == 5, "");
28+
29+
constexpr int f3() {
30+
int i = 0;
31+
while(true) {
32+
i = i + 1;
33+
34+
if (i == 5)
35+
break;
36+
}
37+
return i;
38+
}
39+
static_assert(f3() == 5, "");
40+
41+
constexpr int f4() {
42+
int i = 0;
43+
while(i != 5) {
44+
45+
i = i + 1;
46+
continue;
47+
i = i - 1;
48+
}
49+
return i;
50+
}
51+
static_assert(f4() == 5, "");
52+
53+
54+
constexpr int f5(bool b) {
55+
int i = 0;
56+
57+
while(true) {
58+
if (!b) {
59+
if (i == 5)
60+
break;
61+
}
62+
63+
if (b) {
64+
while (i != 10) {
65+
i = i + 1;
66+
if (i == 8)
67+
break;
68+
69+
continue;
70+
}
71+
}
72+
73+
if (b)
74+
break;
75+
76+
i = i + 1;
77+
continue;
78+
}
79+
80+
return i;
81+
}
82+
static_assert(f5(true) == 8, "");
83+
static_assert(f5(false) == 5, "");
84+
85+
#if 0
86+
/// FIXME: This is an infinite loop, which should
87+
/// be rejected.
88+
constexpr int f6() {
89+
while(true);
90+
}
91+
#endif
92+
};
93+
94+
namespace DoWhileLoop {
95+
96+
constexpr int f() {
97+
int i = 0;
98+
do {
99+
i = i + 1;
100+
} while(false);
101+
return i;
102+
}
103+
static_assert(f() == 1, "");
104+
105+
constexpr int f2() {
106+
int i = 0;
107+
do {
108+
i = i + 1;
109+
} while(i != 5);
110+
return i;
111+
}
112+
static_assert(f2() == 5, "");
113+
114+
115+
constexpr int f3() {
116+
int i = 0;
117+
do {
118+
i = i + 1;
119+
if (i == 5)
120+
break;
121+
} while(true);
122+
return i;
123+
}
124+
static_assert(f3() == 5, "");
125+
126+
constexpr int f4() {
127+
int i = 0;
128+
do {
129+
i = i + 1;
130+
continue;
131+
i = i - 1;
132+
} while(i != 5);
133+
return i;
134+
}
135+
static_assert(f4() == 5, "");
136+
137+
constexpr int f5(bool b) {
138+
int i = 0;
139+
140+
do {
141+
if (!b) {
142+
if (i == 5)
143+
break;
144+
}
145+
146+
if (b) {
147+
do {
148+
i = i + 1;
149+
if (i == 8)
150+
break;
151+
152+
continue;
153+
} while (i != 10);
154+
}
155+
156+
if (b)
157+
break;
158+
159+
i = i + 1;
160+
continue;
161+
} while(true);
162+
163+
return i;
164+
}
165+
static_assert(f5(true) == 8, "");
166+
static_assert(f5(false) == 5, "");
167+
168+
/// FIXME: This should be accepted in C++20 but is currently being rejected
169+
/// because the variable declaration doesn't have an initializier.
170+
#if __cplusplus >= 202002L
171+
constexpr int f6() {
172+
int i;
173+
do {
174+
i = 5;
175+
break;
176+
} while (true);
177+
return i;
178+
}
179+
static_assert(f6() == 5, ""); // expected-cpp20-error {{not an integral constant}}
180+
#endif
181+
182+
#if 0
183+
/// FIXME: This is an infinite loop, which should
184+
/// be rejected.
185+
constexpr int f7() {
186+
while(true);
187+
}
188+
#endif
189+
};

0 commit comments

Comments
 (0)