[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 07:51:38 PDT 2026
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/222296
>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 1/3] [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();
>From de09a923261728b389a84136429b25bb64f8bf9f Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 9 Sep 2026 22:29:50 +0800
Subject: [PATCH 2/3] Address Erich's feedback
---
clang/lib/Sema/SemaOverload.cpp | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 6cec8f9892c2c..429ed5718cd87 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14625,18 +14625,6 @@ static bool DiagnoseTwoPhaseLookup(
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;
@@ -14647,13 +14635,14 @@ static bool DiagnoseTwoPhaseLookup(
R.suppressDiagnostics();
OverloadCandidateSet Candidates(FnLoc, CSK);
- 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);
+ for (auto &Cand : KnownInvalidCandidateSet) {
+ if (Cand.Function)
+ Candidates.exclude(Cand.Function);
+ else if (Cand.IsSurrogate)
+ Candidates.exclude(Cand.Surrogate);
}
+ SemaRef.AddOverloadedCallCandidates(R, ExplicitTemplateArgs, Args,
+ Candidates);
OverloadCandidateSet::iterator Best;
OverloadingResult OR =
>From 3068ea46e3298ad53cccfb8d5a1a106f5521ea95 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 9 Sep 2026 22:51:07 +0800
Subject: [PATCH 3/3] fixup
---
clang/include/clang/Sema/Overload.h | 4 ++++
clang/lib/Sema/SemaOverload.cpp | 21 +++++++++++----------
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index a3ec9ef3f3e6d..3abc0013d8f69 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1375,10 +1375,14 @@ class Sema;
void clear(CandidateSetKind CSK);
using iterator = SmallVectorImpl<OverloadCandidate>::iterator;
+ using const_iterator = SmallVectorImpl<OverloadCandidate>::const_iterator;
iterator begin() { return Candidates.begin(); }
iterator end() { return Candidates.end(); }
+ const_iterator begin() const { return Candidates.begin(); }
+ const_iterator end() const { return Candidates.end(); }
+
size_t size() const { return Candidates.size() + DeferredCandidatesCount; }
size_t nonDeferredCandidatesCount() const { return Candidates.size(); }
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 429ed5718cd87..fb7f1c483c529 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14619,7 +14619,7 @@ static bool canBeDeclaredInNamespace(const DeclarationName &Name) {
static bool DiagnoseTwoPhaseLookup(
Sema &SemaRef, SourceLocation FnLoc, const CXXScopeSpec &SS,
LookupResult &R, OverloadCandidateSet::CandidateSetKind CSK,
- OverloadCandidateSet &KnownInvalidCandidateSet,
+ const OverloadCandidateSet &ResolvedCandidates,
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
CXXRecordDecl **FoundInClass = nullptr) {
if (!SemaRef.inTemplateInstantiation() || !SS.isEmpty())
@@ -14635,7 +14635,9 @@ static bool DiagnoseTwoPhaseLookup(
R.suppressDiagnostics();
OverloadCandidateSet Candidates(FnLoc, CSK);
- for (auto &Cand : KnownInvalidCandidateSet) {
+ // We have performed a BestViableFunction over these candidates, so
+ // exclude them.
+ for (auto &Cand : ResolvedCandidates) {
if (Cand.Function)
Candidates.exclude(Cand.Function);
else if (Cand.IsSurrogate)
@@ -14731,16 +14733,15 @@ static bool DiagnoseTwoPhaseLookup(
/// was defined.
///
/// 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,
- OverloadCandidateSet &KnownInvalidCandidateSet) {
+static bool DiagnoseTwoPhaseOperatorLookup(
+ Sema &SemaRef, OverloadedOperatorKind Op, SourceLocation OpLoc,
+ ArrayRef<Expr *> Args, const OverloadCandidateSet &ResolvedCandidateSet) {
DeclarationName OpName =
SemaRef.Context.DeclarationNames.getCXXOperatorName(Op);
LookupResult R(SemaRef, OpName, OpLoc, Sema::LookupOperatorName);
return DiagnoseTwoPhaseLookup(
SemaRef, OpLoc, CXXScopeSpec(), R, OverloadCandidateSet::CSK_Operator,
- KnownInvalidCandidateSet,
+ ResolvedCandidateSet,
/*ExplicitTemplateArgs=*/nullptr, Args, /*FoundInClass=*/nullptr);
}
@@ -14770,7 +14771,7 @@ static ExprResult
BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
UnresolvedLookupExpr *ULE, SourceLocation LParenLoc,
MutableArrayRef<Expr *> Args, SourceLocation RParenLoc,
- OverloadCandidateSet &KnownInvalidCandidateSet,
+ const OverloadCandidateSet &ResolvedCandidateSet,
bool AllowTypoCorrection) {
// Do not try to recover if it is already building a recovery call.
// This stops infinite loops for template instantiations like
@@ -14797,10 +14798,10 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
CXXRecordDecl *FoundInClass = nullptr;
if (DiagnoseTwoPhaseLookup(SemaRef, Fn->getExprLoc(), SS, R,
OverloadCandidateSet::CSK_Normal,
- KnownInvalidCandidateSet, ExplicitTemplateArgs,
+ ResolvedCandidateSet, ExplicitTemplateArgs,
Args, &FoundInClass)) {
// OK, diagnosed a two-phase lookup issue.
- } else if (KnownInvalidCandidateSet.empty()) {
+ } else if (ResolvedCandidateSet.empty()) {
// Try to recover from an empty lookup with typo correction.
R.clear();
NoTypoCorrectionCCC NoTypoValidator{};
More information about the cfe-commits
mailing list