llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: cor3ntin (cor3ntin) <details> <summary>Changes</summary> This implements the same overload resolution behavior as GCC, as described in https://wg21.link/p3606 (sections 1-2, not 3) If, during overload resolution, a non-template candidate is always picked because each argument is a perfect match (i.e., the source and target types are the same), we do not perform deduction for any template candidate that might exist. The goal is to be able to merge #<!-- -->122423 without being too disruptive. This change means that the selection of the best viable candidate and template argument deduction become interleaved. To avoid rewriting half of Clang, we store in `OverloadCandidateSet` enough information to deduce template candidates from `OverloadCandidateSet::BestViableFunction`. This means the lifetime of any object used by the template argument must outlive a call to `Add*Template*Candidate`. This two-phase resolution is not performed for some initialization as there are cases where template candidates are a better match per the standard. It's also bypassed for code completion. The change has a nice impact on compile times https://llvm-compile-time-tracker.com/compare.php?from=719b029c16eeb1035da522fd641dfcc4cee6be74&to=bf7041045c9408490c395230047c5461de72fc39&stat=instructions%3Au . Fixes #<!-- -->62096 Fixes #<!-- -->74581 --- Patch is 60.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/133426.diff 9 Files Affected: - (modified) clang/include/clang/Sema/Overload.h (+150-7) - (modified) clang/include/clang/Sema/Sema.h (+2) - (modified) clang/lib/Sema/SemaCodeComplete.cpp (+4-2) - (modified) clang/lib/Sema/SemaInit.cpp (+11-4) - (modified) clang/lib/Sema/SemaOverload.cpp (+344-101) - (modified) clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp (+4-4) - (modified) clang/test/SemaCXX/implicit-member-functions.cpp (+7-14) - (modified) clang/test/SemaTemplate/instantiate-function-params.cpp (+3-4) - (modified) clang/test/Templight/templight-empty-entries-fix.cpp (+51-75) ``````````diff diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index 6e08762dcc6d7..547845c01db11 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -38,6 +38,7 @@ #include <cstddef> #include <cstdint> #include <utility> +#include <variant> namespace clang { @@ -407,6 +408,11 @@ class Sema; Third == ICK_Identity; } + bool isPerfect(const ASTContext &C) const { + return isIdentityConversion() && + (!ReferenceBinding || C.hasSameType(getFromType(), getToType(2))); + } + ImplicitConversionRank getRank() const; NarrowingKind getNarrowingKind(ASTContext &Context, const Expr *Converted, @@ -743,6 +749,10 @@ class Sema; Standard.setAllToTypes(T); } + bool isPerfect(const ASTContext &C) const { + return isStandard() && Standard.isPerfect(C); + } + // True iff this is a conversion sequence from an initializer list to an // array or std::initializer. bool hasInitializerListContainerType() const { @@ -979,6 +989,20 @@ class Sema; return false; } + bool isPerfectMatch(const ASTContext &Ctx) const { + if (!Viable) + return false; + for (const auto &C : Conversions) { + if (!C.isInitialized()) + return false; + if (!C.isPerfect(Ctx)) + return false; + } + if (isa_and_nonnull<CXXConversionDecl>(Function)) + return FinalConversion.isPerfect(Ctx); + return true; + } + bool TryToFixBadConversion(unsigned Idx, Sema &S) { bool CanFix = Fix.tryToFixConversion( Conversions[Idx].Bad.FromExpr, @@ -1015,6 +1039,61 @@ class Sema; RewriteKind(CRK_None) {} }; + struct DeferredConversionTemplateOverloadCandidate { + FunctionTemplateDecl *FunctionTemplate; + DeclAccessPair FoundDecl; + CXXRecordDecl *ActingContext; + Expr *From; + QualType ToType; + + LLVM_PREFERRED_TYPE(bool) + unsigned AllowObjCConversionOnExplicit : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned AllowExplicit : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned AllowResultConversion : 1; + }; + + struct DeferredMethodTemplateOverloadCandidate { + FunctionTemplateDecl *FunctionTemplate; + DeclAccessPair FoundDecl; + ArrayRef<Expr *> Args; + CXXRecordDecl *ActingContext; + Expr::Classification ObjectClassification; + QualType ObjectType; + + OverloadCandidateParamOrder PO; + LLVM_PREFERRED_TYPE(bool) + unsigned SuppressUserConversions : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned PartialOverloading : 1; + }; + + struct DeferredFunctionTemplateOverloadCandidate { + FunctionTemplateDecl *FunctionTemplate; + DeclAccessPair FoundDecl; + ArrayRef<Expr *> Args; + + CallExpr::ADLCallKind IsADLCandidate; + OverloadCandidateParamOrder PO; + LLVM_PREFERRED_TYPE(bool) + unsigned SuppressUserConversions : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned PartialOverloading : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned AllowExplicit : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned AggregateCandidateDeduction : 1; + }; + + using DeferredTemplateOverloadCandidate = + std::variant<DeferredConversionTemplateOverloadCandidate, + DeferredMethodTemplateOverloadCandidate, + DeferredFunctionTemplateOverloadCandidate>; + + static_assert( + std::is_trivially_destructible_v<DeferredTemplateOverloadCandidate>); + /// OverloadCandidateSet - A set of overload candidates, used in C++ /// overload resolution (C++ 13.3). class OverloadCandidateSet { @@ -1043,6 +1122,8 @@ class Sema; /// C++ [over.match.call.general] /// Resolve a call through the address of an overload set. CSK_AddressOfOverloadSet, + + CSK_CodeCompletion, }; /// Information about operator rewrites to consider when adding operator @@ -1116,6 +1197,7 @@ class Sema; private: SmallVector<OverloadCandidate, 16> Candidates; llvm::SmallPtrSet<uintptr_t, 16> Functions; + SmallVector<DeferredTemplateOverloadCandidate, 8> DeferredCandidates; // Allocator for ConversionSequenceLists. We store the first few of these // inline to avoid allocation for small sets. @@ -1126,7 +1208,8 @@ class Sema; OperatorRewriteInfo RewriteInfo; constexpr static unsigned NumInlineBytes = - 24 * sizeof(ImplicitConversionSequence); + 32 * sizeof(ImplicitConversionSequence); + unsigned NumInlineBytesUsed = 0; alignas(void *) char InlineSpace[NumInlineBytes]; @@ -1137,15 +1220,13 @@ class Sema; /// from the slab allocator. /// FIXME: It would probably be nice to have a SmallBumpPtrAllocator /// instead. - /// FIXME: Now that this only allocates ImplicitConversionSequences, do we - /// want to un-generalize this? template <typename T> T *slabAllocate(unsigned N) { // It's simpler if this doesn't need to consider alignment. static_assert(alignof(T) == alignof(void *), "Only works for pointer-aligned types."); - static_assert(std::is_trivial<T>::value || - std::is_same<ImplicitConversionSequence, T>::value, + static_assert(std::is_trivially_destructible_v<T> || + (std::is_same_v<ImplicitConversionSequence, T>), "Add destruction logic to OverloadCandidateSet::clear()."); unsigned NBytes = sizeof(T) * N; @@ -1176,6 +1257,9 @@ class Sema; /// Whether diagnostics should be deferred. bool shouldDeferDiags(Sema &S, ArrayRef<Expr *> Args, SourceLocation OpLoc); + // Whether the resolution of template candidates should be defered + bool shouldDeferTemplateArgumentDeduction(const LangOptions &Opts) const; + /// Determine when this overload candidate will be new to the /// overload set. bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO = @@ -1199,8 +1283,12 @@ class Sema; iterator begin() { return Candidates.begin(); } iterator end() { return Candidates.end(); } - size_t size() const { return Candidates.size(); } - bool empty() const { return Candidates.empty(); } + size_t size() const { + return Candidates.size() + DeferredCandidates.size(); + } + bool empty() const { + return Candidates.empty() && DeferredCandidates.empty(); + } /// Allocate storage for conversion sequences for NumConversions /// conversions. @@ -1216,6 +1304,19 @@ class Sema; return ConversionSequenceList(Conversions, NumConversions); } + llvm::MutableArrayRef<Expr *> getPersistentArgsArray(unsigned N) { + Expr **Exprs = slabAllocate<Expr *>(N); + return llvm::MutableArrayRef<Expr *>(Exprs, N); + } + + template <typename... T> + llvm::MutableArrayRef<Expr *> getPersistentArgsArray(T *...Exprs) { + llvm::MutableArrayRef<Expr *> Arr = + getPersistentArgsArray(sizeof...(Exprs)); + llvm::copy(std::initializer_list<Expr *>{Exprs...}, Arr.data()); + return Arr; + } + /// Add a new candidate with NumConversions conversion sequence slots /// to the overload set. OverloadCandidate &addCandidate(unsigned NumConversions = 0, @@ -1231,6 +1332,28 @@ class Sema; return C; } + void AddDeferredTemplateCandidate( + FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl, + ArrayRef<Expr *> Args, bool SuppressUserConversions, + bool PartialOverloading, bool AllowExplicit, + CallExpr::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO, + bool AggregateCandidateDeduction); + + void AddDeferredMethodTemplateCandidate( + FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl, + CXXRecordDecl *ActingContext, QualType ObjectType, + Expr::Classification ObjectClassification, ArrayRef<Expr *> Args, + bool SuppressUserConversions, bool PartialOverloading, + OverloadCandidateParamOrder PO); + + void AddDeferredConversionTemplateCandidate( + FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl, + CXXRecordDecl *ActingContext, Expr *From, QualType ToType, + bool AllowObjCConversionOnExplicit, bool AllowExplicit, + bool AllowResultConversion); + + void InjectNonDeducedTemplateCandidates(Sema &S); + /// Find the best viable function on this overload set, if it exists. OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc, OverloadCandidateSet::iterator& Best); @@ -1263,6 +1386,14 @@ class Sema; DestAS = AS; } + private: + OverloadingResult ResultForBestCandidate(const iterator &Best); + void CudaExcludeWrongSideCandidates(Sema &S); + OverloadingResult + BestViableFunctionImpl(Sema &S, SourceLocation Loc, + OverloadCandidateSet::iterator &Best); + void PerfectViableFunction(Sema &S, SourceLocation Loc, + OverloadCandidateSet::iterator &Best); }; bool isBetterOverloadCandidate(Sema &S, const OverloadCandidate &Cand1, @@ -1311,6 +1442,18 @@ class Sema; // parameter. bool shouldEnforceArgLimit(bool PartialOverloading, FunctionDecl *Function); + inline bool OverloadCandidateSet::shouldDeferTemplateArgumentDeduction( + const LangOptions &Opts) const { + return + // For user defined conversion we need to check against different + // combination of CV qualifiers and look at any expicit specifier, so + // always deduce template candidate. + Kind != CSK_InitByUserDefinedConversion + && Kind != CSK_InitByConstructor + && Kind != CSK_CodeCompletion && + Opts.CPlusPlus && (!Opts.CUDA || Opts.GPUExcludeWrongSideOverloads); + } + } // namespace clang #endif // LLVM_CLANG_SEMA_OVERLOAD_H diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 066bce61c74c1..ab2a94584d46c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -60,6 +60,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/IdentifierResolver.h" +#include "clang/Sema/Overload.h" #include "clang/Sema/Ownership.h" #include "clang/Sema/ParsedAttr.h" #include "clang/Sema/Redeclaration.h" @@ -10345,6 +10346,7 @@ class Sema final : public SemaBase { /// Add a C++ function template specialization as a candidate /// in the candidate set, using template argument deduction to produce /// an appropriate function template specialization. + void AddTemplateOverloadCandidate( FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl, TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args, diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 2003701b65654..e314f6859d71c 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -6364,7 +6364,8 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args, Expr *NakedFn = Fn->IgnoreParenCasts(); // Build an overload candidate set based on the functions we find. SourceLocation Loc = Fn->getExprLoc(); - OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal); + OverloadCandidateSet CandidateSet(Loc, + OverloadCandidateSet::CSK_CodeCompletion); if (auto ULE = dyn_cast<UnresolvedLookupExpr>(NakedFn)) { SemaRef.AddOverloadedCallCandidates(ULE, ArgsWithoutDependentTypes, @@ -6567,7 +6568,8 @@ QualType SemaCodeCompletion::ProduceConstructorSignatureHelp( // FIXME: Provide support for variadic template constructors. if (CRD) { - OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal); + OverloadCandidateSet CandidateSet(Loc, + OverloadCandidateSet::CSK_CodeCompletion); for (NamedDecl *C : SemaRef.LookupConstructors(CRD)) { if (auto *FD = dyn_cast<FunctionDecl>(C)) { // FIXME: we can't yet provide correct signature help for initializer diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 9814c3f456f0d..a8ab43776a6de 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10043,12 +10043,19 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // When [...] the constructor [...] is a candidate by // - [over.match.copy] (in all cases) if (TD) { - SmallVector<Expr *, 8> TmpInits; - for (Expr *E : Inits) + + // As template candidates are not deduced immediately, + // persist the arry in the overload set. + MutableArrayRef<Expr *> TmpInits = + Candidates.getPersistentArgsArray(Inits.size()); + + for (auto [I, E] : llvm::enumerate(Inits)) { if (auto *DI = dyn_cast<DesignatedInitExpr>(E)) - TmpInits.push_back(DI->getInit()); + TmpInits[I] = DI->getInit(); else - TmpInits.push_back(E); + TmpInits[I] = E; + } + AddTemplateOverloadCandidate( TD, FoundDecl, /*ExplicitArgs=*/nullptr, TmpInits, Candidates, /*SuppressUserConversions=*/false, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 6d8006b35dcf4..faf357b010580 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -31,6 +31,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Overload.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaCodeCompletion.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" @@ -45,6 +46,7 @@ #include <cstddef> #include <cstdlib> #include <optional> +#include <variant> using namespace clang; using namespace sema; @@ -7787,15 +7789,14 @@ void Sema::AddMethodCandidate( } } -void Sema::AddMethodTemplateCandidate( +static void AddMethodTemplateCandidateImmediately( + Sema &S, OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType, Expr::Classification ObjectClassification, ArrayRef<Expr *> Args, - OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, - bool PartialOverloading, OverloadCandidateParamOrder PO) { - if (!CandidateSet.isNewCandidate(MethodTmpl, PO)) - return; + bool SuppressUserConversions, bool PartialOverloading, + OverloadCandidateParamOrder PO) { // C++ [over.match.funcs]p7: // In each case where a candidate is a function template, candidate @@ -7809,12 +7810,12 @@ void Sema::AddMethodTemplateCandidate( TemplateDeductionInfo Info(CandidateSet.getLocation()); FunctionDecl *Specialization = nullptr; ConversionSequenceList Conversions; - if (TemplateDeductionResult Result = DeduceTemplateArguments( + if (TemplateDeductionResult Result = S.DeduceTemplateArguments( MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info, PartialOverloading, /*AggregateDeductionCandidate=*/false, /*PartialOrdering=*/false, ObjectType, ObjectClassification, [&](ArrayRef<QualType> ParamTypes) { - return CheckNonDependentConversions( + return S.CheckNonDependentConversions( MethodTmpl, ParamTypes, Args, CandidateSet, Conversions, SuppressUserConversions, ActingContext, ObjectType, ObjectClassification, PO); @@ -7826,7 +7827,7 @@ void Sema::AddMethodTemplateCandidate( Candidate.Function = MethodTmpl->getTemplatedDecl(); Candidate.Viable = false; Candidate.RewriteKind = - CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO); + CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO); Candidate.IsSurrogate = false; Candidate.IgnoreObjectArgument = cast<CXXMethodDecl>(Candidate.Function)->isStatic() || @@ -7836,8 +7837,8 @@ void Sema::AddMethodTemplateCandidate( Candidate.FailureKind = ovl_fail_bad_conversion; else { Candidate.FailureKind = ovl_fail_bad_deduction; - Candidate.DeductionFailure = MakeDeductionFailureInfo(Context, Result, - Info); + Candidate.DeductionFailure = + MakeDeductionFailureInfo(S.Context, Result, Info); } return; } @@ -7847,10 +7848,34 @@ void Sema::AddMethodTemplateCandidate( assert(Specialization && "Missing member function template specialization?"); assert(isa<CXXMethodDecl>(Specialization) && "Specialization is not a member function?"); - AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl, - ActingContext, ObjectType, ObjectClassification, Args, - CandidateSet, SuppressUserConversions, PartialOverloading, - Conversions, PO, Info.hasStrictPackMatch()); + S.AddMethodCandidate( + cast<CXXMethodDecl>(Specialization), FoundDecl, ActingContext, ObjectType, + ObjectClassification, Args, CandidateSet, SuppressUserConversions, + PartialOverloading, Conversions, PO, Info.hasStrictPackMatch()); +} + +void Sema::AddMethodTemplateCandidate( + FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl, + CXXRecordDecl *ActingContext, + TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType, + Expr::Classification ObjectClassification, ArrayRef<Expr *> Args, + OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, + bool PartialOverloading, OverloadCandidateParamOrder PO) { + if (!CandidateSet.isNewCandidate(MethodTmpl, PO)) + return; + + if (ExplicitTemplateArgs || + !CandidateSet.shouldDeferTemplateArgumentDeduction(getLangOpts())) { + AddMethodTemplateCandidateImmediately( + *this, CandidateSet, MethodTmpl, FoundDecl, ActingContext, + ExplicitTemplateArgs, ObjectType, ObjectClassification, Args, + SuppressUserConversions, PartialOverloading, PO); + return; + } + + CandidateSet.AddDeferredMethodTemplateCandidate( + MethodTmpl, FoundDecl, ActingContext, ObjectType, ObjectClassification, + Args, SuppressUserConversions, PartialOverloading, PO); } /// Determine whether a given function template has a simple explicit specifier @@ -7859,14 +7884,13 @@ static bool isNonDependentlyExplicit(FunctionTemplateDecl *FTD) { return ExplicitSpecifier::getFromDecl(FTD->getTemplatedDecl()).isExplicit(); } -void Sema::AddTemplateOverloadCandidate( +static void AddTemplateOverloadCandidateImmediately( + Sema &S, OverloadCandidateSet &CandidateSet, FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl, TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args, - OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, - bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate, - OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) { - if (!CandidateSet.isNewCandidate(FunctionTemplate, PO)) - return; + bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit, + Sema::ADLCallKind IsADLCandidate, OverloadCandidateParamOrder PO, + bool AggregateCandidateDeduction) { // If the function template has a non-dependent explicit specification, // exclude it now if appropriate; we are not permitted to perform deduction @@ -7893,14 +7917,14 @@ void Sema::AddTemplateOverloadCandidate( FunctionTemplate->getTemplateDepth()); FunctionDecl *Specialization = nullptr; ConversionSequenceList Conversions; - if (TemplateDeductionResult Result = DeduceTemplateArguments( + if (TemplateDeductionResult Result = S.DeduceTemplateArguments( FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info, PartialOverloading, AggregateCandidateDeduction, /*PartialOrdering=*/false, /*ObjectType=*/QualType(), /*ObjectClassification=*/Expr::Classification(), [&](ArrayRef<QualType> ParamTypes) { - return CheckNonDependentConversions( + return S.CheckNonDependentConver... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/133426 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits