From a523ce3d6164913fbaac47c12791b02e4cd9f027 Mon Sep 17 00:00:00 2001
From: Sam Panzer <panzer@google.com>
Date: Fri, 8 Jun 2012 10:57:14 -0700
Subject: [PATCH] Better diagnostics in for-range expressions

Fully abstracted for-range non-array checking
---
 include/clang/Basic/DiagnosticSemaKinds.td      |    7 +
 include/clang/Sema/Sema.h                       |   19 ++-
 lib/Sema/SemaOverload.cpp                       |  157 ++++++++----
 lib/Sema/SemaStmt.cpp                           |  308 ++++++++++++++++++-----
 test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp |   28 ++-
 test/SemaCXX/for-range-no-std.cpp               |    5 +-
 test/SemaCXX/typo-correction.cpp                |    3 +-
 7 files changed, 394 insertions(+), 133 deletions(-)

diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 52641b8..97b0469 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1391,7 +1391,14 @@ def err_for_range_member_begin_end_mismatch : Error<
   "range type %0 has '%select{begin|end}1' member but no '%select{end|begin}1' member">;
 def err_for_range_begin_end_types_differ : Error<
   "'begin' and 'end' must return the same type (got %0 and %1)">;
+def err_for_range_invalid: Error<
+  "invalid range expression of type %0">;
+def note_for_range_adl_failure: Note<
+  "no viable '%select{begin|end}0' function for range of type %1">;
 def note_for_range_type : Note<"range has type %0">;
+def err_for_range_dereference : Error<
+  "invalid range expression of type %0; did you mean to dereference it "
+  "with '*'?">;
 def note_for_range_begin_end : Note<
   "selected '%select{begin|end}0' %select{function|template }1%2 with iterator type %3">;
 
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index a48cde0..29fbded 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -1923,7 +1923,24 @@ public:
                                      Expr **Args, unsigned NumArgs,
                                      SourceLocation RParenLoc,
                                      Expr *ExecConfig,
-                                     bool AllowTypoCorrection=true);
+                                     OverloadCandidateSet *CandidateSet,
+                                     bool AllowTypoCorrection,
+                                     bool InRangeExpr);
+
+  ExprResult BuildOverloadedCallExpr(Scope *S, Expr *Fn,
+                                     UnresolvedLookupExpr *ULE,
+                                     SourceLocation LParenLoc,
+                                     Expr **Args, unsigned NumArgs,
+                                     SourceLocation RParenLoc,
+                                     Expr *ExecConfig,
+                                     bool AllowTypoCorrection=true,
+                                     bool InRangeExpr=false);
+
+  bool buildOverloadedCallSet(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
+                              Expr **Args, unsigned NumArgs,
+                              SourceLocation RParenLoc,
+                              OverloadCandidateSet *CandidateSet,
+                              ExprResult *Result);
 
   ExprResult CreateOverloadedUnaryOp(SourceLocation OpLoc,
                                      unsigned Opc,
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index f045a2b..0a71dc7 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -735,9 +735,12 @@ OverloadCandidate::DeductionFailureInfo::getSecondArg() {
 }
 
 void OverloadCandidateSet::clear() {
-  for (iterator i = begin(), e = end(); i != e; ++i)
+  for (iterator i = begin(), e = end(); i != e; ++i) {
     for (unsigned ii = 0, ie = i->NumConversions; ii != ie; ++ii)
       i->Conversions[ii].~ImplicitConversionSequence();
+    if (i->FailureKind == ovl_fail_bad_deduction)
+      i->DeductionFailure.Destroy();
+  }
   NumInlineSequences = 0;
   Candidates.clear();
   Functions.clear();
@@ -9688,55 +9691,30 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
                                RParenLoc);
 }
 
-/// ResolveOverloadedCallFn - Given the call expression that calls Fn
-/// (which eventually refers to the declaration Func) and the call
-/// arguments Args/NumArgs, attempt to resolve the function call down
-/// to a specific function. If overload resolution succeeds, returns
-/// the function declaration produced by overload
-/// resolution. Otherwise, emits diagnostics, deletes all of the
-/// arguments and Fn, and returns NULL.
-ExprResult
-Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
-                              SourceLocation LParenLoc,
-                              Expr **Args, unsigned NumArgs,
-                              SourceLocation RParenLoc,
-                              Expr *ExecConfig,
-                              bool AllowTypoCorrection) {
-#ifndef NDEBUG
-  if (ULE->requiresADL()) {
-    // To do ADL, we must have found an unqualified name.
-    assert(!ULE->getQualifier() && "qualified name with ADL");
-
-    // We don't perform ADL for implicit declarations of builtins.
-    // Verify that this was correctly set up.
-    FunctionDecl *F;
-    if (ULE->decls_begin() + 1 == ULE->decls_end() &&
-        (F = dyn_cast<FunctionDecl>(*ULE->decls_begin())) &&
-        F->getBuiltinID() && F->isImplicit())
-      llvm_unreachable("performing ADL for builtin");
-
-    // We don't perform ADL in C.
-    assert(getLangOpts().CPlusPlus && "ADL enabled in C");
-  } else
-    assert(!ULE->isStdAssociatedNamespace() &&
-           "std is associated namespace but not doing ADL");
-#endif
-
+/// \brief Constructs and populates an OverloadedCandidateSet from
+/// the given function.
+/// \returns true when an the ExprResult output parameter has been set.
+bool Sema::buildOverloadedCallSet(Scope *S, Expr *Fn,
+                                  UnresolvedLookupExpr *ULE,
+                                  Expr **Args, unsigned NumArgs,
+                                  SourceLocation RParenLoc,
+                                  OverloadCandidateSet *CandidateSet,
+                                  ExprResult *Result) {
   UnbridgedCastsSet UnbridgedCasts;
-  if (checkArgPlaceholdersForOverload(*this, Args, NumArgs, UnbridgedCasts))
-    return ExprError();
-
-  OverloadCandidateSet CandidateSet(Fn->getExprLoc());
+  if (checkArgPlaceholdersForOverload(*this, Args, NumArgs, UnbridgedCasts)) {
+    *Result = ExprError();
+    return true;
+  }
 
   // Add the functions denoted by the callee to the set of candidate
   // functions, including those from argument-dependent lookup.
   AddOverloadedCallCandidates(ULE, llvm::makeArrayRef(Args, NumArgs),
-                              CandidateSet);
+                              *CandidateSet);
 
   // If we found nothing, try to recover.
   // BuildRecoveryCallExpr diagnoses the error itself, so we just bail
   // out if it fails.
-  if (CandidateSet.empty()) {
+  if (CandidateSet->empty()) {
     // In Microsoft mode, if we are inside a template class member function then
     // create a type dependent CallExpr. The goal is to postpone name lookup
     // to instantiation time to be able to search into type dependent base
@@ -9747,18 +9725,43 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
                                           Context.DependentTy, VK_RValue,
                                           RParenLoc);
       CE->setTypeDependent(true);
-      return Owned(CE);
+      *Result = Owned(CE);
+      return true;
     }
+    return false;
+  }
+
+  UnbridgedCasts.restore();
+  return false;
+}
+
+
+// FIXME: Update this stale comment
+/// ResolveOverloadedCallFn - Given the call expression that calls Fn
+/// (which eventually refers to the declaration Func) and the call
+/// arguments Args/NumArgs, attempt to resolve the function call down
+/// to a specific function. If overload resolution succeeds, returns
+/// the function declaration produced by overload
+/// resolution. Otherwise, emits diagnostics, deletes all of the
+/// arguments and Fn, and returns NULL.
+
+ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
+                                         UnresolvedLookupExpr *ULE,
+                                         SourceLocation LParenLoc,
+                                         Expr **Args, unsigned NumArgs,
+                                         SourceLocation RParenLoc,
+                                         Expr *ExecConfig,
+                                         OverloadCandidateSet *CandidateSet,
+                                         bool AllowTypoCorrection,
+                                         bool InRangeExpr) {
+  if (CandidateSet->empty())
     return BuildRecoveryCallExpr(*this, S, Fn, ULE, LParenLoc,
                                  llvm::MutableArrayRef<Expr *>(Args, NumArgs),
                                  RParenLoc, /*EmptyLookup=*/true,
                                  AllowTypoCorrection);
-  }
-
-  UnbridgedCasts.restore();
 
   OverloadCandidateSet::iterator Best;
-  switch (CandidateSet.BestViableFunction(*this, Fn->getLocStart(), Best)) {
+  switch (CandidateSet->BestViableFunction(*this, Fn->getLocStart(), Best)) {
   case OR_Success: {
     FunctionDecl *FDecl = Best->Function;
     MarkFunctionReferenced(Fn->getExprLoc(), FDecl);
@@ -9770,6 +9773,10 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
   }
 
   case OR_No_Viable_Function: {
+    // In case we're in a range expression, let the range handling code take
+    // care of it.
+    if (InRangeExpr)
+      break;
     // Try to recover by looking for viable functions which the user might
     // have meant to call.
     ExprResult Recovery = BuildRecoveryCallExpr(*this, S, Fn, ULE, LParenLoc,
@@ -9783,27 +9790,26 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
     Diag(Fn->getLocStart(),
          diag::err_ovl_no_viable_function_in_call)
       << ULE->getName() << Fn->getSourceRange();
-    CandidateSet.NoteCandidates(*this, OCD_AllCandidates,
-                                llvm::makeArrayRef(Args, NumArgs));
+    CandidateSet->NoteCandidates(*this, OCD_AllCandidates,
+                                 llvm::makeArrayRef(Args, NumArgs));
     break;
   }
 
   case OR_Ambiguous:
     Diag(Fn->getLocStart(), diag::err_ovl_ambiguous_call)
       << ULE->getName() << Fn->getSourceRange();
-    CandidateSet.NoteCandidates(*this, OCD_ViableCandidates,
-                                llvm::makeArrayRef(Args, NumArgs));
+    CandidateSet->NoteCandidates(*this, OCD_ViableCandidates,
+                                 llvm::makeArrayRef(Args, NumArgs));
     break;
 
-  case OR_Deleted:
-    {
+    case OR_Deleted: {
       Diag(Fn->getLocStart(), diag::err_ovl_deleted_call)
         << Best->Function->isDeleted()
         << ULE->getName()
         << getDeletedOrUnavailableSuffix(Best->Function)
         << Fn->getSourceRange();
-      CandidateSet.NoteCandidates(*this, OCD_AllCandidates,
-                                  llvm::makeArrayRef(Args, NumArgs));
+      CandidateSet->NoteCandidates(*this, OCD_AllCandidates,
+                                   llvm::makeArrayRef(Args, NumArgs));
 
       // We emitted an error for the unvailable/deleted function call but keep
       // the call in the AST.
@@ -9816,6 +9822,49 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
 
   // Overload resolution failed.
   return ExprError();
+
+}
+
+/// This version of BuildOverloadedCallExpr creates an OverloadCandidateSet
+/// first.
+ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
+                                         UnresolvedLookupExpr *ULE,
+                                         SourceLocation LParenLoc,
+                                         Expr **Args, unsigned NumArgs,
+                                         SourceLocation RParenLoc,
+                                         Expr *ExecConfig,
+                                         bool AllowTypoCorrection,
+                                         bool InRangeExpr) {
+#ifndef NDEBUG
+  if (ULE->requiresADL()) {
+    // To do ADL, we must have found an unqualified name.
+    assert(!ULE->getQualifier() && "qualified name with ADL");
+
+    // We don't perform ADL for implicit declarations of builtins.
+    // Verify that this was correctly set up.
+    FunctionDecl *F;
+    if (ULE->decls_begin() + 1 == ULE->decls_end() &&
+        (F = dyn_cast<FunctionDecl>(*ULE->decls_begin())) &&
+        F->getBuiltinID() && F->isImplicit())
+      llvm_unreachable("performing ADL for builtin");
+
+    // We don't perform ADL in C.
+    assert(getLangOpts().CPlusPlus && "ADL enabled in C");
+  } else
+    assert(!ULE->isStdAssociatedNamespace() &&
+           "std is associated namespace but not doing ADL");
+#endif
+  
+  OverloadCandidateSet CandidateSet(Fn->getExprLoc());
+  ExprResult result;
+  if (buildOverloadedCallSet(S, Fn, ULE, Args, NumArgs, LParenLoc,
+                             &CandidateSet, &result))
+    return result;
+
+  return BuildOverloadedCallExpr(S, Fn, ULE, LParenLoc, Args, NumArgs,
+                                 RParenLoc, ExecConfig, &CandidateSet,
+                                 AllowTypoCorrection, InRangeExpr);
+
 }
 
 static bool IsOverloaded(const UnresolvedSetImpl &Functions) {
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 9be1d34..18bcf89 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -1578,18 +1578,35 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E,
     << BEF << IsTemplate << Description << E->getType();
 }
 
+
+enum ForRangeStatus {
+  FRS_Success,
+  FRS_InvalidMemberFunction,
+  FRS_NoOverload,
+  FRS_DeletedOrAmbiguous,
+  FRS_NoViableFunction,
+  FRS_InvalidIteratorType,
+  FRS_BeginEndMismatch
+};
+
 /// Build a call to 'begin' or 'end' for a C++0x for-range statement. If the
 /// given LookupResult is non-empty, it is assumed to describe a member which
 /// will be invoked. Otherwise, the function will be found via argument
 /// dependent lookup.
-static ExprResult BuildForRangeBeginEndCall(Sema &SemaRef, Scope *S,
-                                            SourceLocation Loc,
-                                            VarDecl *Decl,
-                                            BeginEndFunction BEF,
-                                            const DeclarationNameInfo &NameInfo,
-                                            LookupResult &MemberLookup,
-                                            Expr *Range) {
-  ExprResult CallExpr;
+/// CallExpr is set and FRS_Success returned on success, otherwise some
+/// non-success value is returned.
+static ForRangeStatus
+BuildForRangeBeginEndCall(Sema &SemaRef, Scope *S,
+                          SourceLocation Loc,
+                          SourceLocation RangeLoc,
+                          VarDecl *Decl,
+                          BeginEndFunction BEF,
+                          const DeclarationNameInfo &NameInfo,
+                          LookupResult &MemberLookup,
+                          OverloadCandidateSet *CandidateSet,
+                          Expr *Range,
+                          ExprResult *CallExpr) {
+  CandidateSet->clear();
   if (!MemberLookup.empty()) {
     ExprResult MemberRef =
       SemaRef.BuildMemberReferenceExpr(Range, Range->getType(), Loc,
@@ -1598,12 +1615,16 @@ static ExprResult BuildForRangeBeginEndCall(Sema &SemaRef, Scope *S,
                                        /*FirstQualifierInScope=*/0,
                                        MemberLookup,
                                        /*TemplateArgs=*/0);
-    if (MemberRef.isInvalid())
-      return ExprError();
-    CallExpr = SemaRef.ActOnCallExpr(S, MemberRef.get(), Loc, MultiExprArg(),
+    if (MemberRef.isInvalid()) {
+      *CallExpr = ExprError();
+      return FRS_InvalidMemberFunction;
+    }
+    *CallExpr = SemaRef.ActOnCallExpr(S, MemberRef.get(), Loc, MultiExprArg(),
                                      Loc, 0);
-    if (CallExpr.isInvalid())
-      return ExprError();
+    if (CallExpr->isInvalid()) {
+      *CallExpr = ExprError();
+      return FRS_InvalidMemberFunction;
+    }
   } else {
     UnresolvedSet<0> FoundNames;
     // C++0x [stmt.ranged]p1: For the purposes of this name lookup, namespace
@@ -1614,20 +1635,49 @@ static ExprResult BuildForRangeBeginEndCall(Sema &SemaRef, Scope *S,
                                    /*NeedsADL=*/true, /*Overloaded=*/false,
                                    FoundNames.begin(), FoundNames.end(),
                                    /*LookInStdNamespace=*/true);
-    CallExpr = SemaRef.BuildOverloadedCallExpr(S, Fn, Fn, Loc, &Range, 1, Loc,
-                                               0, /*AllowTypoCorrection=*/false);
-    if (CallExpr.isInvalid()) {
-      SemaRef.Diag(Range->getLocStart(), diag::note_for_range_type)
-        << Range->getType();
-      return ExprError();
+
+    SemaRef.buildOverloadedCallSet(S, Fn, Fn, &Range, 1, Loc, CandidateSet,
+                                   CallExpr);
+
+    OverloadCandidateSet::iterator Best;
+    if (CandidateSet->empty()) {
+      *CallExpr = ExprError();
+      return FRS_NoOverload;
+    } else {
+      switch (CandidateSet->BestViableFunction(SemaRef, Fn->getLocStart(),
+                                               Best)) {
+      case OR_Success:
+        *CallExpr =
+            SemaRef.BuildOverloadedCallExpr(S, Fn, Fn, Loc, &Range, 1, Loc, 0,
+                                            CandidateSet,
+                                            /*AllowTypoCorrection=*/false,
+                                            /*InRangeExpr=*/true);
+        if (CallExpr->isInvalid())
+          return FRS_NoOverload;
+        break;
+
+      case OR_No_Viable_Function:
+        *CallExpr = ExprError();
+        return FRS_NoViableFunction;
+
+      case OR_Deleted:
+      case OR_Ambiguous:
+        // Here we want to defer to the diagnostics in BuildOverloadedCallExpr.
+        // The return value doesn't matter, since we're returning an ExprError()
+        // anyway.
+        SemaRef.BuildOverloadedCallExpr(S, Fn, Fn, Loc, &Range, 1, Loc, 0,
+                                        /*AllowTypoCorrection=*/false,
+                                        /*InRangeExpr=*/true);
+        *CallExpr = ExprError();
+        return FRS_DeletedOrAmbiguous;
+      }
     }
+
   }
-  if (FinishForRangeVarDecl(SemaRef, Decl, CallExpr.get(), Loc,
-                            diag::err_for_range_iter_deduction_failure)) {
-    NoteForRangeBeginEndFunction(SemaRef, CallExpr.get(), BEF);
-    return ExprError();
-  }
-  return CallExpr;
+  if (FinishForRangeVarDecl(SemaRef, Decl, CallExpr->get(), Loc,
+                            diag::err_for_range_iter_deduction_failure))
+    return FRS_InvalidIteratorType;
+  return FRS_Success;
 }
 
 }
@@ -1700,6 +1750,129 @@ Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
                               RParenLoc);
 }
 
+// Emit the diagnostic appropriate to the given ForRangeStatus. The other
+// parameters are used for supplementary information.
+static void ForRangeDiagnostics(Sema &SemaRef, ForRangeStatus st, Expr *Range,
+                                const ExprResult &CallExpr,
+                                SourceLocation RangeLoc,
+                                OverloadCandidateSet *CandidateSet,
+                                BeginEndFunction BEF) {
+  switch (st) {
+  case FRS_Success:
+    // It worked; nothing to do.
+    break;
+
+  case FRS_InvalidMemberFunction:
+    SemaRef.Diag(Range->getLocStart(), diag::err_for_range_invalid)
+      << RangeLoc << Range->getType();
+    SemaRef.Diag(Range->getLocStart(), diag::note_for_range_adl_failure)
+      << BEF << Range->getType();
+    break;
+
+  case FRS_NoOverload:
+    SemaRef.Diag(Range->getLocStart(), diag::err_for_range_invalid)
+      << RangeLoc << Range->getType();
+    SemaRef.Diag(Range->getLocStart(), diag::note_for_range_adl_failure)
+      << BEF << Range->getType();
+    break;
+
+  case FRS_DeletedOrAmbiguous:
+    // Some of the diagnostics are emitted in BuildOverloadedCallExpr, so we
+    // just add these.
+    SemaRef.Diag(Range->getLocStart(), diag::err_for_range_invalid)
+      << RangeLoc << Range->getType();
+    SemaRef.Diag(Range->getLocStart(), diag::note_for_range_adl_failure)
+      << BEF << Range->getType();
+    break;
+
+  case FRS_NoViableFunction:
+    SemaRef.Diag(Range->getLocStart(), diag::err_for_range_invalid)
+        << RangeLoc << Range->getType();
+    CandidateSet->NoteCandidates(SemaRef, OCD_AllCandidates,
+                                 llvm::makeArrayRef(&Range, /*NumArgs=*/1));
+    SemaRef.Diag(Range->getLocStart(), diag::note_for_range_adl_failure)
+      << BEF << Range->getType();
+    break;
+
+  case FRS_InvalidIteratorType:
+    NoteForRangeBeginEndFunction(SemaRef, CallExpr.get(), BEF);
+    break;
+
+  case FRS_BeginEndMismatch:
+    SemaRef.Diag(RangeLoc, diag::err_for_range_member_begin_end_mismatch)
+        << Range->getType() << BEF;
+    break;
+  }
+}
+
+/// \brief Create the initialization, compare, and increment steps for
+/// the range-based for loop expression.
+/// This function does not handle array-based for loops,
+/// which are created in Sema::BuildCXXForRangeStmt.
+///
+/// \returns a ForRangeStatus indicating success or what kind of error occurred.
+/// BeginExpr and EndExpr are set and FRS_Success is returned on success;
+/// CandidateSet and BEF are set and some non-success value is returned on
+/// failure.
+static ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S,
+                                            Expr *BeginRange, Expr *EndRange,
+                                            QualType RangeType,
+                                            VarDecl *BeginVar,
+                                            VarDecl *EndVar,
+                                            SourceLocation ColonLoc,
+                                            OverloadCandidateSet *CandidateSet,
+                                            ExprResult *BeginExpr,
+                                            ExprResult *EndExpr,
+                                            BeginEndFunction *BEF) {
+  DeclarationNameInfo BeginNameInfo(
+      &SemaRef.PP.getIdentifierTable().get("begin"), ColonLoc);
+  DeclarationNameInfo EndNameInfo(&SemaRef.PP.getIdentifierTable().get("end"),
+                                  ColonLoc);
+
+  LookupResult BeginMemberLookup(SemaRef, BeginNameInfo,
+                                 Sema::LookupMemberName);
+  LookupResult EndMemberLookup(SemaRef, EndNameInfo, Sema::LookupMemberName);
+
+  if (CXXRecordDecl *D = RangeType->getAsCXXRecordDecl()) {
+    // - if _RangeT is a class type, the unqualified-ids begin and end are
+    //   looked up in the scope of class _RangeT as if by class member access
+    //   lookup (3.4.5), and if either (or both) finds at least one
+    //   declaration, begin-expr and end-expr are __range.begin() and
+    //   __range.end(), respectively;
+    SemaRef.LookupQualifiedName(BeginMemberLookup, D);
+    SemaRef.LookupQualifiedName(EndMemberLookup, D);
+
+    if (BeginMemberLookup.empty() != EndMemberLookup.empty()) {
+      *BEF = BeginMemberLookup.empty() ? BEF_end : BEF_begin;
+      return FRS_BeginEndMismatch;
+    }
+  } else {
+    // - otherwise, begin-expr and end-expr are begin(__range) and
+    //   end(__range), respectively, where begin and end are looked up with
+    //   argument-dependent lookup (3.4.2). For the purposes of this name
+    //   lookup, namespace std is an associated namespace.
+
+  }
+
+  *BEF = BEF_begin;
+  ForRangeStatus
+      RangeStatus = BuildForRangeBeginEndCall(SemaRef, S, ColonLoc,
+                                              ColonLoc, BeginVar,
+                                              BEF_begin, BeginNameInfo,
+                                              BeginMemberLookup,
+                                              CandidateSet,
+                                              BeginRange, BeginExpr);
+  if (RangeStatus != FRS_Success)
+    return st;
+
+  *BEF = BEF_end;
+  RangeStatus = BuildForRangeBeginEndCall(SemaRef, S, ColonLoc, ColonLoc,
+                                          EndVar, BEF_end, EndNameInfo,
+                                          EndMemberLookup, CandidateSet,
+                                          EndRange, EndExpr);
+  return RangeStatus;
+}
+
 /// BuildCXXForRangeStmt - Build or instantiate a C++0x for-range statement.
 StmtResult
 Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc,
@@ -1791,49 +1964,58 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc,
         return StmtError();
       }
     } else {
-      DeclarationNameInfo BeginNameInfo(&PP.getIdentifierTable().get("begin"),
-                                        ColonLoc);
-      DeclarationNameInfo EndNameInfo(&PP.getIdentifierTable().get("end"),
-                                      ColonLoc);
-
-      LookupResult BeginMemberLookup(*this, BeginNameInfo, LookupMemberName);
-      LookupResult EndMemberLookup(*this, EndNameInfo, LookupMemberName);
-
-      if (CXXRecordDecl *D = RangeType->getAsCXXRecordDecl()) {
-        // - if _RangeT is a class type, the unqualified-ids begin and end are
-        //   looked up in the scope of class _RangeT as if by class member access
-        //   lookup (3.4.5), and if either (or both) finds at least one
-        //   declaration, begin-expr and end-expr are __range.begin() and
-        //   __range.end(), respectively;
-        LookupQualifiedName(BeginMemberLookup, D);
-        LookupQualifiedName(EndMemberLookup, D);
-
-        if (BeginMemberLookup.empty() != EndMemberLookup.empty()) {
-          Diag(ColonLoc, diag::err_for_range_member_begin_end_mismatch)
-            << RangeType << BeginMemberLookup.empty();
+      Expr *BeginRange = BeginRangeRef.get();
+      Expr *EndRange = EndRangeRef.get();
+      OverloadCandidateSet CandidateSet(RangeLoc);
+      BeginEndFunction BEFFailure;
+      ForRangeStatus RangeStatus =
+          BuildNonArrayForRange(*this, S, BeginRange, EndRange, RangeType,
+                                BeginVar, EndVar, ColonLoc, &CandidateSet,
+                                &BeginExpr, &EndExpr, &BEFFailure);
+
+      // Attempt to dereference pointers and try again.
+      if (RangeStatus != FRS_Success) {
+        if (!RangeType->isPointerType()) {
+          const ExprResult & TheCall = BEFFailure ? EndExpr: BeginExpr;
+          ForRangeDiagnostics(*this, RangeStatus,
+                              BEFFailure ? EndRange : BeginRange,
+                              TheCall, RangeLoc, &CandidateSet, BEFFailure);
           return StmtError();
         }
-      } else {
-        // - otherwise, begin-expr and end-expr are begin(__range) and
-        //   end(__range), respectively, where begin and end are looked up with
-        //   argument-dependent lookup (3.4.2). For the purposes of this name
-        //   lookup, namespace std is an associated namespace.
+        BeginRange =
+            new (Context) UnaryOperator(BeginRange, UO_Deref,
+                                        RangeType->getPointeeType(),
+                                        VK_LValue, OK_Ordinary, RangeLoc);
+        EndRange =
+            new (Context) UnaryOperator(EndRange, UO_Deref,
+                                        RangeType->getPointeeType(),
+                                        VK_LValue, OK_Ordinary, RangeLoc);
+
+        BeginEndFunction DerefBEF;
+        ForRangeStatus DerefStatus
+            = BuildNonArrayForRange(*this, S, BeginRange, EndRange,
+                                    BeginRange->getType(),
+                                    BeginVar, EndVar,
+                                    ColonLoc, &CandidateSet,
+                                    &BeginExpr, &EndExpr, &DerefBEF);
+        if (DerefStatus != FRS_Success) {
+          const ExprResult & TheCall = BEFFailure ? EndExpr: BeginExpr;
+          ForRangeDiagnostics(*this, RangeStatus,
+                              DerefBEF ? EndRangeRef.get(): BeginRangeRef.get(),
+                              TheCall, RangeLoc, &CandidateSet, BEFFailure);
+          return StmtError();
+        } else {
+          // The attempt to dereference would likely succeed.
+          Diag(RangeLoc, diag::err_for_range_dereference)
+              << RangeLoc << RangeType
+              << FixItHint::CreateInsertion(RangeLoc, "*");
+        }
       }
-
-      BeginExpr = BuildForRangeBeginEndCall(*this, S, ColonLoc, BeginVar,
-                                            BEF_begin, BeginNameInfo,
-                                            BeginMemberLookup,
-                                            BeginRangeRef.get());
-      if (BeginExpr.isInvalid())
-        return StmtError();
-
-      EndExpr = BuildForRangeBeginEndCall(*this, S, ColonLoc, EndVar,
-                                          BEF_end, EndNameInfo,
-                                          EndMemberLookup, EndRangeRef.get());
-      if (EndExpr.isInvalid())
-        return StmtError();
     }
 
+    assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() &&
+           "invalid range expression in for loop");
+
     // C++0x [decl.spec.auto]p6: BeginType and EndType must be the same.
     QualType BeginType = BeginVar->getType(), EndType = EndVar->getType();
     if (!Context.hasSameType(BeginType, EndType)) {
diff --git a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
index 96bb472..e5809f0 100644
--- a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
+++ b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
@@ -3,20 +3,21 @@
 struct pr12960 {
   int begin;
   void foo(int x) {
-    for (int& it : x) { // expected-error {{use of undeclared identifier 'begin'}} expected-note {{range has type 'int'}}
+    for (int& it : x) { //expected-error{{invalid range expression}}\
+      expected-note{{no viable 'begin' function for range of type 'int'}}
     }
   }
 };
 
 namespace std {
   template<typename T>
-    auto begin(T &&t) -> decltype(t.begin()) { return t.begin(); } // expected-note 4{{ignored: substitution failure}}
+    auto begin(T &&t) -> decltype(t.begin()) { return t.begin(); } // expected-note 3{{ignored: substitution failure}}
   template<typename T>
     auto end(T &&t) -> decltype(t.end()) { return t.end(); } // expected-note {{candidate template ignored: substitution failure [with T = }}
 
   template<typename T>
     auto begin(T &&t) -> decltype(t.alt_begin()) { return t.alt_begin(); } // expected-note {{selected 'begin' template [with T = }} \
-                                                                              expected-note 4{{candidate template ignored: substitution failure [with T = }}
+                                                                              expected-note 3{{candidate template ignored: substitution failure [with T = }}
   template<typename T>
     auto end(T &&t) -> decltype(t.alt_end()) { return t.alt_end(); } // expected-note {{candidate template ignored: substitution failure [with T = }}
 
@@ -116,9 +117,11 @@ void g() {
   struct NoEndADL {
     null_t alt_begin();
   };
-  for (auto u : NoBeginADL()) { // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type 'NoBeginADL'}}
+  for (auto u : NoBeginADL()) {// expected-error{{invalid range expression of type 'NoBeginADL'}} \
+    expected-note{{no viable 'begin' function for range of type 'NoBeginADL'}}
   }
-  for (auto u : NoEndADL()) { // expected-error {{no matching function for call to 'end'}} expected-note {{range has type 'NoEndADL'}}
+  for (auto u : NoEndADL()) { // expected-error{{invalid range expression of type 'NoEndADL'}} \
+    expected-note{{no viable 'end' function for range of type 'NoEndADL'}}
   }
 
   struct NoBegin {
@@ -156,8 +159,8 @@ void g() {
   for (int n : NoCopy()) { // ok
   }
 
-  for (int n : 42) { // expected-error {{no matching function for call to 'begin'}} \
-                        expected-note {{range has type 'int'}}
+  for (int n : 42) { // expected-error{{invalid range expression of type 'int'}} \
+    expected-note{{no viable 'begin' function for range of type 'int'}}
   }
 
   for (auto a : *also_incomplete) { // expected-error {{cannot use incomplete type 'struct Incomplete' as a range}}
@@ -179,9 +182,10 @@ template void h<A(&)[13], int>(A(&)[13]); // expected-note {{requested here}}
 
 template<typename T>
 void i(T t) {
-  for (auto u : t) { // expected-error {{no matching function for call to 'begin'}} \
-                        expected-error {{member function 'begin' not viable}} \
-                        expected-note {{range has type}}
+  for (auto u : t) { // expected-error {{invalid range expression of type 'A *'; did you mean to dereference it with '*'?}} \
+                        expected-error {{member function 'begin' not viable}}\
+                        expected-error{{invalid range expression of type 'const A'}}\
+                        expected-note{{no viable 'begin' function for range of type 'const A'}}
   }
 }
 template void i<A[13]>(A*); // expected-note {{requested here}}
@@ -204,7 +208,8 @@ void end(VoidBeginADL);
 void j() {
   for (auto u : NS::ADL()) {
   }
-  for (auto u : NS::NoADL()) { // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}}
+  for (auto u : NS::NoADL()) { // expected-error{{invalid range expression of type 'NS::NoADL'}}\
+    expected-note {{no viable 'begin' function for range of type 'NS::NoADL'}}
   }
   for (auto a : VoidBeginADL()) { // expected-error {{cannot use type 'void' as an iterator}}
   }
@@ -215,4 +220,3 @@ void example() {
   for (int &x : array)
     x *= 2;
 }
-
diff --git a/test/SemaCXX/for-range-no-std.cpp b/test/SemaCXX/for-range-no-std.cpp
index fa42ca4..8f3213b 100644
--- a/test/SemaCXX/for-range-no-std.cpp
+++ b/test/SemaCXX/for-range-no-std.cpp
@@ -31,10 +31,11 @@ NS::iter end(NS::NoADL);
 void f() {
   int a[] = {1, 2, 3};
   for (auto b : S()) {} // ok
-  for (auto b : T()) {} // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}}
+  for (auto b : T()) {} // expected-error {{invalid range expression of type 'T'}} expected-note {{no viable 'begin' function for range of type 'T'}}
   for (auto b : a) {} // ok
   for (int b : NS::ADL()) {} // ok
-  for (int b : NS::NoADL()) {} // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}}
+  for (int b : NS::NoADL()) {} // expected-error {{invalid range expression of type 'NS::NoADL'}} \
+  expected-note {{no viable 'begin' function for range of type 'NS::NoADL'}}
 }
 
 void PR11601() {
diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp
index 893f08a..f8bb8d0 100644
--- a/test/SemaCXX/typo-correction.cpp
+++ b/test/SemaCXX/typo-correction.cpp
@@ -155,7 +155,8 @@ void Test3() {
 struct R {};
 bool begun(R);
 void RangeTest() {
-  for (auto b : R()) {} // expected-error {{use of undeclared identifier 'begin'}} expected-note {{range has type}}
+  for (auto b : R()) {} // expected-error {{invalid range expression of type 'R'}}\
+  expected-note {{no viable 'begin' function for range of type 'R'}}
 }
 
 // PR 12019 - Avoid infinite mutual recursion in DiagnoseInvalidRedeclaration
-- 
1.7.7.3

