From b67bb92bab28e82ea0a5974e872bacac3c7f05a7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 28 May 2025 22:33:57 +0000 Subject: [PATCH] Fast path for subtype and coercion goals --- .../rustc_next_trait_solver/src/delegate.rs | 4 +--- .../src/solve/eval_ctxt/mod.rs | 11 +++++---- .../src/solve/delegate.rs | 24 +++++++++++++------ .../src/solve/fulfill.rs | 9 +++++-- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 32dc85b3e6a71..7b932010d498a 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -3,8 +3,6 @@ use std::ops::Deref; use rustc_type_ir::solve::{Certainty, Goal, NoSolution}; use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable}; -use crate::solve::HasChanged; - pub trait SolverDelegate: Deref + Sized { type Infcx: InferCtxtLike; type Interner: Interner; @@ -23,7 +21,7 @@ pub trait SolverDelegate: Deref + Sized { &self, goal: Goal::Predicate>, span: ::Span, - ) -> Option; + ) -> Option; fn fresh_var_for_kind_with_span( &self, diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 2924208173d2b..776442b9020b4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -671,10 +671,13 @@ where // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); for (source, goal, stalled_on) in mem::take(&mut self.nested_goals) { - if let Some(has_changed) = self.delegate.compute_goal_fast_path(goal, self.origin_span) - { - if matches!(has_changed, HasChanged::Yes) { - unchanged_certainty = None; + if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) { + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, goal, None)); + unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); + } } continue; } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 038cdc1a56499..c0c6b018d1093 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -12,7 +12,6 @@ use rustc_infer::traits::solve::Goal; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, TypingMode}; -use rustc_next_trait_solver::solve::HasChanged; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use crate::traits::{EvaluateConstErr, ObligationCause, specialization_graph}; @@ -61,11 +60,11 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< &self, goal: Goal<'tcx, ty::Predicate<'tcx>>, span: Span, - ) -> Option { + ) -> Option { let pred = goal.predicate.kind(); match pred.no_bound_vars()? { ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => { - Some(HasChanged::No) + Some(Certainty::Yes) } ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => { self.0.sub_regions( @@ -73,7 +72,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< outlives.1, outlives.0, ); - Some(HasChanged::No) + Some(Certainty::Yes) } ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => { self.0.register_type_outlives_constraint( @@ -82,23 +81,34 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< &ObligationCause::dummy_with_span(span), ); - Some(HasChanged::No) + Some(Certainty::Yes) } ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => { match self.0.tcx.as_lang_item(trait_pred.def_id()) { Some(LangItem::Sized) if trait_pred.self_ty().is_trivially_sized(self.0.tcx) => { - Some(HasChanged::No) + Some(Certainty::Yes) } Some(LangItem::Copy | LangItem::Clone) if trait_pred.self_ty().is_trivially_pure_clone_copy() => { - Some(HasChanged::No) + Some(Certainty::Yes) } _ => None, } } + ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. }) + | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { + if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() { + // FIXME: We also need to register a subtype relation between these vars + // when those are added, and if they aren't in the same sub root then + // we should mark this goal as `has_changed`. + Some(Certainty::AMBIGUOUS) + } else { + None + } + } _ => None, } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index d273703a9b121..ed99c678a4d2e 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -195,10 +195,15 @@ where let goal = obligation.as_goal(); let delegate = <&SolverDelegate<'tcx>>::from(infcx); - if let Some(fast_path_has_changed) = + if let Some(certainty) = delegate.compute_goal_fast_path(goal, obligation.cause.span) { - any_changed |= matches!(fast_path_has_changed, HasChanged::Yes); + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.obligations.register(obligation, None); + } + } continue; }