If we find an error in the range expression in a range-based for loop, and the
loop variable has a type containing 'auto', set the declaration to be invalid
(because we couldn't deduce its type) to prevent follow-on errors.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@188853 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 5b21c93..0570a60 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -1831,10 +1831,10 @@
 Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc,
                            Stmt *First, SourceLocation ColonLoc, Expr *Range,
                            SourceLocation RParenLoc, BuildForRangeKind Kind) {
-  if (!First || !Range)
+  if (!First)
     return StmtError();
 
-  if (ObjCEnumerationCollection(Range))
+  if (Range && ObjCEnumerationCollection(Range))
     return ActOnObjCForCollectionStmt(ForLoc, First, Range, RParenLoc);
 
   DeclStmt *DS = dyn_cast<DeclStmt>(First);
@@ -1844,11 +1844,13 @@
     Diag(DS->getStartLoc(), diag::err_type_defined_in_for_range);
     return StmtError();
   }
-  if (DS->getSingleDecl()->isInvalidDecl())
-    return StmtError();
 
-  if (DiagnoseUnexpandedParameterPack(Range, UPPC_Expression))
+  Decl *LoopVar = DS->getSingleDecl();
+  if (LoopVar->isInvalidDecl() || !Range ||
+      DiagnoseUnexpandedParameterPack(Range, UPPC_Expression)) {
+    LoopVar->setInvalidDecl();
     return StmtError();
+  }
 
   // Build  auto && __range = range-init
   SourceLocation RangeLoc = Range->getLocStart();
@@ -1856,16 +1858,20 @@
                                            Context.getAutoRRefDeductType(),
                                            "__range");
   if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
-                            diag::err_for_range_deduction_failure))
+                            diag::err_for_range_deduction_failure)) {
+    LoopVar->setInvalidDecl();
     return StmtError();
+  }
 
   // Claim the type doesn't contain auto: we've already done the checking.
   DeclGroupPtrTy RangeGroup =
       BuildDeclaratorGroup(llvm::MutableArrayRef<Decl *>((Decl **)&RangeVar, 1),
                            /*TypeMayContainAuto=*/ false);
   StmtResult RangeDecl = ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc);
-  if (RangeDecl.isInvalid())
+  if (RangeDecl.isInvalid()) {
+    LoopVar->setInvalidDecl();
     return StmtError();
+  }
 
   return BuildCXXForRangeStmt(ForLoc, ColonLoc, RangeDecl.get(),
                               /*BeginEndDecl=*/0, /*Cond=*/0, /*Inc=*/0, DS,
@@ -1994,6 +2000,22 @@
                                       Sema::BFRK_Rebuild);
 }
 
+namespace {
+/// RAII object to automatically invalidate a declaration if an error occurs.
+struct InvalidateOnErrorScope {
+  InvalidateOnErrorScope(Sema &SemaRef, Decl *D, bool Enabled)
+      : Trap(SemaRef.Diags), D(D), Enabled(Enabled) {}
+  ~InvalidateOnErrorScope() {
+    if (Enabled && Trap.hasErrorOccurred())
+      D->setInvalidDecl();
+  }
+
+  DiagnosticErrorTrap Trap;
+  Decl *D;
+  bool Enabled;
+};
+}
+
 /// BuildCXXForRangeStmt - Build or instantiate a C++11 for-range statement.
 StmtResult
 Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc,
@@ -2009,6 +2031,11 @@
   DeclStmt *LoopVarDS = cast<DeclStmt>(LoopVarDecl);
   VarDecl *LoopVar = cast<VarDecl>(LoopVarDS->getSingleDecl());
 
+  // If we hit any errors, mark the loop variable as invalid if its type
+  // contains 'auto'.
+  InvalidateOnErrorScope Invalidate(*this, LoopVar,
+                                    LoopVar->getType()->isUndeducedType());
+
   StmtResult BeginEndDecl = BeginEnd;
   ExprResult NotEqExpr = Cond, IncrExpr = Inc;
 
diff --git a/test/SemaCXX/cxx11-crashes.cpp b/test/SemaCXX/cxx11-crashes.cpp
index a4d4829..bd51af1 100644
--- a/test/SemaCXX/cxx11-crashes.cpp
+++ b/test/SemaCXX/cxx11-crashes.cpp
@@ -70,9 +70,7 @@
     for (auto x : s) {
       // We used to attempt to evaluate the initializer of this variable,
       // and crash because it has an undeduced type.
-      // FIXME: We should set the loop variable to be invalid if we can't build
-      // the loop, to suppress this follow-on error.
-      const int &n(x); // expected-error {{could not bind to an lvalue of type 'auto'}}
+      const int &n(x);
     }
   }
 }
diff --git a/test/SemaCXX/for-range-examples.cpp b/test/SemaCXX/for-range-examples.cpp
index ca505e0..08c6936 100644
--- a/test/SemaCXX/for-range-examples.cpp
+++ b/test/SemaCXX/for-range-examples.cpp
@@ -180,3 +180,14 @@
     for (y : {1, 2, 3}) {} // expected-error {{must declare a variable}} expected-warning {{result unused}}
   }
 }
+
+namespace test5 {
+  // Test error-recovery.
+  void f() {
+    for (auto x : undeclared_identifier) // expected-error {{undeclared identifier}}
+      for (auto y : x->foo)
+        y->bar();
+    for (auto x : 123) // expected-error {{no viable 'begin'}}
+      x->foo();
+  }
+}