[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