From 9b534547250a37b0cdf9fd4ac1b10dc37d3cfe4a Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 31 Dec 2015 02:02:54 +0000 Subject: [PATCH] Improve diagnostic for the case where a function template candidate is rejected by overload resolution because deduction succeeds, but the substituted parameter type for some parameter (with deduced type) doesn't exactly match the corresponding adjusted argument type. llvm-svn: 256657 --- .../clang/Basic/DiagnosticSemaKinds.td | 4 ++ clang/include/clang/Sema/Sema.h | 3 + clang/include/clang/Sema/TemplateDeduction.h | 33 +++++++++-- clang/lib/Sema/SemaOverload.cpp | 56 +++++++++++++++++-- clang/lib/Sema/SemaTemplateDeduction.cpp | 8 ++- clang/test/CXX/drs/dr5xx.cpp | 3 +- .../temp.deduct/temp.deduct.call/p3.cpp | 2 +- .../temp.deduct/temp.deduct.type/p9-0x.cpp | 2 +- 8 files changed, 95 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 25fbe2db1ca8..a8e468d956c3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3112,6 +3112,10 @@ def note_addrof_ovl_candidate_disabled_by_enable_if_attr : Note< def note_ovl_candidate_failed_overload_resolution : Note< "candidate template ignored: couldn't resolve reference to overloaded " "function %0">; +def note_ovl_candidate_deduced_mismatch : Note< + "candidate template ignored: deduced type " + "%diff{$ of %ordinal0 parameter does not match adjusted type $ of argument" + "|of %ordinal0 parameter does not match adjusted type of argument}1,2%3">; def note_ovl_candidate_non_deduced_mismatch : Note< "candidate template ignored: could not match %diff{$ against $|types}0,1">; // This note is needed because the above note would sometimes print two diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7873843ab3bd..bce2f0c0bad0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6302,6 +6302,9 @@ public: /// \brief Substitution of the deduced template argument values /// resulted in an error. TDK_SubstitutionFailure, + /// \brief After substituting deduced template arguments, a dependent + /// parameter type did not match the corresponding argument. + TDK_DeducedMismatch, /// \brief A non-depnedent component of the parameter did not match the /// corresponding component of the argument. TDK_NonDeducedMismatch, diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h index 9315ddd89c6b..c22c703ef732 100644 --- a/clang/include/clang/Sema/TemplateDeduction.h +++ b/clang/include/clang/Sema/TemplateDeduction.h @@ -140,6 +140,9 @@ public: /// TDK_SubstitutionFailure: this argument is the template /// argument we were instantiating when we encountered an error. /// + /// TDK_DeducedMismatch: this is the parameter type, after substituting + /// deduced arguments. + /// /// TDK_NonDeducedMismatch: this is the component of the 'parameter' /// of the deduction, directly provided in the source code. TemplateArgument FirstArg; @@ -147,18 +150,32 @@ public: /// \brief The second template argument to which the template /// argument deduction failure refers. /// + /// TDK_Inconsistent: this argument is the second value deduced + /// for the corresponding template parameter. + /// + /// TDK_DeducedMismatch: this is the (adjusted) call argument type. + /// /// TDK_NonDeducedMismatch: this is the mismatching component of the /// 'argument' of the deduction, from which we are deducing arguments. /// /// FIXME: Finish documenting this. TemplateArgument SecondArg; - /// \brief The expression which caused a deduction failure. - /// - /// TDK_FailedOverloadResolution: this argument is the reference to - /// an overloaded function which could not be resolved to a specific - /// function. - Expr *Expression; + union { + /// \brief The expression which caused a deduction failure. + /// + /// TDK_FailedOverloadResolution: this argument is the reference to + /// an overloaded function which could not be resolved to a specific + /// function. + Expr *Expression; + + /// \brief The index of the function argument that caused a deduction + /// failure. + /// + /// TDK_DeducedMismatch: this is the index of the argument that had a + /// different argument type from its substituted parameter type. + unsigned CallArgIndex; + }; /// \brief Information on packs that we're currently expanding. /// @@ -211,6 +228,10 @@ struct DeductionFailureInfo { /// if any. Expr *getExpr(); + /// \brief Return the index of the call argument that this deduction + /// failure refers to, if any. + llvm::Optional getCallArgIndex(); + /// \brief Free any memory associated with this deduction failure. void Destroy(); }; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 1caa94c9a458..e0c10e4479e2 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -543,6 +543,12 @@ namespace { struct DFIParamWithArguments : DFIArguments { TemplateParameter Param; }; + // Structure used by DeductionFailureInfo to store template argument + // information and the index of the problematic call argument. + struct DFIDeducedMismatchArgs : DFIArguments { + TemplateArgumentList *TemplateArgs; + unsigned CallArgIndex; + }; } /// \brief Convert from Sema's representation of template deduction information @@ -554,13 +560,14 @@ clang::MakeDeductionFailureInfo(ASTContext &Context, DeductionFailureInfo Result; Result.Result = static_cast(TDK); Result.HasDiagnostic = false; - Result.Data = nullptr; switch (TDK) { case Sema::TDK_Success: case Sema::TDK_Invalid: case Sema::TDK_InstantiationDepth: case Sema::TDK_TooManyArguments: case Sema::TDK_TooFewArguments: + case Sema::TDK_MiscellaneousDeductionFailure: + Result.Data = nullptr; break; case Sema::TDK_Incomplete: @@ -568,6 +575,17 @@ clang::MakeDeductionFailureInfo(ASTContext &Context, Result.Data = Info.Param.getOpaqueValue(); break; + case Sema::TDK_DeducedMismatch: { + // FIXME: Should allocate from normal heap so that we can free this later. + auto *Saved = new (Context) DFIDeducedMismatchArgs; + Saved->FirstArg = Info.FirstArg; + Saved->SecondArg = Info.SecondArg; + Saved->TemplateArgs = Info.take(); + Saved->CallArgIndex = Info.CallArgIndex; + Result.Data = Saved; + break; + } + case Sema::TDK_NonDeducedMismatch: { // FIXME: Should allocate from normal heap so that we can free this later. DFIArguments *Saved = new (Context) DFIArguments; @@ -601,9 +619,6 @@ clang::MakeDeductionFailureInfo(ASTContext &Context, case Sema::TDK_FailedOverloadResolution: Result.Data = Info.Expression; break; - - case Sema::TDK_MiscellaneousDeductionFailure: - break; } return Result; @@ -623,6 +638,7 @@ void DeductionFailureInfo::Destroy() { case Sema::TDK_Inconsistent: case Sema::TDK_Underqualified: + case Sema::TDK_DeducedMismatch: case Sema::TDK_NonDeducedMismatch: // FIXME: Destroy the data? Data = nullptr; @@ -657,6 +673,7 @@ TemplateParameter DeductionFailureInfo::getTemplateParameter() { case Sema::TDK_TooManyArguments: case Sema::TDK_TooFewArguments: case Sema::TDK_SubstitutionFailure: + case Sema::TDK_DeducedMismatch: case Sema::TDK_NonDeducedMismatch: case Sema::TDK_FailedOverloadResolution: return TemplateParameter(); @@ -692,6 +709,9 @@ TemplateArgumentList *DeductionFailureInfo::getTemplateArgumentList() { case Sema::TDK_FailedOverloadResolution: return nullptr; + case Sema::TDK_DeducedMismatch: + return static_cast(Data)->TemplateArgs; + case Sema::TDK_SubstitutionFailure: return static_cast(Data); @@ -718,6 +738,7 @@ const TemplateArgument *DeductionFailureInfo::getFirstArg() { case Sema::TDK_Inconsistent: case Sema::TDK_Underqualified: + case Sema::TDK_DeducedMismatch: case Sema::TDK_NonDeducedMismatch: return &static_cast(Data)->FirstArg; @@ -744,6 +765,7 @@ const TemplateArgument *DeductionFailureInfo::getSecondArg() { case Sema::TDK_Inconsistent: case Sema::TDK_Underqualified: + case Sema::TDK_DeducedMismatch: case Sema::TDK_NonDeducedMismatch: return &static_cast(Data)->SecondArg; @@ -763,6 +785,14 @@ Expr *DeductionFailureInfo::getExpr() { return nullptr; } +llvm::Optional DeductionFailureInfo::getCallArgIndex() { + if (static_cast(Result) == + Sema::TDK_DeducedMismatch) + return static_cast(Data)->CallArgIndex; + + return llvm::None; +} + void OverloadCandidateSet::destroyCandidates() { for (iterator i = begin(), e = end(); i != e; ++i) { for (unsigned ii = 0, ie = i->NumConversions; ii != ie; ++ii) @@ -9397,6 +9427,23 @@ static void DiagnoseBadDeduction(Sema &S, Decl *Templated, return; } + case Sema::TDK_DeducedMismatch: { + // Format the template argument list into the argument string. + SmallString<128> TemplateArgString; + if (TemplateArgumentList *Args = + DeductionFailure.getTemplateArgumentList()) { + TemplateArgString = " "; + TemplateArgString += S.getTemplateArgumentBindingsText( + getDescribedTemplate(Templated)->getTemplateParameters(), *Args); + } + + S.Diag(Templated->getLocation(), diag::note_ovl_candidate_deduced_mismatch) + << (*DeductionFailure.getCallArgIndex() + 1) + << *DeductionFailure.getFirstArg() << *DeductionFailure.getSecondArg() + << TemplateArgString; + break; + } + case Sema::TDK_NonDeducedMismatch: { // FIXME: Provide a source location to indicate what we couldn't match. TemplateArgument FirstTA = *DeductionFailure.getFirstArg(); @@ -9686,6 +9733,7 @@ static unsigned RankDeductionFailure(const DeductionFailureInfo &DFI) { return 2; case Sema::TDK_SubstitutionFailure: + case Sema::TDK_DeducedMismatch: case Sema::TDK_NonDeducedMismatch: case Sema::TDK_MiscellaneousDeductionFailure: return 3; diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 837e0de4038d..cd54920b08cf 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2954,8 +2954,12 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, continue; QualType DeducedA = Specialization->getParamDecl(ParamIdx)->getType(); - if (CheckOriginalCallArgDeduction(*this, OriginalArg, DeducedA)) - return Sema::TDK_SubstitutionFailure; + if (CheckOriginalCallArgDeduction(*this, OriginalArg, DeducedA)) { + Info.FirstArg = TemplateArgument(DeducedA); + Info.SecondArg = TemplateArgument(OriginalArg.OriginalArgType); + Info.CallArgIndex = OriginalArg.ArgIdx; + return TDK_DeducedMismatch; + } } } diff --git a/clang/test/CXX/drs/dr5xx.cpp b/clang/test/CXX/drs/dr5xx.cpp index 17b525d96228..96d3494bf060 100644 --- a/clang/test/CXX/drs/dr5xx.cpp +++ b/clang/test/CXX/drs/dr5xx.cpp @@ -148,8 +148,7 @@ namespace dr522 { // dr522: yes template void b2(volatile T * const *); template void b2(volatile T * const S::*); template void b2(volatile T * const S::* const *); - // FIXME: This diagnostic isn't very good. The problem is not substitution failure. - template void b2a(volatile T *S::* const *); // expected-note {{substitution failure}} + template void b2a(volatile T *S::* const *); // expected-note {{candidate template ignored: deduced type 'volatile int *dr522::S::*const *' of 1st parameter does not match adjusted type 'int *dr522::S::**' of argument}} template struct Base {}; struct Derived : Base {}; diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp index 295f080a75d9..f46ea2e585dc 100644 --- a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp @@ -139,7 +139,7 @@ namespace N { } namespace PR9233 { - template void f(const T **q); // expected-note{{candidate template ignored: substitution failure [with T = int]}} + template void f(const T **q); // expected-note{{candidate template ignored: deduced type 'const int **' of 1st parameter does not match adjusted type 'int **' of argument [with T = int]}} void g(int **p) { f(p); // expected-error{{no matching function for call to 'f'}} diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp index 782057d397b9..b807a0ff9f11 100644 --- a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp @@ -55,7 +55,7 @@ namespace DeduceNonTypeTemplateArgsInArray { } namespace DeduceWithDefaultArgs { - template class Container> void f(Container); // expected-note {{substitution failure [with Container = X]}} + template class Container> void f(Container); // expected-note {{deduced type 'X<[...], (default) int>' of 1st parameter does not match adjusted type 'X<[...], double>' of argument [with Container = X]}} template struct X {}; void g() { // OK, use default argument for the second template parameter.