From 4432f6bf828411f9c02bbab230e47adfaac3bcb9 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 5 May 2026 13:31:40 +0900 Subject: [PATCH] Implement a new flag `-Zdisable-fast-paths` in trait solving --- compiler/rustc_infer/src/infer/context.rs | 4 ++++ compiler/rustc_infer/src/infer/mod.rs | 5 +++++ compiler/rustc_middle/src/ty/context.rs | 4 ++++ .../rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs | 6 +++++- compiler/rustc_session/src/options.rs | 2 ++ compiler/rustc_trait_selection/src/solve/fulfill.rs | 5 +++-- compiler/rustc_trait_selection/src/traits/fulfill.rs | 8 +++++++- .../rustc_trait_selection/src/traits/query/type_op/mod.rs | 8 ++++++-- compiler/rustc_trait_selection/src/traits/select/mod.rs | 4 +++- compiler/rustc_traits/src/evaluate_obligation.rs | 2 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 2 ++ 11 files changed, 42 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 74da4d4c6db8..0dc1f5e5eec5 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -22,6 +22,10 @@ fn next_trait_solver(&self) -> bool { self.next_trait_solver } + fn disable_trait_solver_fast_paths(&self) -> bool { + self.disable_trait_solver_fast_paths() + } + fn typing_mode(&self) -> ty::TypingMode<'tcx> { self.typing_mode() } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 05043f8617a9..104a4e4d1ec4 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -640,6 +640,11 @@ pub fn next_trait_solver(&self) -> bool { self.next_trait_solver } + #[inline(always)] + pub fn disable_trait_solver_fast_paths(&self) -> bool { + self.tcx.disable_trait_solver_fast_paths() + } + #[inline(always)] pub fn typing_mode(&self) -> TypingMode<'tcx> { self.typing_mode diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d1048a65a7be..ba028ac9aad3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2657,6 +2657,10 @@ pub fn next_trait_solver_in_coherence(self) -> bool { self.sess.opts.unstable_opts.next_solver.coherence } + pub fn disable_trait_solver_fast_paths(self) -> bool { + self.sess.opts.unstable_opts.disable_fast_paths + } + #[allow(rustc::bad_opt_access)] pub fn use_typing_mode_borrowck(self) -> bool { self.next_trait_solver_globally() || self.sess.opts.unstable_opts.typing_mode_borrowck 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 615cc9e8f81d..b9e1758be0fd 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 @@ -447,6 +447,7 @@ pub(super) fn evaluate_goal_raw( ref sub_roots, stalled_certainty, }) = stalled_on + && !self.delegate.disable_trait_solver_fast_paths() && !stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) && !sub_roots .iter() @@ -666,7 +667,10 @@ fn evaluate_added_goals_step(&mut self) -> Result, NoSolution> // 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(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) { + if !self.delegate.disable_trait_solver_fast_paths() + && let Some(certainty) = + self.delegate.compute_goal_fast_path(goal, self.origin_span) + { match certainty { Certainty::Yes => {} Certainty::Maybe { .. } => { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f7a3387e5238..4767b0cfbe86 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2269,6 +2269,8 @@ pub(crate) fn parse_assert_incr_state( themselves (default: no)"), direct_access_external_data: Option = (None, parse_opt_bool, [TRACKED], "Direct or use GOT indirect to reference external data symbols"), + disable_fast_paths: bool = (false, parse_bool, [TRACKED], + "disable various performance optimizations in trait solving"), dual_proc_macros: bool = (false, parse_bool, [TRACKED], "load proc macros for both target and host, but only link to the target (default: no)"), dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 8848b4c40f51..d8b9c400b4d7 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -186,8 +186,9 @@ fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { let goal = obligation.as_goal(); let delegate = <&SolverDelegate<'tcx>>::from(infcx); - if let Some(certainty) = - delegate.compute_goal_fast_path(goal, obligation.cause.span) + if !delegate.disable_trait_solver_fast_paths() + && let Some(certainty) = + delegate.compute_goal_fast_path(goal, obligation.cause.span) { match certainty { // This fast path doesn't depend on region identity so it doesn't diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 0b1d4a8453d0..b4b54cd16bd5 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -325,6 +325,10 @@ fn skippable_obligations<'b>( /// compile-time benchmarks are very sensitive to even small changes. #[inline(always)] fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool { + if self.selcx.infcx.disable_trait_solver_fast_paths() { + return true; + } + // If we were stalled on some unresolved variables, first check whether // any of them have been resolved; if not, don't bother doing more work // yet. @@ -388,7 +392,9 @@ fn process_obligation( let infcx = self.selcx.infcx; - if sizedness_fast_path(infcx.tcx, obligation.predicate, obligation.param_env) { + if !infcx.disable_trait_solver_fast_paths() + && sizedness_fast_path(infcx.tcx, obligation.predicate, obligation.param_env) + { return ProcessResult::Changed(thin_vec![]); } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index b2c1f9a5eed1..3a8a3fc66e66 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -110,7 +110,9 @@ fn fully_perform_into( ), NoSolution, > { - if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { + if !infcx.disable_trait_solver_fast_paths() + && let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) + { return Ok((result, None, PredicateObligations::new(), Certainty::Proven)); } @@ -159,7 +161,9 @@ fn fully_perform( "query type op", span, |ocx| { - if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &self) { + if !infcx.disable_trait_solver_fast_paths() + && let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &self) + { return Ok(result); } QueryTypeOp::perform_locally_with_next_solver(ocx, self, span) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9eca0f31a440..3a8e2429069f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -604,7 +604,9 @@ fn evaluate_predicate_recursively<'o>( None => self.check_recursion_limit(&obligation, &obligation)?, } - if sizedness_fast_path(self.tcx(), obligation.predicate, obligation.param_env) { + if !self.infcx.disable_trait_solver_fast_paths() + && sizedness_fast_path(self.tcx(), obligation.predicate, obligation.param_env) + { return Ok(EvaluatedToOk); } diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 8f72bdf09726..6bc6543a0a01 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -24,7 +24,7 @@ fn evaluate_obligation<'tcx>( debug!("evaluate_obligation: goal={:#?}", goal); let ParamEnvAnd { param_env, value: predicate } = goal; - if sizedness_fast_path(tcx, predicate, param_env) { + if !tcx.disable_trait_solver_fast_paths() && sizedness_fast_path(tcx, predicate, param_env) { return Ok(EvaluationResult::EvaluatedToOk); } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 1b6bdf8c34dd..99588cb20aed 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -230,6 +230,8 @@ fn next_trait_solver(&self) -> bool { true } + fn disable_trait_solver_fast_paths(&self) -> bool; + fn typing_mode(&self) -> TypingMode; fn universe(&self) -> ty::UniverseIndex;