https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/222296
There is a wall-time improvement: https://llvm-compile-time-tracker.com/compare.php?from=678058af7da6a20593d8bb23076dc4773f9e55f1&to=d5c04428be4b47526390e4bbcbb49e92bd9005dd&stat=instructions%3Au When overload resolution fails, we tried to build a RecoveryExpr which ended up performing duplicate overload resolutions, where the template argument deduction can be expensive. This patch removes those 'known' invalid overload candidates before building RecoveryExpr. >From d5c04428be4b47526390e4bbcbb49e92bd9005dd Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Wed, 9 Sep 2026 15:19:40 +0800 Subject: [PATCH] [Clang] Avoid unnecessary overload resolution when building RecoveryExpr --- clang/lib/Sema/SemaOverload.cpp | 64 +++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 106ddb90ed9dc..6cec8f9892c2c 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -14612,18 +14612,31 @@ static bool canBeDeclaredInNamespace(const DeclarationName &Name) { /// Attempt to recover from an ill-formed use of a non-dependent name in a /// template, where the non-dependent name was declared after the template -/// was defined. This is common in code written for a compilers which do not +/// was defined. This is common in code written for compilers which do not /// correctly implement two-stage name lookup. /// /// Returns true if a viable candidate was found and a diagnostic was issued. static bool DiagnoseTwoPhaseLookup( Sema &SemaRef, SourceLocation FnLoc, const CXXScopeSpec &SS, LookupResult &R, OverloadCandidateSet::CandidateSetKind CSK, + OverloadCandidateSet &KnownInvalidCandidateSet, TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args, CXXRecordDecl **FoundInClass = nullptr) { if (!SemaRef.inTemplateInstantiation() || !SS.isEmpty()) return false; + llvm::SmallPtrSet<FunctionDecl *, 4> InvalidCandidates( + llvm::from_range, + llvm::make_filter_range( + llvm::map_range( + KnownInvalidCandidateSet, + [](const OverloadCandidate &Candidate) -> FunctionDecl * { + if (!Candidate.Viable) + return Candidate.Function; + return nullptr; + }), + [](const FunctionDecl *FD) { return FD != nullptr; })); + for (DeclContext *DC = SemaRef.CurContext; DC; DC = DC->getParent()) { if (DC->isTransparentContext()) continue; @@ -14634,8 +14647,13 @@ static bool DiagnoseTwoPhaseLookup( R.suppressDiagnostics(); OverloadCandidateSet Candidates(FnLoc, CSK); - SemaRef.AddOverloadedCallCandidates(R, ExplicitTemplateArgs, Args, - Candidates); + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { + if (InvalidCandidates.contains(I->getAsFunction())) + continue; + AddOverloadedCallCandidate(SemaRef, I.getPair(), ExplicitTemplateArgs, + Args, Candidates, false, + /*KnownValid=*/false); + } OverloadCandidateSet::iterator Best; OverloadingResult OR = @@ -14726,14 +14744,15 @@ static bool DiagnoseTwoPhaseLookup( /// Returns true if a viable candidate was found and a diagnostic was issued. static bool DiagnoseTwoPhaseOperatorLookup(Sema &SemaRef, OverloadedOperatorKind Op, - SourceLocation OpLoc, - ArrayRef<Expr *> Args) { + SourceLocation OpLoc, ArrayRef<Expr *> Args, + OverloadCandidateSet &KnownInvalidCandidateSet) { DeclarationName OpName = - SemaRef.Context.DeclarationNames.getCXXOperatorName(Op); + SemaRef.Context.DeclarationNames.getCXXOperatorName(Op); LookupResult R(SemaRef, OpName, OpLoc, Sema::LookupOperatorName); - return DiagnoseTwoPhaseLookup(SemaRef, OpLoc, CXXScopeSpec(), R, - OverloadCandidateSet::CSK_Operator, - /*ExplicitTemplateArgs=*/nullptr, Args); + return DiagnoseTwoPhaseLookup( + SemaRef, OpLoc, CXXScopeSpec(), R, OverloadCandidateSet::CSK_Operator, + KnownInvalidCandidateSet, + /*ExplicitTemplateArgs=*/nullptr, Args, /*FoundInClass=*/nullptr); } namespace { @@ -14760,11 +14779,10 @@ class BuildRecoveryCallExprRAII { /// expected to diagnose as appropriate. static ExprResult BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, - UnresolvedLookupExpr *ULE, - SourceLocation LParenLoc, - MutableArrayRef<Expr *> Args, - SourceLocation RParenLoc, - bool EmptyLookup, bool AllowTypoCorrection) { + UnresolvedLookupExpr *ULE, SourceLocation LParenLoc, + MutableArrayRef<Expr *> Args, SourceLocation RParenLoc, + OverloadCandidateSet &KnownInvalidCandidateSet, + bool AllowTypoCorrection) { // Do not try to recover if it is already building a recovery call. // This stops infinite loops for template instantiations like // @@ -14790,9 +14808,10 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, CXXRecordDecl *FoundInClass = nullptr; if (DiagnoseTwoPhaseLookup(SemaRef, Fn->getExprLoc(), SS, R, OverloadCandidateSet::CSK_Normal, - ExplicitTemplateArgs, Args, &FoundInClass)) { + KnownInvalidCandidateSet, ExplicitTemplateArgs, + Args, &FoundInClass)) { // OK, diagnosed a two-phase lookup issue. - } else if (EmptyLookup) { + } else if (KnownInvalidCandidateSet.empty()) { // Try to recover from an empty lookup with typo correction. R.clear(); NoTypoCorrectionCCC NoTypoValidator{}; @@ -15005,10 +15024,9 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, // Try to recover by looking for viable functions which the user might // have meant to call. - ExprResult Recovery = BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc, - Args, RParenLoc, - CandidateSet->empty(), - AllowTypoCorrection); + ExprResult Recovery = + BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc, Args, RParenLoc, + *CandidateSet, AllowTypoCorrection); if (Recovery.isInvalid() || Recovery.isUsable()) return Recovery; @@ -15410,7 +15428,8 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc, // This is an erroneous use of an operator which can be overloaded by // a non-member function. Check for non-member operators which were // defined too late to be candidates. - if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, ArgsArray)) + if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, ArgsArray, + CandidateSet)) // FIXME: Recover by calling the found function. return ExprError(); @@ -15932,7 +15951,8 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc, // This is an erroneous use of an operator which can be overloaded by // a non-member function. Check for non-member operators which were // defined too late to be candidates. - if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, Args)) + if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, Args, + CandidateSet)) // FIXME: Recover by calling the found function. return ExprError(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
