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
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 5d153c4..14503f4 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -915,10 +915,14 @@
 
 /// 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 @@
   /// 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 @@
   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 @@
   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 @@
 
     // 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 @@
     }
     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 3bc639d..4393727 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 @@
   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 @@
 #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 820a02a..8a4fa42 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 @@
       /**/;
 #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 2dfded4..d8cfb1c 100644
--- a/test/SemaCXX/constant-expression-cxx1y.cpp
+++ b/test/SemaCXX/constant-expression-cxx1y.cpp
@@ -156,33 +156,14 @@
   }
   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 @@
   }
   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(), "");
+}