diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-05-06 05:56:11 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-05-06 05:56:11 +0000 |
commit | ce61715606b5f55ccc023720cdf9c1a796b0d526 (patch) | |
tree | c71b5f8c190fde81858034e66293e969d33ef6f8 | |
parent | 27f9cf3a5a92f70f043b6cfbc5f3f2ac3a38f182 (diff) |
C++1y: support 'for', 'while', and 'do ... while' in constant expressions.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181181 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/AST/ExprConstant.cpp | 113 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp | 8 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp | 4 | ||||
-rw-r--r-- | test/SemaCXX/constant-expression-cxx1y.cpp | 91 |
4 files changed, 172 insertions, 44 deletions
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 5d153c422f..14503f4752 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -915,10 +915,14 @@ static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info); /// Evaluate an expression to see if it had side-effects, and discard its /// result. -static void EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) { +/// \return \c true if the caller should keep evaluating. +static bool EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) { APValue Scratch; - if (!Evaluate(Scratch, Info, E)) + if (!Evaluate(Scratch, Info, E)) { Info.EvalStatus.HasSideEffects = true; + return Info.keepEvaluatingAfterFailure(); + } + return true; } /// Should this call expression be treated as a string literal? @@ -2457,7 +2461,11 @@ enum EvalStmtResult { /// Hit a 'return' statement. ESR_Returned, /// Evaluation succeeded. - ESR_Succeeded + ESR_Succeeded, + /// Hit a 'continue' statement. + ESR_Continue, + /// Hit a 'break' statement. + ESR_Break }; } @@ -2482,6 +2490,32 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) { return true; } +/// Evaluate a condition (either a variable declaration or an expression). +static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl, + const Expr *Cond, bool &Result) { + if (CondDecl && !EvaluateDecl(Info, CondDecl)) + return false; + return EvaluateAsBooleanCondition(Cond, Result, Info); +} + +static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, + const Stmt *S); + +/// Evaluate the body of a loop, and translate the result as appropriate. +static EvalStmtResult EvaluateLoopBody(APValue &Result, EvalInfo &Info, + const Stmt *Body) { + switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body)) { + case ESR_Break: + return ESR_Succeeded; + case ESR_Succeeded: + case ESR_Continue: + return ESR_Continue; + case ESR_Failed: + case ESR_Returned: + return ESR; + } +} + // Evaluate a statement. static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, const Stmt *S) { @@ -2490,10 +2524,9 @@ static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, switch (S->getStmtClass()) { default: if (const Expr *E = dyn_cast<Expr>(S)) { - EvaluateIgnoredValue(Info, E); // Don't bother evaluating beyond an expression-statement which couldn't // be evaluated. - if (Info.EvalStatus.HasSideEffects && !Info.keepEvaluatingAfterFailure()) + if (!EvaluateIgnoredValue(Info, E)) return ESR_Failed; return ESR_Succeeded; } @@ -2536,13 +2569,7 @@ static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, // Evaluate the condition, as either a var decl or as an expression. bool Cond; - if (VarDecl *CondDecl = IS->getConditionVariable()) { - if (!EvaluateDecl(Info, CondDecl)) - return ESR_Failed; - if (!HandleConversionToBool(Info.CurrentCall->Temporaries[CondDecl], - Cond)) - return ESR_Failed; - } else if (!EvaluateAsBooleanCondition(IS->getCond(), Cond, Info)) + if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond)) return ESR_Failed; if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) { @@ -2552,6 +2579,68 @@ static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, } return ESR_Succeeded; } + + case Stmt::WhileStmtClass: { + const WhileStmt *WS = cast<WhileStmt>(S); + while (true) { + bool Continue; + if (!EvaluateCond(Info, WS->getConditionVariable(), WS->getCond(), + Continue)) + return ESR_Failed; + if (!Continue) + break; + + EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody()); + if (ESR != ESR_Continue) + return ESR; + } + return ESR_Succeeded; + } + + case Stmt::DoStmtClass: { + const DoStmt *DS = cast<DoStmt>(S); + bool Continue; + do { + EvalStmtResult ESR = EvaluateLoopBody(Result, Info, DS->getBody()); + if (ESR != ESR_Continue) + return ESR; + + if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info)) + return ESR_Failed; + } while (Continue); + return ESR_Succeeded; + } + + case Stmt::ForStmtClass: { + const ForStmt *FS = cast<ForStmt>(S); + if (FS->getInit()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit()); + if (ESR != ESR_Succeeded) + return ESR; + } + while (true) { + bool Continue = true; + if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(), + FS->getCond(), Continue)) + return ESR_Failed; + if (!Continue) + break; + + EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody()); + if (ESR != ESR_Continue) + return ESR; + + if (FS->getInc() && !EvaluateIgnoredValue(Info, FS->getInc())) + return ESR_Failed; + } + return ESR_Succeeded; + } + + case Stmt::ContinueStmtClass: + return ESR_Continue; + + case Stmt::BreakStmtClass: + return ESR_Break; } } diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp index 3bc639dd57..4393727c19 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp @@ -143,10 +143,6 @@ constexpr int ForStmt() { for (int n = 0; n < 10; ++n) #ifndef CXX1Y // expected-error@-2 {{statement not allowed in constexpr function}} -#else - // FIXME: Once we support evaluating a for-statement, this diagnostic should disappear. - // expected-error@-6 {{never produces a constant expression}} - // expected-note@-6 {{subexpression}} #endif return 0; } @@ -289,9 +285,5 @@ namespace std_example { #ifndef CXX1Y // expected-error@-5 {{C++1y}} // expected-error@-5 {{statement not allowed}} -#else - // FIXME: This should be allowed. - // expected-error@-10 {{never produces a constant}} - // expected-note@-9 {{subexpression}} #endif } diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp index 820a02a5e0..8a4fa42f00 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp @@ -87,10 +87,6 @@ struct V { /**/; #ifndef CXX1Y // expected-error@-3 {{statement not allowed in constexpr constructor}} -#else - // FIXME: Once we support evaluating a for-statement, this diagnostic should disappear. - // expected-error@-7 {{never produces a constant expression}} - // expected-note@-7 {{subexpression}} #endif } constexpr V(int(&)[2]) { diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index 2dfded4cc3..d8cfb1c297 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -156,33 +156,14 @@ namespace string_assign { } template<typename Iterator> constexpr void reverse(Iterator begin, Iterator end) { -#if 0 // FIXME: once implementation is complete... while (begin != end && begin != --end) swap(*begin++, *end); -#else - if (begin != end) { - if (begin == --end) - return; - swap(*begin++, *end); - reverse(begin, end); - } -#endif } template<typename Iterator1, typename Iterator2> constexpr bool equal(Iterator1 a, Iterator1 ae, Iterator2 b, Iterator2 be) { -#if 0 // FIXME: once implementation is complete... - while (a != ae && b != be) { - if (*a != *b) - return false; - ++a, ++b; - } -#else - if (a != ae && b != be) { + while (a != ae && b != be) if (*a++ != *b++) return false; - return equal(a, ae, b, be); - } -#endif return a == ae && b == be; } constexpr bool test1(int n) { @@ -352,3 +333,73 @@ namespace incdec { } static_assert(incr(0) == 101, ""); } + +namespace loops { + constexpr int fib_loop(int a) { + int f_k = 0, f_k_plus_one = 1; + for (int k = 1; k != a; ++k) { + int f_k_plus_two = f_k + f_k_plus_one; + f_k = f_k_plus_one; + f_k_plus_one = f_k_plus_two; + } + return f_k_plus_one; + } + static_assert(fib_loop(46) == 1836311903, ""); + + constexpr bool breaks_work() { + int a = 0; + for (int n = 0; n != 100; ++n) { + ++a; + if (a == 5) continue; + if ((a % 5) == 0) break; + } + + int b = 0; + while (b != 17) { + ++b; + if (b == 6) continue; + if ((b % 6) == 0) break; + } + + int c = 0; + do { + ++c; + if (c == 7) continue; + if ((c % 7) == 0) break; + } while (c != 21); + + return a == 10 && b == 12 & c == 14; + } + static_assert(breaks_work(), ""); + + void not_constexpr(); + constexpr bool no_cont_after_break() { + for (;;) { + break; + not_constexpr(); + } + while (true) { + break; + not_constexpr(); + } + do { + break; + not_constexpr(); + } while (true); + return true; + } + static_assert(no_cont_after_break(), ""); + + constexpr bool cond() { + for (int a = 1; bool b = a != 3; ++a) { + if (!b) + return false; + } + while (bool b = true) { + b = false; + break; + } + return true; + } + static_assert(cond(), ""); +} |