C++1y: support 'for', 'while', and 'do ... while' in constant expressions.
llvm-svn: 181181
This commit is contained in:
parent
b07a68ebb0
commit
4e18ca5200
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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]) {
|
||||
|
|
|
@ -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(), "");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue