A little tweak to the SFINAE condition reporting. Don't say:

  candidate template ignored: substitution failed [with T = int]: no type named 'type' in 'std::enable_if<false, void>'

Instead, just say:

  candidate template ignored: disabled by 'enable_if' [with T = int]

... and point at the enable_if condition which (we assume) failed.

This is applied to all cases where the user writes 'typename enable_if<...>::type' (optionally prefixed with a nested name specifier), and 'enable_if<...>' names a complete class type which does not have a member named 'type', and this results in a candidate function being ignored in a SFINAE context. Thus it catches 'std::enable_if', 'std::__1::enable_if', 'boost::enable_if' and 'llvm::enable_if'.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156463 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 5a473e0..2aec5db 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2024,6 +2024,8 @@
     "make %2 equal %1">;
 def note_ovl_candidate_substitution_failure : Note<
     "candidate template ignored: substitution failure%0%1">;
+def note_ovl_candidate_disabled_by_enable_if : Note<
+    "candidate template ignored: disabled by %0%1">;
     
 // Note that we don't treat templates differently for this diagnostic.
 def note_ovl_candidate_arity : Note<"candidate "
@@ -2732,6 +2734,9 @@
   
 // C++ typename-specifiers
 def err_typename_nested_not_found : Error<"no type named %0 in %1">;
+def err_typename_nested_not_found_enable_if : Error<
+  "no type named 'type' in %0; 'enable_if' cannot be used to disable "
+  "this declaration">;
 def err_typename_nested_not_type : Error<
     "typename specifier refers to non-type member %0 in %1">;
 def note_typename_refers_here : Note<
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index f065d38..b99a638 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -8263,13 +8263,23 @@
           Fn->getDescribedFunctionTemplate()->getTemplateParameters(), *Args);
     }
 
+    // If this candidate was disabled by enable_if, say so.
+    PartialDiagnosticAt *PDiag = Cand->DeductionFailure.getSFINAEDiagnostic();
+    if (PDiag && PDiag->second.getDiagID() ==
+          diag::err_typename_nested_not_found_enable_if) {
+      // FIXME: Use the source range of the condition, and the fully-qualified
+      //        name of the enable_if template. These are both present in PDiag.
+      S.Diag(PDiag->first, diag::note_ovl_candidate_disabled_by_enable_if)
+        << "'enable_if'" << TemplateArgString;
+      return;
+    }
+
     // Format the SFINAE diagnostic into the argument string.
     // FIXME: Add a general mechanism to include a PartialDiagnostic *'s
     //        formatted message in another diagnostic.
     llvm::SmallString<128> SFINAEArgString;
     SourceRange R;
-    if (PartialDiagnosticAt *PDiag =
-          Cand->DeductionFailure.getSFINAEDiagnostic()) {
+    if (PDiag) {
       SFINAEArgString = ": ";
       R = SourceRange(PDiag->first, PDiag->first);
       PDiag->second.EmitToString(S.getDiagnostics(), SFINAEArgString);
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index bde80fd..7faf80e 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -6923,6 +6923,42 @@
 }
 
 
+/// Determine whether this failed name lookup should be treated as being
+/// disabled by a usage of std::enable_if.
+static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II,
+                       SourceRange &CondRange) {
+  // We must be looking for a ::type...
+  if (!II.isStr("type"))
+    return false;
+
+  // ... within an explicitly-written template specialization...
+  if (!NNS || !NNS.getNestedNameSpecifier()->getAsType())
+    return false;
+  TypeLoc EnableIfTy = NNS.getTypeLoc();
+  TemplateSpecializationTypeLoc *EnableIfTSTLoc =
+    dyn_cast<TemplateSpecializationTypeLoc>(&EnableIfTy);
+  if (!EnableIfTSTLoc || EnableIfTSTLoc->getNumArgs() == 0)
+    return false;
+  const TemplateSpecializationType *EnableIfTST =
+    cast<TemplateSpecializationType>(EnableIfTSTLoc->getTypePtr());
+
+  // ... which names a complete class template declaration...
+  const TemplateDecl *EnableIfDecl =
+    EnableIfTST->getTemplateName().getAsTemplateDecl();
+  if (!EnableIfDecl || EnableIfTST->isIncompleteType())
+    return false;
+
+  // ... called "enable_if".
+  const IdentifierInfo *EnableIfII =
+    EnableIfDecl->getDeclName().getAsIdentifierInfo();
+  if (!EnableIfII || !EnableIfII->isStr("enable_if"))
+    return false;
+
+  // Assume the first template argument is the condition.
+  CondRange = EnableIfTSTLoc->getArgLoc(0).getSourceRange();
+  return true;
+}
+
 /// \brief Build the type that describes a C++ typename specifier,
 /// e.g., "typename T::type".
 QualType
@@ -6959,9 +6995,19 @@
   unsigned DiagID = 0;
   Decl *Referenced = 0;
   switch (Result.getResultKind()) {
-  case LookupResult::NotFound:
+  case LookupResult::NotFound: {
+    // If we're looking up 'type' within a template named 'enable_if', produce
+    // a more specific diagnostic.
+    SourceRange CondRange;
+    if (isEnableIf(QualifierLoc, II, CondRange)) {
+      Diag(CondRange.getBegin(), diag::err_typename_nested_not_found_enable_if)
+        << Ctx << CondRange;
+      return QualType();
+    }
+
     DiagID = diag::err_typename_nested_not_found;
     break;
+  }
 
   case LookupResult::FoundUnresolvedValue: {
     // We found a using declaration that is a value. Most likely, the using
diff --git a/test/SemaTemplate/overload-candidates.cpp b/test/SemaTemplate/overload-candidates.cpp
index 23ca1d4..dc6d2a5 100644
--- a/test/SemaTemplate/overload-candidates.cpp
+++ b/test/SemaTemplate/overload-candidates.cpp
@@ -40,3 +40,25 @@
 void test_X_min(X x) {
   (void)x.min(1, 2l); // expected-error{{no matching member function for call to 'min'}}
 }
+
+namespace boost {
+  template<bool, typename = void> struct enable_if {};
+  template<typename T> struct enable_if<true, T> { typedef T type; };
+}
+template<typename T> typename boost::enable_if<sizeof(T) == 4, int>::type if_size_4(); // expected-note{{candidate template ignored: disabled by 'enable_if' [with T = char]}}
+int k = if_size_4<char>(); // expected-error{{no matching function}}
+
+namespace llvm {
+  template<typename Cond, typename T = void> struct enable_if : boost::enable_if<Cond::value, T> {};
+}
+template<typename T> struct is_int { enum { value = false }; };
+template<> struct is_int<int> { enum { value = true }; };
+template<typename T> typename llvm::enable_if<is_int<T> >::type if_int(); // expected-note{{candidate template ignored: disabled by 'enable_if' [with T = char]}}
+void test_if_int() {
+  if_int<char>(); // expected-error{{no matching function}}
+}
+
+template<typename T> struct NonTemplateFunction {
+  typename boost::enable_if<sizeof(T) == 4, int>::type f(); // expected-error{{no type named 'type' in 'boost::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration}}
+};
+NonTemplateFunction<char> NTFC; // expected-note{{here}}