diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 7ac1ef19bbc9..d06450820ce2 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -21,10 +21,9 @@ use rustc_middle::ty::{ use rustc_span::DUMMY_SP; use std::ops::ControlFlow; -use crate::solve::inspect::DebugSolver; use crate::traits::specialization_graph; -use super::inspect::InspectSolve; +use super::inspect::ProofTreeBuilder; use super::search_graph::{self, OverflowHandler}; use super::SolverMode; use super::{search_graph::SearchGraph, Goal}; @@ -76,7 +75,7 @@ pub struct EvalCtxt<'a, 'tcx> { // evaluation code. tainted: Result<(), NoSolution>, - inspect: Box + 'tcx>, + inspect: ProofTreeBuilder<'tcx>, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -147,25 +146,20 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { var_values: CanonicalVarValues::dummy(), nested_goals: NestedGoals::new(), tainted: Ok(()), - inspect: match self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree { - true => Box::new(DebugSolver::new()), - false => Box::new(()), - }, + inspect: self + .tcx + .sess + .opts + .unstable_opts + .dump_solver_proof_tree + .then(ProofTreeBuilder::new_root) + .unwrap_or_else(ProofTreeBuilder::new_noop), }; let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal); - let tcx = ecx.tcx(); - match ecx.inspect.into_debug_solver() { - Some(tree) => match Box::leak(tree) { - DebugSolver::GoalEvaluation(tree) => { - if tcx.sess.opts.unstable_opts.dump_solver_proof_tree { - println!("{:?}", tree); - } - } - _ => unreachable!("unable to convert to `DebugSolver::GoalEvaluation`"), - }, - _ => unreachable!("unable to convert to `DebugSolver::GoalEvaluation`"), - }; + if let Some(tree) = ecx.inspect.into_proof_tree() { + println!("{:?}", tree); + } assert!( ecx.nested_goals.is_empty(), @@ -196,7 +190,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, search_graph: &'a mut search_graph::SearchGraph<'tcx>, canonical_input: CanonicalInput<'tcx>, - mut goal_evaluation: &mut dyn InspectSolve<'tcx>, + mut goal_evaluation: &mut ProofTreeBuilder<'tcx>, ) -> QueryResult<'tcx> { goal_evaluation.canonicalized_goal(canonical_input); @@ -272,7 +266,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.tcx(), self.search_graph, canonical_goal, - &mut *goal_evaluation, + &mut goal_evaluation, ); goal_evaluation.query_result(canonical_response); self.inspect.goal_evaluation(goal_evaluation); @@ -309,7 +303,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.search_graph, canonical_goal, // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal` - &mut (), + &mut ProofTreeBuilder::new_noop(), )?; // We only check for modulo regions as we convert all regions in // the input to new existentials, even if they're expected to be diff --git a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs index 0dcee5a92966..20d77f86ca88 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs @@ -15,101 +15,44 @@ pub enum DebugSolver<'tcx> { GoalCandidate(GoalCandidate<'tcx>), } -pub trait InspectSolve<'tcx> { - fn into_debug_solver(self: Box) -> Option>>; +pub struct ProofTreeBuilder<'tcx>(Option>>); - fn new_goal_evaluation( +impl<'tcx> ProofTreeBuilder<'tcx> { + pub fn into_proof_tree(self) -> Option> { + self.0.map(|tree| *tree) + } + + pub fn new_root() -> ProofTreeBuilder<'tcx> { + Self(Some(Box::new(DebugSolver::Root))) + } + + pub fn new_noop() -> ProofTreeBuilder<'tcx> { + Self(None) + } + + pub fn new_goal_evaluation( &mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> Box + 'tcx>; - fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>); - fn cache_hit(&mut self, cache_hit: CacheHit); - fn goal_evaluation(&mut self, goal_evaluation: Box + 'tcx>); + ) -> ProofTreeBuilder<'tcx> { + if self.0.is_none() { + return ProofTreeBuilder(None); + } - fn new_goal_evaluation_step( - &mut self, - instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, - ) -> Box + 'tcx>; - fn goal_evaluation_step(&mut self, goal_eval_step: Box + 'tcx>); - - fn new_goal_candidate(&mut self) -> Box + 'tcx>; - fn candidate_name(&mut self, f: &mut dyn FnMut() -> String); - fn goal_candidate(&mut self, candidate: Box + 'tcx>); - - fn new_evaluate_added_goals(&mut self) -> Box + 'tcx>; - fn evaluate_added_goals_loop_start(&mut self); - fn eval_added_goals_result(&mut self, result: Result); - fn added_goals_evaluation(&mut self, goals_evaluation: Box + 'tcx>); - - fn query_result(&mut self, result: QueryResult<'tcx>); -} - -/// No-op `InspectSolve` impl to use for normal trait solving when we do not want -/// to take a performance hit from recording information about how things are being -/// proven. -impl<'tcx> InspectSolve<'tcx> for () { - fn into_debug_solver(self: Box) -> Option>> { - None - } - - fn new_goal_evaluation( - &mut self, - _goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> Box + 'tcx> { - Box::new(()) - } - fn canonicalized_goal(&mut self, _canonical_goal: CanonicalInput<'tcx>) {} - fn cache_hit(&mut self, _cache_hit: CacheHit) {} - fn goal_evaluation(&mut self, _goal_evaluation: Box + 'tcx>) {} - - fn new_goal_evaluation_step( - &mut self, - _instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, - ) -> Box + 'tcx> { - Box::new(()) - } - fn goal_evaluation_step(&mut self, _goal_eval_step: Box + 'tcx>) {} - - fn new_goal_candidate(&mut self) -> Box + 'tcx> { - Box::new(()) - } - fn candidate_name(&mut self, _f: &mut dyn FnMut() -> String) {} - fn goal_candidate(&mut self, _candidate: Box + 'tcx>) {} - - fn new_evaluate_added_goals(&mut self) -> Box + 'tcx> { - Box::new(()) - } - fn evaluate_added_goals_loop_start(&mut self) {} - fn eval_added_goals_result(&mut self, _result: Result) {} - fn added_goals_evaluation(&mut self, _goals_evaluation: Box + 'tcx>) {} - - fn query_result(&mut self, _result: QueryResult<'tcx>) {} -} - -impl<'tcx> DebugSolver<'tcx> { - pub fn new() -> Self { - Self::Root - } -} -impl<'tcx> InspectSolve<'tcx> for DebugSolver<'tcx> { - fn into_debug_solver(self: Box) -> Option>> { - Some(self) - } - - fn new_goal_evaluation( - &mut self, - goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> Box + 'tcx> { - Box::new(DebugSolver::GoalEvaluation(GoalEvaluation { + Self(Some(Box::new(DebugSolver::GoalEvaluation(GoalEvaluation { uncanonicalized_goal: goal, canonicalized_goal: None, evaluation_steps: vec![], cache_hit: None, result: None, - })) + })))) } - fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) { - match self { + pub fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; + + match this { DebugSolver::GoalEvaluation(goal_evaluation) => { assert!(goal_evaluation.canonicalized_goal.is_none()); goal_evaluation.canonicalized_goal = Some(canonical_goal) @@ -117,17 +60,26 @@ impl<'tcx> InspectSolve<'tcx> for DebugSolver<'tcx> { _ => unreachable!(), } } - fn cache_hit(&mut self, cache_hit: CacheHit) { - match self { + pub fn cache_hit(&mut self, cache_hit: CacheHit) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; + + match this { DebugSolver::GoalEvaluation(goal_evaluation) => { goal_evaluation.cache_hit = Some(cache_hit) } _ => unreachable!(), }; } - fn goal_evaluation(&mut self, goal_evaluation: Box + 'tcx>) { - let goal_evaluation = goal_evaluation.into_debug_solver().unwrap(); - match (self, *goal_evaluation) { + pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; + + match (this, *goal_evaluation.0.unwrap()) { ( DebugSolver::AddedGoalsEvaluation(AddedGoalsEvaluation { evaluations, .. }), DebugSolver::GoalEvaluation(goal_evaluation), @@ -137,20 +89,24 @@ impl<'tcx> InspectSolve<'tcx> for DebugSolver<'tcx> { } } - fn new_goal_evaluation_step( + pub fn new_goal_evaluation_step( &mut self, instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, - ) -> Box + 'tcx> { - Box::new(DebugSolver::GoalEvaluationStep(GoalEvaluationStep { + ) -> ProofTreeBuilder<'tcx> { + Self(Some(Box::new(DebugSolver::GoalEvaluationStep(GoalEvaluationStep { instantiated_goal, nested_goal_evaluations: vec![], candidates: vec![], result: None, - })) + })))) } - fn goal_evaluation_step(&mut self, goal_eval_step: Box + 'tcx>) { - let goal_eval_step = goal_eval_step.into_debug_solver().unwrap(); - match (self, *goal_eval_step) { + pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; + + match (this, *goal_eval_step.0.unwrap()) { (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => { goal_eval.evaluation_steps.push(step); } @@ -158,28 +114,36 @@ impl<'tcx> InspectSolve<'tcx> for DebugSolver<'tcx> { } } - fn new_goal_candidate(&mut self) -> Box + 'tcx> { - Box::new(DebugSolver::GoalCandidate(GoalCandidate { + pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> { + Self(Some(Box::new(DebugSolver::GoalCandidate(GoalCandidate { nested_goal_evaluations: vec![], candidates: vec![], name: None, result: None, - })) + })))) } - fn candidate_name(&mut self, f: &mut dyn FnMut() -> String) { - let name = f(); + pub fn candidate_name(&mut self, f: &mut dyn FnMut() -> String) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; - match self { + match this { DebugSolver::GoalCandidate(goal_candidate) => { + let name = f(); assert!(goal_candidate.name.is_none()); goal_candidate.name = Some(name); } _ => unreachable!(), } } - fn goal_candidate(&mut self, candidate: Box + 'tcx>) { - let candidate = candidate.into_debug_solver().unwrap(); - match (self, *candidate) { + pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; + + match (this, *candidate.0.unwrap()) { ( DebugSolver::GoalCandidate(GoalCandidate { candidates, .. }) | DebugSolver::GoalEvaluationStep(GoalEvaluationStep { candidates, .. }), @@ -189,22 +153,32 @@ impl<'tcx> InspectSolve<'tcx> for DebugSolver<'tcx> { } } - fn new_evaluate_added_goals(&mut self) -> Box + 'tcx> { - Box::new(DebugSolver::AddedGoalsEvaluation(AddedGoalsEvaluation { + pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { + Self(Some(Box::new(DebugSolver::AddedGoalsEvaluation(AddedGoalsEvaluation { evaluations: vec![], result: None, - })) + })))) } - fn evaluate_added_goals_loop_start(&mut self) { - match self { + pub fn evaluate_added_goals_loop_start(&mut self) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; + + match this { DebugSolver::AddedGoalsEvaluation(this) => { this.evaluations.push(vec![]); } _ => unreachable!(), } } - fn eval_added_goals_result(&mut self, result: Result) { - match self { + pub fn eval_added_goals_result(&mut self, result: Result) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; + + match this { DebugSolver::AddedGoalsEvaluation(this) => { assert!(this.result.is_none()); this.result = Some(result); @@ -212,9 +186,13 @@ impl<'tcx> InspectSolve<'tcx> for DebugSolver<'tcx> { _ => unreachable!(), } } - fn added_goals_evaluation(&mut self, goals_evaluation: Box + 'tcx>) { - let goals_evaluation = goals_evaluation.into_debug_solver().unwrap(); - match (self, *goals_evaluation) { + pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; + + match (this, *goals_evaluation.0.unwrap()) { ( DebugSolver::GoalEvaluationStep(GoalEvaluationStep { nested_goal_evaluations, .. @@ -226,8 +204,13 @@ impl<'tcx> InspectSolve<'tcx> for DebugSolver<'tcx> { } } - fn query_result(&mut self, result: QueryResult<'tcx>) { - match self { + pub fn query_result(&mut self, result: QueryResult<'tcx>) { + let this = match self.0.as_mut() { + None => return, + Some(this) => &mut **this, + }; + + match this { DebugSolver::GoalEvaluation(goal_evaluation) => { assert!(goal_evaluation.result.is_none()); goal_evaluation.result = Some(result); diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 86cc1778c595..d167ee46b393 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -13,7 +13,7 @@ use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryRe use rustc_middle::ty::TyCtxt; use std::{collections::hash_map::Entry, mem}; -use super::inspect::InspectSolve; +use super::inspect::ProofTreeBuilder; use super::SolverMode; rustc_index::newtype_index! { @@ -95,7 +95,7 @@ impl<'tcx> SearchGraph<'tcx> { &mut self, tcx: TyCtxt<'tcx>, input: CanonicalInput<'tcx>, - inspect: &mut dyn InspectSolve<'tcx>, + inspect: &mut ProofTreeBuilder<'tcx>, ) -> Result<(), QueryResult<'tcx>> { // Look at the provisional cache to check for cycles. let cache = &mut self.provisional_cache; @@ -210,8 +210,8 @@ impl<'tcx> SearchGraph<'tcx> { &mut self, tcx: TyCtxt<'tcx>, canonical_input: CanonicalInput<'tcx>, - inspect: &mut dyn InspectSolve<'tcx>, - mut loop_body: impl FnMut(&mut Self, &mut dyn InspectSolve<'tcx>) -> QueryResult<'tcx>, + inspect: &mut ProofTreeBuilder<'tcx>, + mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { if self.should_use_global_cache() { if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {