[cxx2a] P0614R1: Support init-statements in range-based for loops.

We don't yet support this for the case where a range-based for loop is
implicitly rewritten to an ObjC for..in statement.

llvm-svn: 343350
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index dfd1f8c..2b5e266 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -345,22 +345,55 @@
   bool CanBeExpression = true;
   bool CanBeCondition = true;
   bool CanBeInitStatement;
+  bool CanBeForRangeDecl;
 
-  ConditionDeclarationOrInitStatementState(Parser &P, bool CanBeInitStatement)
-      : P(P), CanBeInitStatement(CanBeInitStatement) {}
+  ConditionDeclarationOrInitStatementState(Parser &P, bool CanBeInitStatement,
+                                           bool CanBeForRangeDecl)
+      : P(P), CanBeInitStatement(CanBeInitStatement),
+        CanBeForRangeDecl(CanBeForRangeDecl) {}
+
+  bool resolved() {
+    return CanBeExpression + CanBeCondition + CanBeInitStatement +
+               CanBeForRangeDecl < 2;
+  }
 
   void markNotExpression() {
     CanBeExpression = false;
 
-    if (CanBeCondition && CanBeInitStatement) {
+    if (!resolved()) {
       // FIXME: Unify the parsing codepaths for condition variables and
       // simple-declarations so that we don't need to eagerly figure out which
       // kind we have here. (Just parse init-declarators until we reach a
       // semicolon or right paren.)
       RevertingTentativeParsingAction PA(P);
-      P.SkipUntil(tok::r_paren, tok::semi, StopBeforeMatch);
+      if (CanBeForRangeDecl) {
+        // Skip until we hit a ')', ';', or a ':' with no matching '?'.
+        // The final case is a for range declaration, the rest are not.
+        while (true) {
+          unsigned QuestionColonDepth = 0;
+          P.SkipUntil({tok::r_paren, tok::semi, tok::question, tok::colon},
+                      StopBeforeMatch);
+          if (P.Tok.is(tok::question))
+            ++QuestionColonDepth;
+          else if (P.Tok.is(tok::colon)) {
+            if (QuestionColonDepth)
+              --QuestionColonDepth;
+            else {
+              CanBeCondition = CanBeInitStatement = false;
+              return;
+            }
+          } else {
+            CanBeForRangeDecl = false;
+            break;
+          }
+          P.ConsumeToken();
+        }
+      } else {
+        // Just skip until we hit a ')' or ';'.
+        P.SkipUntil(tok::r_paren, tok::semi, StopBeforeMatch);
+      }
       if (P.Tok.isNot(tok::r_paren))
-        CanBeCondition = false;
+        CanBeCondition = CanBeForRangeDecl = false;
       if (P.Tok.isNot(tok::semi))
         CanBeInitStatement = false;
     }
@@ -368,28 +401,36 @@
 
   bool markNotCondition() {
     CanBeCondition = false;
-    return !CanBeInitStatement || !CanBeExpression;
+    return resolved();
+  }
+
+  bool markNotForRangeDecl() {
+    CanBeForRangeDecl = false;
+    return resolved();
   }
 
   bool update(TPResult IsDecl) {
     switch (IsDecl) {
     case TPResult::True:
       markNotExpression();
-      return true;
+      assert(resolved() && "can't continue after tentative parsing bails out");
+      break;
     case TPResult::False:
-      CanBeCondition = CanBeInitStatement = false;
-      return true;
+      CanBeCondition = CanBeInitStatement = CanBeForRangeDecl = false;
+      break;
     case TPResult::Ambiguous:
-      return false;
+      break;
     case TPResult::Error:
-      CanBeExpression = CanBeCondition = CanBeInitStatement = false;
-      return true;
+      CanBeExpression = CanBeCondition = CanBeInitStatement =
+          CanBeForRangeDecl = false;
+      break;
     }
-    llvm_unreachable("unknown tentative parse result");
+    return resolved();
   }
 
   ConditionOrInitStatement result() const {
-    assert(CanBeExpression + CanBeCondition + CanBeInitStatement < 2 &&
+    assert(CanBeExpression + CanBeCondition + CanBeInitStatement +
+                   CanBeForRangeDecl < 2 &&
            "result called but not yet resolved");
     if (CanBeExpression)
       return ConditionOrInitStatement::Expression;
@@ -397,6 +438,8 @@
       return ConditionOrInitStatement::ConditionDecl;
     if (CanBeInitStatement)
       return ConditionOrInitStatement::InitStmtDecl;
+    if (CanBeForRangeDecl)
+      return ConditionOrInitStatement::ForRangeDecl;
     return ConditionOrInitStatement::Error;
   }
 };
@@ -419,8 +462,10 @@
 /// to the ';' to disambiguate cases like 'int(x))' (an expression) from
 /// 'int(x);' (a simple-declaration in an init-statement).
 Parser::ConditionOrInitStatement
-Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement) {
-  ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement);
+Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement,
+                                                 bool CanBeForRangeDecl) {
+  ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement,
+                                                 CanBeForRangeDecl);
 
   if (State.update(isCXXDeclarationSpecifier()))
     return State.result();
@@ -447,11 +492,19 @@
       return State.result();
     }
 
+    // A colon here identifies a for-range declaration.
+    if (State.CanBeForRangeDecl && Tok.is(tok::colon))
+      return ConditionOrInitStatement::ForRangeDecl;
+
     // At this point, it can't be a condition any more, because a condition
     // must have a brace-or-equal-initializer.
     if (State.markNotCondition())
       return State.result();
 
+    // Likewise, it can't be a for-range declaration any more.
+    if (State.markNotForRangeDecl())
+      return State.result();
+
     // A parenthesized initializer could be part of an expression or a
     // simple-declaration.
     if (Tok.is(tok::l_paren)) {