[clang] [Clang] Avoid unnecessary overload resolution when building RecoveryExpr (PR #222296)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 9 04:02:21 PDT 2026
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 <zyn7109 at gmail.com>
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();
More information about the cfe-commits
mailing list