Skip to content

Commit

Permalink
[clang][Interp] Implement while and do-while loops
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D135433
  • Loading branch information
tbaederr committed Oct 14, 2022
1 parent 6fad712 commit 5a85943
Show file tree
Hide file tree
Showing 3 changed files with 264 additions and 0 deletions.
71 changes: 71 additions & 0 deletions clang/lib/AST/Interp/ByteCodeStmtGen.cpp
Expand Up @@ -153,6 +153,14 @@ bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) {
return visitReturnStmt(cast<ReturnStmt>(S));
case Stmt::IfStmtClass:
return visitIfStmt(cast<IfStmt>(S));
case Stmt::WhileStmtClass:
return visitWhileStmt(cast<WhileStmt>(S));
case Stmt::DoStmtClass:
return visitDoStmt(cast<DoStmt>(S));
case Stmt::BreakStmtClass:
return visitBreakStmt(cast<BreakStmt>(S));
case Stmt::ContinueStmtClass:
return visitContinueStmt(cast<ContinueStmt>(S));
case Stmt::NullStmtClass:
return true;
default: {
Expand Down Expand Up @@ -262,6 +270,69 @@ bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
return true;
}

template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitWhileStmt(const WhileStmt *S) {
const Expr *Cond = S->getCond();
const Stmt *Body = S->getBody();

LabelTy CondLabel = this->getLabel(); // Label before the condition.
LabelTy EndLabel = this->getLabel(); // Label after the loop.
LoopScope<Emitter> LS(this, EndLabel, CondLabel);

this->emitLabel(CondLabel);
if (!this->visitBool(Cond))
return false;
if (!this->jumpFalse(EndLabel))
return false;

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

this->emitLabel(EndLabel);

return true;
}

template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitDoStmt(const DoStmt *S) {
const Expr *Cond = S->getCond();
const Stmt *Body = S->getBody();

LabelTy StartLabel = this->getLabel();
LabelTy EndLabel = this->getLabel();
LabelTy CondLabel = this->getLabel();
LoopScope<Emitter> LS(this, EndLabel, CondLabel);

this->emitLabel(StartLabel);
if (!this->visitStmt(Body))
return false;
this->emitLabel(CondLabel);
if (!this->visitBool(Cond))
return false;
if (!this->jumpTrue(StartLabel))
return false;
this->emitLabel(EndLabel);
return true;
}

template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitBreakStmt(const BreakStmt *S) {
if (!BreakLabel)
return false;

return this->jump(*BreakLabel);
}

template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitContinueStmt(const ContinueStmt *S) {
if (!ContinueLabel)
return false;

return this->jump(*ContinueLabel);
}

template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) {
if (!VD->hasLocalStorage()) {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/Interp/ByteCodeStmtGen.h
Expand Up @@ -58,6 +58,10 @@ class ByteCodeStmtGen final : public ByteCodeExprGen<Emitter> {
bool visitDeclStmt(const DeclStmt *DS);
bool visitReturnStmt(const ReturnStmt *RS);
bool visitIfStmt(const IfStmt *IS);
bool visitWhileStmt(const WhileStmt *S);
bool visitDoStmt(const DoStmt *S);
bool visitBreakStmt(const BreakStmt *S);
bool visitContinueStmt(const ContinueStmt *S);

/// Compiles a variable declaration.
bool visitVarDecl(const VarDecl *VD);
Expand Down
189 changes: 189 additions & 0 deletions clang/test/AST/Interp/loops.cpp
@@ -0,0 +1,189 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++14 -verify %s
// RUN: %clang_cc1 -std=c++14 -verify=ref %s
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify=expected-cpp20 %s
// RUN: %clang_cc1 -std=c++20 -verify=ref %s

// ref-no-diagnostics
// expected-no-diagnostics

namespace WhileLoop {
constexpr int f() {
int i = 0;
while(false) {
i = i + 1;
}
return i;
}
static_assert(f() == 0, "");


constexpr int f2() {
int i = 0;
while(i != 5) {
i = i + 1;
}
return i;
}
static_assert(f2() == 5, "");

constexpr int f3() {
int i = 0;
while(true) {
i = i + 1;

if (i == 5)
break;
}
return i;
}
static_assert(f3() == 5, "");

constexpr int f4() {
int i = 0;
while(i != 5) {

i = i + 1;
continue;
i = i - 1;
}
return i;
}
static_assert(f4() == 5, "");


constexpr int f5(bool b) {
int i = 0;

while(true) {
if (!b) {
if (i == 5)
break;
}

if (b) {
while (i != 10) {
i = i + 1;
if (i == 8)
break;

continue;
}
}

if (b)
break;

i = i + 1;
continue;
}

return i;
}
static_assert(f5(true) == 8, "");
static_assert(f5(false) == 5, "");

#if 0
/// FIXME: This is an infinite loop, which should
/// be rejected.
constexpr int f6() {
while(true);
}
#endif
};

namespace DoWhileLoop {

constexpr int f() {
int i = 0;
do {
i = i + 1;
} while(false);
return i;
}
static_assert(f() == 1, "");

constexpr int f2() {
int i = 0;
do {
i = i + 1;
} while(i != 5);
return i;
}
static_assert(f2() == 5, "");


constexpr int f3() {
int i = 0;
do {
i = i + 1;
if (i == 5)
break;
} while(true);
return i;
}
static_assert(f3() == 5, "");

constexpr int f4() {
int i = 0;
do {
i = i + 1;
continue;
i = i - 1;
} while(i != 5);
return i;
}
static_assert(f4() == 5, "");

constexpr int f5(bool b) {
int i = 0;

do {
if (!b) {
if (i == 5)
break;
}

if (b) {
do {
i = i + 1;
if (i == 8)
break;

continue;
} while (i != 10);
}

if (b)
break;

i = i + 1;
continue;
} while(true);

return i;
}
static_assert(f5(true) == 8, "");
static_assert(f5(false) == 5, "");

/// FIXME: This should be accepted in C++20 but is currently being rejected
/// because the variable declaration doesn't have an initializier.
#if __cplusplus >= 202002L
constexpr int f6() {
int i;
do {
i = 5;
break;
} while (true);
return i;
}
static_assert(f6() == 5, ""); // expected-cpp20-error {{not an integral constant}}
#endif

#if 0
/// FIXME: This is an infinite loop, which should
/// be rejected.
constexpr int f7() {
while(true);
}
#endif
};

0 comments on commit 5a85943

Please sign in to comment.