From 24e6b99544acca6b63269472520fb6afa91183e9 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 5 May 2026 08:04:36 +0700 Subject: [PATCH 1/4] obligations_for_self_ty: skip irrelevant goals --- .../src/fn_ctxt/inspect_obligations.rs | 31 +++++++++++++++--- compiler/rustc_infer/src/traits/engine.rs | 12 ++++++- .../src/solve/fulfill.rs | 32 ++++++++++++++++++- .../src/solve/inspect/analyse.rs | 4 +++ 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index 652f0498b5841..a5e6dacb05d9d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -38,10 +38,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> bool { match predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { - self.type_matches_expected_vid(expected_vid, data.self_ty()) + self.type_matches_expected_vid(data.self_ty(), expected_vid) } ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { - self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty()) + self.type_matches_expected_vid(data.projection_term.self_ty(), expected_vid) } ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::Subtype(..) @@ -61,7 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self), ret)] - fn type_matches_expected_vid(&self, expected_vid: ty::TyVid, ty: Ty<'tcx>) -> bool { + fn type_matches_expected_vid(&self, ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool { let ty = self.shallow_resolve(ty); debug!(?ty); @@ -77,7 +77,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, self_ty: ty::TyVid, ) -> PredicateObligations<'tcx> { - let obligations = self.fulfillment_cx.borrow().pending_obligations(); + // We only look at obligations which may reference the self type. + // This lookup uses the `sub_root` instead of the inference variable + // itself as that's slightly nicer to implement. It shouldn't really + // matter. + // + // This is really impactful when typechecking functions with a lot of + // stalled obligations, e.g. in the `wg-grammar` benchmark. + let sub_root_var = self.sub_unification_table_root_var(self_ty); + let obligations = self + .fulfillment_cx + .borrow() + .pending_obligations_potentially_referencing_sub_root(sub_root_var); debug!(?obligations); let mut obligations_for_self_ty = PredicateObligations::new(); for obligation in obligations { @@ -189,6 +200,18 @@ impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { return; } + // We don't care about any pending goals which don't actually + // use the self type. + if !inspect_goal + .orig_values() + .iter() + .filter_map(|arg| arg.as_type()) + .any(|ty| self.fcx.type_matches_expected_vid(ty, self.self_ty)) + { + debug!(goal = ?inspect_goal.goal(), "goal does not mention self type"); + return; + } + let tcx = self.fcx.tcx; let goal = inspect_goal.goal(); if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 39fff48de6aa6..ad06f406ac27d 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, Ty, Upcast}; +use rustc_middle::ty::{self, Ty, TyVid, Upcast}; use super::{ObligationCause, PredicateObligation, PredicateObligations}; use crate::infer::InferCtxt; @@ -108,6 +108,16 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { fn pending_obligations(&self) -> PredicateObligations<'tcx>; + /// Returning all pending obligations which reference an inference + /// variable with `_sub_root`. This assumes that no type inference + /// progress has been made since the last `try_evaluate_obligations` call. + fn pending_obligations_potentially_referencing_sub_root( + &self, + _sub_root: TyVid, + ) -> PredicateObligations<'tcx> { + self.pending_obligations() + } + /// Among all pending obligations, collect those are stalled on a inference variable which has /// changed since the last call to `try_evaluate_obligations`. Those obligations are marked as /// successful and returned. diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 8848b4c40f510..d1de426fbca00 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -6,7 +6,7 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::{ FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; -use rustc_middle::ty::{TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{self, TyCtxt, TyVid, TypeVisitableExt, TypingMode}; use rustc_next_trait_solver::delegate::SolverDelegate as _; use rustc_next_trait_solver::solve::{ GoalEvaluation, GoalStalledOn, HasChanged, MaybeInfo, SolverDelegateEvalExt as _, @@ -79,6 +79,29 @@ impl<'tcx> ObligationStorage<'tcx> { obligations } + fn clone_pending_potentially_referencing_sub_root( + &self, + vid: TyVid, + ) -> PredicateObligations<'tcx> { + let mut obligations: PredicateObligations<'tcx> = self + .pending + .iter() + .filter(|(_, stalled_on)| { + // This may incorrectly return `false` in case we've made + // inference progress since the last time we tried to evaluate + // this obligation. + if let Some(stalled_on) = stalled_on { + stalled_on.sub_roots.iter().any(|&r| r == vid) + } else { + true + } + }) + .map(|(o, _)| o.clone()) + .collect(); + obligations.extend(self.overflowed.iter().cloned()); + obligations + } + fn drain_pending( &mut self, cond: impl Fn(&PredicateObligation<'tcx>, &Option>>) -> bool, @@ -271,6 +294,13 @@ where self.obligations.clone_pending() } + fn pending_obligations_potentially_referencing_sub_root( + &self, + vid: ty::TyVid, + ) -> PredicateObligations<'tcx> { + self.obligations.clone_pending_potentially_referencing_sub_root(vid) + } + fn drain_stalled_obligations_for_coroutines( &mut self, infcx: &InferCtxt<'tcx>, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index ad66078025bc4..eeb41f3f3195b 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -317,6 +317,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { self.depth } + pub fn orig_values(&self) -> &[ty::GenericArg<'tcx>] { + &self.orig_values + } + fn candidates_recur( &'a self, candidates: &mut Vec>, From 596f7e90f63307274ed204d952f447e92804b31c Mon Sep 17 00:00:00 2001 From: inq Date: Tue, 5 May 2026 18:38:22 +0700 Subject: [PATCH 2/4] walk stalled_vars at read time for sub_root filter --- .../src/fn_ctxt/inspect_obligations.rs | 2 +- compiler/rustc_infer/src/traits/engine.rs | 4 ++-- .../rustc_trait_selection/src/solve/fulfill.rs | 16 +++++++--------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index a5e6dacb05d9d..1876dd40b5521 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -88,7 +88,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let obligations = self .fulfillment_cx .borrow() - .pending_obligations_potentially_referencing_sub_root(sub_root_var); + .pending_obligations_potentially_referencing_sub_root(&self.infcx, sub_root_var); debug!(?obligations); let mut obligations_for_self_ty = PredicateObligations::new(); for obligation in obligations { diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index ad06f406ac27d..ca2f1d73001c1 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -109,10 +109,10 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { fn pending_obligations(&self) -> PredicateObligations<'tcx>; /// Returning all pending obligations which reference an inference - /// variable with `_sub_root`. This assumes that no type inference - /// progress has been made since the last `try_evaluate_obligations` call. + /// variable with `_sub_root`. fn pending_obligations_potentially_referencing_sub_root( &self, + _infcx: &InferCtxt<'tcx>, _sub_root: TyVid, ) -> PredicateObligations<'tcx> { self.pending_obligations() diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index d1de426fbca00..d9a9789045e98 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -81,20 +81,17 @@ impl<'tcx> ObligationStorage<'tcx> { fn clone_pending_potentially_referencing_sub_root( &self, + infcx: &InferCtxt<'tcx>, vid: TyVid, ) -> PredicateObligations<'tcx> { let mut obligations: PredicateObligations<'tcx> = self .pending .iter() .filter(|(_, stalled_on)| { - // This may incorrectly return `false` in case we've made - // inference progress since the last time we tried to evaluate - // this obligation. - if let Some(stalled_on) = stalled_on { - stalled_on.sub_roots.iter().any(|&r| r == vid) - } else { - true - } + let Some(stalled_on) = stalled_on else { return true }; + stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type()).any(|ty| { + matches!(*ty.kind(), ty::Infer(ty::TyVar(tv)) if infcx.sub_unification_table_root_var(tv) == vid) + }) }) .map(|(o, _)| o.clone()) .collect(); @@ -296,9 +293,10 @@ where fn pending_obligations_potentially_referencing_sub_root( &self, + infcx: &InferCtxt<'tcx>, vid: ty::TyVid, ) -> PredicateObligations<'tcx> { - self.obligations.clone_pending_potentially_referencing_sub_root(vid) + self.obligations.clone_pending_potentially_referencing_sub_root(infcx, vid) } fn drain_stalled_obligations_for_coroutines( From 7173239a21a68d38443dc26045298fdd6a0ce834 Mon Sep 17 00:00:00 2001 From: inq Date: Tue, 5 May 2026 19:53:58 +0700 Subject: [PATCH 3/4] shallow_resolve stalled vars + conservative include --- compiler/rustc_trait_selection/src/solve/fulfill.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index d9a9789045e98..c1b51723833d5 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -89,8 +89,14 @@ impl<'tcx> ObligationStorage<'tcx> { .iter() .filter(|(_, stalled_on)| { let Some(stalled_on) = stalled_on else { return true }; + // Conservative here: if any of its stalled vars are not infer var anymore, + // some unification happened, so this goal is no longer stalled. + // So include it to re-evaluate in the downstream. stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type()).any(|ty| { - matches!(*ty.kind(), ty::Infer(ty::TyVar(tv)) if infcx.sub_unification_table_root_var(tv) == vid) + match *infcx.shallow_resolve(ty).kind() { + ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid, + _ => true, + } }) }) .map(|(o, _)| o.clone()) From 38cdf1cdc59b464c3947cb2237c979b64cafdd0b Mon Sep 17 00:00:00 2001 From: inq Date: Tue, 5 May 2026 20:25:09 +0700 Subject: [PATCH 4/4] format fix --- compiler/rustc_trait_selection/src/solve/fulfill.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index c1b51723833d5..df2335d4eae17 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -92,12 +92,12 @@ impl<'tcx> ObligationStorage<'tcx> { // Conservative here: if any of its stalled vars are not infer var anymore, // some unification happened, so this goal is no longer stalled. // So include it to re-evaluate in the downstream. - stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type()).any(|ty| { - match *infcx.shallow_resolve(ty).kind() { + stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type()).any( + |ty| match *infcx.shallow_resolve(ty).kind() { ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid, _ => true, - } - }) + }, + ) }) .map(|(o, _)| o.clone()) .collect();