From 0442d08fdb173d89b0779d32eb929957a344f5e6 Mon Sep 17 00:00:00 2001 From: "Yaxun (Sam) Liu" Date: Tue, 9 May 2023 14:05:55 -0400 Subject: [PATCH] [clang][Sema] Improve diagnostics for auto return type Currently when clang fails to deduce auto return type of a function, it does not emit any notes about why it fails. This causes difficulty for users to fix such errors. Actually, clang already generates the information for emitting notes about the failed deduction. There is a TODO for actually emitting them. This patch tries to implement the TODO. Basically it passes the failed template specialization candidate set from the point of specialization failure back to the point where the deduction starts. It is not comprehensive but would be a start for further improvement. Reviewed by: Richard Smith, Matheus Izvekov Differential Revision: https://reviews.llvm.org/D150212 Fixes: SWDEV-354278 --- clang/include/clang/Sema/Sema.h | 18 +++++++------- clang/lib/Sema/SemaOverload.cpp | 18 ++++++-------- clang/lib/Sema/SemaStmt.cpp | 16 +++++++++--- clang/lib/Sema/SemaTemplateDeduction.cpp | 41 ++++++++++++++++++------------- clang/test/SemaCXX/auto-type-from-cxx.cpp | 18 ++++++++++++++ 5 files changed, 72 insertions(+), 39 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 012a3aa..be93f8a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4153,10 +4153,9 @@ public: bool resolveAndFixAddressOfSingleOverloadCandidate( ExprResult &SrcExpr, bool DoFunctionPointerConversion = false); - FunctionDecl * - ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, - bool Complain = false, - DeclAccessPair *Found = nullptr); + FunctionDecl *ResolveSingleFunctionTemplateSpecialization( + OverloadExpr *ovl, bool Complain = false, DeclAccessPair *Found = nullptr, + TemplateSpecCandidateSet *FailedTSC = nullptr); bool ResolveAndFixSingleFunctionTemplateSpecialization( ExprResult &SrcExpr, bool DoFunctionPointerConversion = false, @@ -9140,11 +9139,12 @@ public: TypeSourceInfo *ReplaceAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, QualType Replacement); - TemplateDeductionResult DeduceAutoType(TypeLoc AutoTypeLoc, Expr *Initializer, - QualType &Result, - sema::TemplateDeductionInfo &Info, - bool DependentDeduction = false, - bool IgnoreConstraints = false); + TemplateDeductionResult + DeduceAutoType(TypeLoc AutoTypeLoc, Expr *Initializer, QualType &Result, + sema::TemplateDeductionInfo &Info, + bool DependentDeduction = false, + bool IgnoreConstraints = false, + TemplateSpecCandidateSet *FailedTSC = nullptr); void DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init); bool DeduceReturnType(FunctionDecl *FD, SourceLocation Loc, bool Diagnose = true); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5308934..71359f1 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -12797,10 +12797,9 @@ bool Sema::resolveAndFixAddressOfSingleOverloadCandidate( /// /// If no template-ids are found, no diagnostics are emitted and NULL is /// returned. -FunctionDecl * -Sema::ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, - bool Complain, - DeclAccessPair *FoundResult) { +FunctionDecl *Sema::ResolveSingleFunctionTemplateSpecialization( + OverloadExpr *ovl, bool Complain, DeclAccessPair *FoundResult, + TemplateSpecCandidateSet *FailedTSC) { // C++ [over.over]p1: // [...] [Note: any redundant set of parentheses surrounding the // overloaded function name is ignored (5.1). ] @@ -12814,7 +12813,6 @@ Sema::ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, TemplateArgumentListInfo ExplicitTemplateArgs; ovl->copyTemplateArgumentsInto(ExplicitTemplateArgs); - TemplateSpecCandidateSet FailedCandidates(ovl->getNameLoc()); // Look through all of the overloaded functions, searching for one // whose type matches exactly. @@ -12837,16 +12835,16 @@ Sema::ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, // function template specialization, which is added to the set of // overloaded functions considered. FunctionDecl *Specialization = nullptr; - TemplateDeductionInfo Info(FailedCandidates.getLocation()); + TemplateDeductionInfo Info(ovl->getNameLoc()); if (TemplateDeductionResult Result = DeduceTemplateArguments(FunctionTemplate, &ExplicitTemplateArgs, Specialization, Info, /*IsAddressOfFunction*/true)) { // Make a note of the failed deduction for diagnostics. - // TODO: Actually use the failed-deduction info? - FailedCandidates.addCandidate() - .set(I.getPair(), FunctionTemplate->getTemplatedDecl(), - MakeDeductionFailureInfo(Context, Result, Info)); + if (FailedTSC) + FailedTSC->addCandidate().set( + I.getPair(), FunctionTemplate->getTemplatedDecl(), + MakeDeductionFailureInfo(Context, Result, Info)); continue; } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 7daebbd..2c9a17a 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3825,9 +3825,18 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, { // Otherwise, [...] deduce a value for U using the rules of template // argument deduction. - TemplateDeductionInfo Info(RetExpr->getExprLoc()); - TemplateDeductionResult Res = - DeduceAutoType(OrigResultType, RetExpr, Deduced, Info); + auto RetExprLoc = RetExpr->getExprLoc(); + TemplateDeductionInfo Info(RetExprLoc); + SourceLocation TemplateSpecLoc; + if (RetExpr->getType() == Context.OverloadTy) { + auto FindResult = OverloadExpr::find(RetExpr); + if (FindResult.Expression) + TemplateSpecLoc = FindResult.Expression->getNameLoc(); + } + TemplateSpecCandidateSet FailedTSC(TemplateSpecLoc); + TemplateDeductionResult Res = DeduceAutoType( + OrigResultType, RetExpr, Deduced, Info, /*DependentDeduction=*/false, + /*IgnoreConstraints=*/false, &FailedTSC); if (Res != TDK_Success && FD->isInvalidDecl()) return true; switch (Res) { @@ -3853,6 +3862,7 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, default: Diag(RetExpr->getExprLoc(), diag::err_auto_fn_deduction_failure) << OrigResultType.getType() << RetExpr->getType(); + FailedTSC.NoteCandidates(*this, RetExprLoc); return true; } } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 27a8a59..b3dc61a 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3751,7 +3751,8 @@ static QualType GetTypeOfFunction(Sema &S, const OverloadExpr::FindResult &R, static QualType ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams, Expr *Arg, QualType ParamType, - bool ParamWasReference) { + bool ParamWasReference, + TemplateSpecCandidateSet *FailedTSC = nullptr) { OverloadExpr::FindResult R = OverloadExpr::find(Arg); @@ -3773,8 +3774,10 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams, !ParamType->isMemberFunctionPointerType()) { if (Ovl->hasExplicitTemplateArgs()) { // But we can still look for an explicit specialization. - if (FunctionDecl *ExplicitSpec - = S.ResolveSingleFunctionTemplateSpecialization(Ovl)) + if (FunctionDecl *ExplicitSpec = + S.ResolveSingleFunctionTemplateSpecialization( + Ovl, /*Complain=*/false, + /*FoundDeclAccessPair=*/nullptr, FailedTSC)) return GetTypeOfFunction(S, R, ExplicitSpec); } @@ -3856,7 +3859,8 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams, /// overloaded function set that could not be resolved. static bool AdjustFunctionParmAndArgTypesForDeduction( Sema &S, TemplateParameterList *TemplateParams, unsigned FirstInnerIndex, - QualType &ParamType, QualType &ArgType, Expr *Arg, unsigned &TDF) { + QualType &ParamType, QualType &ArgType, Expr *Arg, unsigned &TDF, + TemplateSpecCandidateSet *FailedTSC = nullptr) { // C++0x [temp.deduct.call]p3: // If P is a cv-qualified type, the top level cv-qualifiers of P's type // are ignored for type deduction. @@ -3873,9 +3877,8 @@ static bool AdjustFunctionParmAndArgTypesForDeduction( // but there are sometimes special circumstances. Typically // involving a template-id-expr. if (ArgType == S.Context.OverloadTy) { - ArgType = ResolveOverloadForDeduction(S, TemplateParams, - Arg, ParamType, - ParamRefType != nullptr); + ArgType = ResolveOverloadForDeduction(S, TemplateParams, Arg, ParamType, + ParamRefType != nullptr, FailedTSC); if (ArgType.isNull()) return true; } @@ -3953,7 +3956,8 @@ static Sema::TemplateDeductionResult DeduceTemplateArgumentsFromCallArgument( QualType ParamType, Expr *Arg, TemplateDeductionInfo &Info, SmallVectorImpl &Deduced, SmallVectorImpl &OriginalCallArgs, - bool DecomposedParam, unsigned ArgIdx, unsigned TDF); + bool DecomposedParam, unsigned ArgIdx, unsigned TDF, + TemplateSpecCandidateSet *FailedTSC = nullptr); /// Attempt template argument deduction from an initializer list /// deemed to be an argument in a function call. @@ -4029,14 +4033,16 @@ static Sema::TemplateDeductionResult DeduceTemplateArgumentsFromCallArgument( QualType ParamType, Expr *Arg, TemplateDeductionInfo &Info, SmallVectorImpl &Deduced, SmallVectorImpl &OriginalCallArgs, - bool DecomposedParam, unsigned ArgIdx, unsigned TDF) { + bool DecomposedParam, unsigned ArgIdx, unsigned TDF, + TemplateSpecCandidateSet *FailedTSC) { QualType ArgType = Arg->getType(); QualType OrigParamType = ParamType; // If P is a reference type [...] // If P is a cv-qualified type [...] - if (AdjustFunctionParmAndArgTypesForDeduction( - S, TemplateParams, FirstInnerIndex, ParamType, ArgType, Arg, TDF)) + if (AdjustFunctionParmAndArgTypesForDeduction(S, TemplateParams, + FirstInnerIndex, ParamType, + ArgType, Arg, TDF, FailedTSC)) return Sema::TDK_Success; // If [...] the argument is a non-empty initializer list [...] @@ -4719,11 +4725,11 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, /// should be specified in the 'Info' parameter. /// \param IgnoreConstraints Set if we should not fail if the deduced type does /// not satisfy the type-constraint in the auto type. -Sema::TemplateDeductionResult Sema::DeduceAutoType(TypeLoc Type, Expr *Init, - QualType &Result, - TemplateDeductionInfo &Info, - bool DependentDeduction, - bool IgnoreConstraints) { +Sema::TemplateDeductionResult +Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, + TemplateDeductionInfo &Info, bool DependentDeduction, + bool IgnoreConstraints, + TemplateSpecCandidateSet *FailedTSC) { assert(DependentDeduction || Info.getDeducedDepth() == 0); if (Init->containsErrors()) return TDK_AlreadyDiagnosed; @@ -4837,7 +4843,8 @@ Sema::TemplateDeductionResult Sema::DeduceAutoType(TypeLoc Type, Expr *Init, "substituting template parameter for 'auto' failed"); if (auto TDK = DeduceTemplateArgumentsFromCallArgument( *this, TemplateParamsSt.get(), 0, FuncParam, Init, Info, Deduced, - OriginalCallArgs, /*Decomposed=*/false, /*ArgIdx=*/0, /*TDF=*/0)) + OriginalCallArgs, /*Decomposed=*/false, /*ArgIdx=*/0, /*TDF=*/0, + FailedTSC)) return DeductionFailed(TDK); } diff --git a/clang/test/SemaCXX/auto-type-from-cxx.cpp b/clang/test/SemaCXX/auto-type-from-cxx.cpp index 21620f7..5cd4899 100644 --- a/clang/test/SemaCXX/auto-type-from-cxx.cpp +++ b/clang/test/SemaCXX/auto-type-from-cxx.cpp @@ -18,3 +18,21 @@ int d() { new __auto_type; // expected-error {{'__auto_type' not allowed in type allocated by 'new'}} } +namespace TestDeductionFail { + +template +void caller(T x) {x.fun();} // expected-note {{candidate template ignored: substitution failure [with T = TestDeductionFail::Abstract]: parameter type 'TestDeductionFail::Abstract' is an abstract class}} + +template +auto getCaller(){ + return caller; // expected-error {{cannot deduce return type 'auto' from returned value of type ''}} +} + +class Abstract{ + public: + void fun(); + virtual void vfun()=0; + void call(){getCaller()(*this);} // expected-note {{in instantiation of function template specialization 'TestDeductionFail::getCaller' requested here}} +}; + +} -- 2.7.4