diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.h b/clang/lib/AST/Interp/ByteCodeStmtGen.h --- a/clang/lib/AST/Interp/ByteCodeStmtGen.h +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.h @@ -58,6 +58,10 @@ 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); diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -153,6 +153,14 @@ return visitReturnStmt(cast(S)); case Stmt::IfStmtClass: return visitIfStmt(cast(S)); + case Stmt::WhileStmtClass: + return visitWhileStmt(cast(S)); + case Stmt::DoStmtClass: + return visitDoStmt(cast(S)); + case Stmt::BreakStmtClass: + return visitBreakStmt(cast(S)); + case Stmt::ContinueStmtClass: + return visitContinueStmt(cast(S)); case Stmt::NullStmtClass: return true; default: { @@ -262,6 +270,69 @@ return true; } +template +bool ByteCodeStmtGen::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 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 +bool ByteCodeStmtGen::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 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 +bool ByteCodeStmtGen::visitBreakStmt(const BreakStmt *S) { + if (!BreakLabel) + return false; + + return this->jump(*BreakLabel); +} + +template +bool ByteCodeStmtGen::visitContinueStmt(const ContinueStmt *S) { + if (!ContinueLabel) + return false; + + return this->jump(*ContinueLabel); +} + template bool ByteCodeStmtGen::visitVarDecl(const VarDecl *VD) { if (!VD->hasLocalStorage()) { diff --git a/clang/test/AST/Interp/loops.cpp b/clang/test/AST/Interp/loops.cpp new file mode 100644 --- /dev/null +++ b/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 +};