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 367e2b6b372ae..fa35cfed959ec 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -37,10 +37,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(..) @@ -60,7 +60,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); @@ -76,7 +76,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 { @@ -125,6 +136,18 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, '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 4a252719694d5..216c16bd11e92 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; @@ -106,6 +106,15 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { fn has_pending_obligations(&self) -> bool; 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 `select_where_possible` 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 `select_where_possible`. Those obligations are marked as diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index bff4f6ce3fc6b..18781c88f88d9 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -10,8 +10,8 @@ use rustc_infer::traits::{ FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; use rustc_middle::ty::{ - self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, - TypingMode, + self, DelayedSet, Ty, TyCtxt, TyVid, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + TypeVisitor, TypingMode, }; use rustc_next_trait_solver::delegate::SolverDelegate as _; use rustc_next_trait_solver::solve::{ @@ -85,8 +85,30 @@ impl<'tcx> ObligationStorage<'tcx> { obligations.extend(self.overflowed.iter().cloned()); 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( + fn drain_pending_ignoring_overflowed( &mut self, cond: impl Fn(&PredicateObligation<'tcx>) -> bool, ) -> PendingObligations<'tcx> { @@ -184,7 +206,9 @@ where let mut errors = Vec::new(); loop { let mut any_changed = false; - for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) { + for (mut obligation, stalled_on) in + self.obligations.drain_pending_ignoring_overflowed(|_| true) + { if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) { self.obligations.on_fulfillment_overflow(infcx); // Only return true errors that we have accumulated while processing. @@ -277,6 +301,12 @@ where fn pending_obligations(&self) -> PredicateObligations<'tcx> { 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, @@ -297,7 +327,7 @@ where } self.obligations - .drain_pending(|obl| { + .drain_pending_ignoring_overflowed(|obl| { infcx.probe(|_| { infcx .visit_proof_tree( diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 086a7a44786d6..f782a9bb13901 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>,