diff options
| author | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-05-09 05:17:00 +0000 |
|---|---|---|
| committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-05-09 05:17:00 +0000 |
| commit | 6f8d2c6c9c3451effdf075a7034bbe77045bfeba (patch) | |
| tree | 9f37148d4c839abe13285781974985cee8bcf443 | |
| parent | 9133c34a2a35c26692c8387763b517f808117c6f (diff) | |
| download | bcm5719-llvm-6f8d2c6c9c3451effdf075a7034bbe77045bfeba.tar.gz bcm5719-llvm-6f8d2c6c9c3451effdf075a7034bbe77045bfeba.zip | |
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'.
llvm-svn: 156463
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 14 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 48 | ||||
| -rw-r--r-- | clang/test/SemaTemplate/overload-candidates.cpp | 22 |
4 files changed, 86 insertions, 3 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5a473e0454b..2aec5dba16b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/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/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index f065d38d92c..b99a638bdac 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/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/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index bde80fd5b18..7faf80edfce 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/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<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 @@ 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/clang/test/SemaTemplate/overload-candidates.cpp b/clang/test/SemaTemplate/overload-candidates.cpp index 23ca1d47f1a..dc6d2a51ec2 100644 --- a/clang/test/SemaTemplate/overload-candidates.cpp +++ b/clang/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<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}} |

