From: Richard Smith Date: Wed, 9 May 2012 05:17:00 +0000 (+0000) Subject: A little tweak to the SFINAE condition reporting. Don't say: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4493c0aa8add05b2fc534b0d8970c4b5480fad3b;p=clang 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' 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 5a473e0454..2aec5dba16 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2024,6 +2024,8 @@ def note_ovl_candidate_underqualified : Note< "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 @@ def note_explicit_instantiation_definition_here : Note< // 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 f065d38d92..b99a638bda 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -8263,13 +8263,23 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, 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 bde80fd5b1..7faf80edfc 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -6923,6 +6923,42 @@ Sema::ActOnTypenameType(Scope *S, } +/// 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(&EnableIfTy); + if (!EnableIfTSTLoc || EnableIfTSTLoc->getNumArgs() == 0) + return false; + const TemplateSpecializationType *EnableIfTST = + cast(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 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, 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 23ca1d47f1..dc6d2a51ec 100644 --- a/test/SemaTemplate/overload-candidates.cpp +++ b/test/SemaTemplate/overload-candidates.cpp @@ -40,3 +40,25 @@ struct X { void test_X_min(X x) { (void)x.min(1, 2l); // expected-error{{no matching member function for call to 'min'}} } + +namespace boost { + template struct enable_if {}; + template struct enable_if { typedef T type; }; +} +template typename boost::enable_if::type if_size_4(); // expected-note{{candidate template ignored: disabled by 'enable_if' [with T = char]}} +int k = if_size_4(); // expected-error{{no matching function}} + +namespace llvm { + template struct enable_if : boost::enable_if {}; +} +template struct is_int { enum { value = false }; }; +template<> struct is_int { enum { value = true }; }; +template typename llvm::enable_if >::type if_int(); // expected-note{{candidate template ignored: disabled by 'enable_if' [with T = char]}} +void test_if_int() { + if_int(); // expected-error{{no matching function}} +} + +template struct NonTemplateFunction { + typename boost::enable_if::type f(); // expected-error{{no type named 'type' in 'boost::enable_if'; 'enable_if' cannot be used to disable this declaration}} +}; +NonTemplateFunction NTFC; // expected-note{{here}}