From a933319ebf754396623165f9dc0a29c2a48879f5 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Sat, 8 May 2010 17:41:32 +0000 Subject: [PATCH] When printing a non-viable overload candidate that failed due to conflicting deduced template argument values, give a more specific reason along with those values, e.g., test/SemaTemplate/overload-candidates.cpp:4:10: note: candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'long') const T& min(const T&, const T&); ^ git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@103339 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/TemplateBase.h | 6 +- include/clang/Basic/DiagnosticSemaKinds.td | 5 +- lib/AST/TemplateBase.cpp | 40 ++++ lib/Sema/SemaOverload.cpp | 192 +++++++++++++++++- lib/Sema/SemaOverload.h | 29 ++- .../temp.deduct/temp.deduct.call/basic.cpp | 3 +- .../temp.deduct/temp.deduct.call/p6.cpp | 2 +- test/SemaTemplate/overload-candidates.cpp | 8 + 8 files changed, 265 insertions(+), 20 deletions(-) create mode 100644 test/SemaTemplate/overload-candidates.cpp diff --git a/include/clang/AST/TemplateBase.h b/include/clang/AST/TemplateBase.h index 50a100c6af..8b38001bd1 100644 --- a/include/clang/AST/TemplateBase.h +++ b/include/clang/AST/TemplateBase.h @@ -28,6 +28,7 @@ namespace llvm { namespace clang { class Decl; +class DiagnosticBuilder; class Expr; class TypeSourceInfo; @@ -473,6 +474,9 @@ public: } }; -} +const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, + const TemplateArgument &Arg); + +} // end namespace clang #endif diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 89f892cec1..93364845ec 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1060,7 +1060,10 @@ def note_ovl_candidate_bad_deduction : Note< "candidate template ignored: failed template argument deduction">; def note_ovl_candidate_incomplete_deduction : Note<"candidate template ignored: " "couldn't infer template argument %0">; - +def note_ovl_candidate_inconsistent_deduction : Note< + "candidate template ignored: deduced conflicting %select{types|values|" + "templates}0 for parameter %1 (%2 vs. %3)">; + // Note that we don't treat templates differently for this diagnostic. def note_ovl_candidate_arity : Note<"candidate " "%select{function|function|constructor|function|function|constructor|" diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp index e9b1725641..0bf9f2fb7c 100644 --- a/lib/AST/TemplateBase.cpp +++ b/lib/AST/TemplateBase.cpp @@ -18,6 +18,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/TypeLoc.h" +#include "clang/Basic/Diagnostic.h" using namespace clang; @@ -119,3 +120,42 @@ SourceRange TemplateArgumentLoc::getSourceRange() const { // Silence bonus gcc warning. return SourceRange(); } + +const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB, + const TemplateArgument &Arg) { + switch (Arg.getKind()) { + case TemplateArgument::Null: + return DB; + + case TemplateArgument::Type: + return DB << Arg.getAsType(); + + case TemplateArgument::Declaration: + return DB << Arg.getAsDecl(); + + case TemplateArgument::Integral: + return DB << Arg.getAsIntegral()->toString(10); + + case TemplateArgument::Template: + return DB << Arg.getAsTemplate(); + + case TemplateArgument::Expression: { + // This shouldn't actually ever happen, so it's okay that we're + // regurgitating an expression here. + // FIXME: We're guessing at LangOptions! + llvm::SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + LangOptions LangOpts; + LangOpts.CPlusPlus = true; + PrintingPolicy Policy(LangOpts); + Arg.getAsExpr()->printPretty(OS, 0, Policy); + return DB << OS.str(); + } + + case TemplateArgument::Pack: + // FIXME: Format arguments in a list! + return DB << ""; + } + + return DB; +} diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index dec9854c72..c08095fae4 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -275,7 +275,166 @@ AmbiguousConversionSequence::copyFrom(const AmbiguousConversionSequence &O) { new (&conversions()) ConversionSet(O.conversions()); } +namespace { + // Structure used by OverloadCandidate::DeductionFailureInfo to store + // template parameter and template argument information. + struct DFIParamWithArguments { + TemplateParameter Param; + TemplateArgument FirstArg; + TemplateArgument SecondArg; + }; +} + +/// \brief Convert from Sema's representation of template deduction information +/// to the form used in overload-candidate information. +OverloadCandidate::DeductionFailureInfo +static MakeDeductionFailureInfo(Sema::TemplateDeductionResult TDK, + const Sema::TemplateDeductionInfo &Info) { + OverloadCandidate::DeductionFailureInfo Result; + Result.Result = static_cast(TDK); + Result.Data = 0; + switch (TDK) { + case Sema::TDK_Success: + case Sema::TDK_InstantiationDepth: + break; + + case Sema::TDK_Incomplete: + Result.Data = Info.Param.getOpaqueValue(); + break; + + // Unhandled + case Sema::TDK_Inconsistent: + case Sema::TDK_InconsistentQuals: { + DFIParamWithArguments *Saved = new DFIParamWithArguments; + Saved->Param = Info.Param; + Saved->FirstArg = Info.FirstArg; + Saved->SecondArg = Info.SecondArg; + Result.Data = Saved; + break; + } + + case Sema::TDK_SubstitutionFailure: + case Sema::TDK_NonDeducedMismatch: + case Sema::TDK_TooManyArguments: + case Sema::TDK_TooFewArguments: + case Sema::TDK_InvalidExplicitArguments: + case Sema::TDK_FailedOverloadResolution: + break; + } + + return Result; +} + +void OverloadCandidate::DeductionFailureInfo::Destroy() { + switch (static_cast(Result)) { + case Sema::TDK_Success: + case Sema::TDK_InstantiationDepth: + case Sema::TDK_Incomplete: + break; + + // Unhandled + case Sema::TDK_Inconsistent: + case Sema::TDK_InconsistentQuals: + delete static_cast(Data); + Data = 0; + break; + + case Sema::TDK_SubstitutionFailure: + case Sema::TDK_NonDeducedMismatch: + case Sema::TDK_TooManyArguments: + case Sema::TDK_TooFewArguments: + case Sema::TDK_InvalidExplicitArguments: + case Sema::TDK_FailedOverloadResolution: + break; + } +} + +TemplateParameter +OverloadCandidate::DeductionFailureInfo::getTemplateParameter() { + switch (static_cast(Result)) { + case Sema::TDK_Success: + case Sema::TDK_InstantiationDepth: + return TemplateParameter(); + + case Sema::TDK_Incomplete: + return TemplateParameter::getFromOpaqueValue(Data); + case Sema::TDK_Inconsistent: + case Sema::TDK_InconsistentQuals: + return static_cast(Data)->Param; + + // Unhandled + case Sema::TDK_SubstitutionFailure: + case Sema::TDK_NonDeducedMismatch: + case Sema::TDK_TooManyArguments: + case Sema::TDK_TooFewArguments: + case Sema::TDK_InvalidExplicitArguments: + case Sema::TDK_FailedOverloadResolution: + break; + } + + return TemplateParameter(); +} + +const TemplateArgument *OverloadCandidate::DeductionFailureInfo::getFirstArg() { + switch (static_cast(Result)) { + case Sema::TDK_Success: + case Sema::TDK_InstantiationDepth: + case Sema::TDK_Incomplete: + return 0; + + // Unhandled + case Sema::TDK_Inconsistent: + case Sema::TDK_InconsistentQuals: + return &static_cast(Data)->FirstArg; + + case Sema::TDK_SubstitutionFailure: + case Sema::TDK_NonDeducedMismatch: + case Sema::TDK_TooManyArguments: + case Sema::TDK_TooFewArguments: + case Sema::TDK_InvalidExplicitArguments: + case Sema::TDK_FailedOverloadResolution: + break; + } + + return 0; +} + +const TemplateArgument * +OverloadCandidate::DeductionFailureInfo::getSecondArg() { + switch (static_cast(Result)) { + case Sema::TDK_Success: + case Sema::TDK_InstantiationDepth: + case Sema::TDK_Incomplete: + return 0; + + // Unhandled + case Sema::TDK_Inconsistent: + case Sema::TDK_InconsistentQuals: + return &static_cast(Data)->SecondArg; + + case Sema::TDK_SubstitutionFailure: + case Sema::TDK_NonDeducedMismatch: + case Sema::TDK_TooManyArguments: + case Sema::TDK_TooFewArguments: + case Sema::TDK_InvalidExplicitArguments: + case Sema::TDK_FailedOverloadResolution: + break; + } + + return 0; +} + +void OverloadCandidateSet::clear() { + for (iterator C = begin(), CEnd = end(); C != CEnd; ++C) { + if (C->FailureKind == ovl_fail_bad_deduction) + C->DeductionFailure.Destroy(); + } + + inherited::clear(); + Functions.clear(); +} + // IsOverload - Determine whether the given New declaration is an // overload of the declarations in Old. This routine returns false if // New and Old cannot be overloaded, e.g., if New has the same @@ -3028,7 +3187,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, } } } - + /// \brief Add a C++ member function template as a candidate to the candidate /// set, using template argument deduction to produce an appropriate member /// function template specialization. @@ -3109,10 +3268,7 @@ Sema::AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate, Candidate.FailureKind = ovl_fail_bad_deduction; Candidate.IsSurrogate = false; Candidate.IgnoreObjectArgument = false; - - // TODO: record more information about failed template arguments - Candidate.DeductionFailure.Result = Result; - Candidate.DeductionFailure.TemplateParameter = Info.Param.getOpaqueValue(); + Candidate.DeductionFailure = MakeDeductionFailureInfo(Result, Info); return; } @@ -4929,9 +5085,7 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, Expr **Args, unsigned NumArgs) { FunctionDecl *Fn = Cand->Function; // pattern - TemplateParameter Param = TemplateParameter::getFromOpaqueValue( - Cand->DeductionFailure.TemplateParameter); - + TemplateParameter Param = Cand->DeductionFailure.getTemplateParameter(); switch (Cand->DeductionFailure.Result) { case Sema::TDK_Success: llvm_unreachable("TDK_success while diagnosing bad deduction"); @@ -4947,11 +5101,29 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, return; } + case Sema::TDK_Inconsistent: + case Sema::TDK_InconsistentQuals: { + NamedDecl *ParamD; + int which = 0; + if ((ParamD = Param.dyn_cast())) + which = 0; + else if ((ParamD = Param.dyn_cast())) + which = 1; + else { + ParamD = Param.get(); + which = 2; + } + + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_inconsistent_deduction) + << which << ParamD->getDeclName() + << *Cand->DeductionFailure.getFirstArg() + << *Cand->DeductionFailure.getSecondArg(); + return; + } + // TODO: diagnose these individually, then kill off // note_ovl_candidate_bad_deduction, which is uselessly vague. case Sema::TDK_InstantiationDepth: - case Sema::TDK_Inconsistent: - case Sema::TDK_InconsistentQuals: case Sema::TDK_SubstitutionFailure: case Sema::TDK_NonDeducedMismatch: case Sema::TDK_TooManyArguments: diff --git a/lib/Sema/SemaOverload.h b/lib/Sema/SemaOverload.h index 5e61111308..b7b628886c 100644 --- a/lib/Sema/SemaOverload.h +++ b/lib/Sema/SemaOverload.h @@ -16,7 +16,9 @@ #define LLVM_CLANG_SEMA_OVERLOAD_H #include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" #include "clang/AST/UnresolvedSet.h" #include "llvm/ADT/SmallPtrSet.h" @@ -529,8 +531,24 @@ namespace clang { // A Sema::TemplateDeductionResult. unsigned Result; - // A TemplateParameter. - void *TemplateParameter; + /// \brief Opaque pointer containing additional data about + /// this deduction failure. + void *Data; + + /// \brief Retrieve the template parameter this deduction failure + /// refers to, if any. + TemplateParameter getTemplateParameter(); + + /// \brief Return the first template argument this deduction failure + /// refers to, if any. + const TemplateArgument *getFirstArg(); + + /// \brief Return the second template argument this deduction failure + /// refers to, if any. + const TemplateArgument *getSecondArg(); + + /// \brief Free any memory associated with this deduction failure. + void Destroy(); }; union { @@ -574,10 +592,9 @@ namespace clang { } /// \brief Clear out all of the candidates. - void clear() { - inherited::clear(); - Functions.clear(); - } + void clear(); + + ~OverloadCandidateSet() { clear(); } }; } // end namespace clang diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp index 1b7310f000..90d29497f4 100644 --- a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp @@ -16,7 +16,8 @@ void test_f1(int *ip, float fv) { } // TODO: this diagnostic can and should improve -template void f2(T*, T*); // expected-note 2 {{candidate template ignored: failed template argument deduction}} +template void f2(T*, T*); // expected-note {{candidate template ignored: failed template argument deduction}} \ +// expected-note{{candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'float')}} struct ConvToIntPtr { operator int*() const; diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p6.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p6.cpp index 6edf079b35..1b240cc98f 100644 --- a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p6.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p6.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s namespace test0 { - template void apply(T x, void (*f)(T)) { f(x); } // expected-note 2 {{failed template argument deduction}}\ + template void apply(T x, void (*f)(T)) { f(x); } // expected-note 2 {{candidate template ignored: deduced conflicting types for parameter 'T'}}\ // expected-note {{no overload of 'temp2' matching 'void (*)(int)'}} template void temp(A); diff --git a/test/SemaTemplate/overload-candidates.cpp b/test/SemaTemplate/overload-candidates.cpp new file mode 100644 index 0000000000..84e23716a9 --- /dev/null +++ b/test/SemaTemplate/overload-candidates.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +template +const T& min(const T&, const T&); // expected-note{{candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'long')}} + +void test_min() { + (void)min(1, 2l); // expected-error{{no matching function for call to 'min'}} +} -- 2.40.0