diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 4e2af3816ac..dc51ded1b80 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -16,12 +16,15 @@ pub enum CacheHit { #[derive(Eq, PartialEq, Hash, HashStable)] pub struct GoalEvaluation<'tcx> { pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>, - pub canonicalized_goal: CanonicalInput<'tcx>, - - pub kind: GoalEvaluationKind<'tcx>, pub is_normalizes_to_hack: IsNormalizesToHack, + pub evaluation: CanonicalGoalEvaluation<'tcx>, pub returned_goals: Vec>>, +} +#[derive(Eq, PartialEq, Hash, HashStable)] +pub struct CanonicalGoalEvaluation<'tcx> { + pub goal: CanonicalInput<'tcx>, + pub kind: GoalEvaluationKind<'tcx>, pub result: QueryResult<'tcx>, } @@ -41,30 +44,20 @@ pub struct AddedGoalsEvaluation<'tcx> { pub evaluations: Vec>>, pub result: Result, } -impl Debug for AddedGoalsEvaluation<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - ProofTreeFormatter::new(f).format_nested_goal_evaluation(self) - } -} #[derive(Eq, PartialEq, Hash, HashStable)] pub struct GoalEvaluationStep<'tcx> { pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, - pub nested_goal_evaluations: Vec>, + pub added_goals_evaluations: Vec>, pub candidates: Vec>, pub result: QueryResult<'tcx>, } -impl Debug for GoalEvaluationStep<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - ProofTreeFormatter::new(f).format_evaluation_step(self) - } -} #[derive(Eq, PartialEq, Hash, HashStable)] pub struct GoalCandidate<'tcx> { - pub nested_goal_evaluations: Vec>, + pub added_goals_evaluations: Vec>, pub candidates: Vec>, pub kind: CandidateKind<'tcx>, } @@ -83,8 +76,3 @@ pub enum CandidateKind<'tcx> { /// the source type upholds all of the target type's object bounds. UpcastProbe, } -impl Debug for GoalCandidate<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - ProofTreeFormatter::new(f).format_candidate(self) - } -} diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 8759fecb05a..a9f2b76baf9 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -39,44 +39,49 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { func(&mut ProofTreeFormatter { f: &mut Indentor { f: self.f, on_newline: true } }) } - pub(super) fn format_goal_evaluation(&mut self, goal: &GoalEvaluation<'_>) -> std::fmt::Result { - let goal_text = match goal.is_normalizes_to_hack { + pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result { + let goal_text = match eval.is_normalizes_to_hack { IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL", IsNormalizesToHack::No => "GOAL", }; + writeln!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?; + self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation))?; + if eval.returned_goals.len() > 0 { + writeln!(self.f, "NESTED GOALS ADDED TO CALLER: [")?; + self.nested(|this| { + for goal in eval.returned_goals.iter() { + writeln!(this.f, "ADDED GOAL: {goal:?},")?; + } + Ok(()) + })?; - writeln!(self.f, "{}: {:?}", goal_text, goal.uncanonicalized_goal)?; - writeln!(self.f, "CANONICALIZED: {:?}", goal.canonicalized_goal)?; + writeln!(self.f, "]") + } else { + Ok(()) + } + } - match &goal.kind { + pub(super) fn format_canonical_goal_evaluation( + &mut self, + eval: &CanonicalGoalEvaluation<'_>, + ) -> std::fmt::Result { + writeln!(self.f, "GOAL: {:?}", eval.goal)?; + + match &eval.kind { GoalEvaluationKind::CacheHit(CacheHit::Global) => { - writeln!(self.f, "GLOBAL CACHE HIT: {:?}", goal.result) + writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result) } GoalEvaluationKind::CacheHit(CacheHit::Provisional) => { - writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", goal.result) + writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result) } GoalEvaluationKind::Uncached { revisions } => { for (n, step) in revisions.iter().enumerate() { writeln!(self.f, "REVISION {n}: {:?}", step.result)?; self.nested(|this| this.format_evaluation_step(step))?; } - writeln!(self.f, "RESULT: {:?}", goal.result) + writeln!(self.f, "RESULT: {:?}", eval.result) } - }?; - - if goal.returned_goals.len() > 0 { - writeln!(self.f, "NESTED GOALS ADDED TO CALLER: [")?; - self.nested(|this| { - for goal in goal.returned_goals.iter() { - writeln!(this.f, "ADDED GOAL: {goal:?},")?; - } - Ok(()) - })?; - - writeln!(self.f, "]")?; } - - Ok(()) } pub(super) fn format_evaluation_step( @@ -88,8 +93,8 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { for candidate in &evaluation_step.candidates { self.nested(|this| this.format_candidate(candidate))?; } - for nested in &evaluation_step.nested_goal_evaluations { - self.nested(|this| this.format_nested_goal_evaluation(nested))?; + for nested in &evaluation_step.added_goals_evaluations { + self.nested(|this| this.format_added_goals_evaluation(nested))?; } Ok(()) @@ -115,20 +120,20 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { for candidate in &candidate.candidates { this.format_candidate(candidate)?; } - for nested in &candidate.nested_goal_evaluations { - this.format_nested_goal_evaluation(nested)?; + for nested in &candidate.added_goals_evaluations { + this.format_added_goals_evaluation(nested)?; } Ok(()) }) } - pub(super) fn format_nested_goal_evaluation( + pub(super) fn format_added_goals_evaluation( &mut self, - nested_goal_evaluation: &AddedGoalsEvaluation<'_>, + added_goals_evaluation: &AddedGoalsEvaluation<'_>, ) -> std::fmt::Result { - writeln!(self.f, "TRY_EVALUATE_ADDED_GOALS: {:?}", nested_goal_evaluation.result)?; + writeln!(self.f, "TRY_EVALUATE_ADDED_GOALS: {:?}", added_goals_evaluation.result)?; - for (n, revision) in nested_goal_evaluation.evaluations.iter().enumerate() { + for (n, revision) in added_goals_evaluation.evaluations.iter().enumerate() { writeln!(self.f, "REVISION {n}")?; self.nested(|this| { for goal_evaluation in revision { diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 63ae83c8ef4..2901baa017e 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -237,7 +237,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, search_graph: &'a mut search_graph::SearchGraph<'tcx>, canonical_input: CanonicalInput<'tcx>, - goal_evaluation: &mut ProofTreeBuilder<'tcx>, + canonical_goal_evaluation: &mut ProofTreeBuilder<'tcx>, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R, ) -> R { let intercrate = match search_graph.solver_mode() { @@ -260,7 +260,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { search_graph, nested_goals: NestedGoals::new(), tainted: Ok(()), - inspect: goal_evaluation.new_goal_evaluation_step(input), + inspect: canonical_goal_evaluation.new_goal_evaluation_step(input), }; for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { @@ -274,7 +274,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let result = f(&mut ecx, input.goal); - goal_evaluation.goal_evaluation_step(ecx.inspect); + canonical_goal_evaluation.goal_evaluation_step(ecx.inspect); // When creating a query response we clone the opaque type constraints // instead of taking them. This would cause an ICE here, since we have @@ -302,24 +302,25 @@ 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 ProofTreeBuilder<'tcx>, + goal_evaluation: &mut ProofTreeBuilder<'tcx>, ) -> QueryResult<'tcx> { - goal_evaluation.canonicalized_goal(canonical_input); + let mut canonical_goal_evaluation = + goal_evaluation.new_canonical_goal_evaluation(canonical_input); // Deal with overflow, caching, and coinduction. // // The actual solver logic happens in `ecx.compute_goal`. - ensure_sufficient_stack(|| { + let result = ensure_sufficient_stack(|| { search_graph.with_new_goal( tcx, canonical_input, - goal_evaluation, - |search_graph, goal_evaluation| { + &mut canonical_goal_evaluation, + |search_graph, canonical_goal_evaluation| { EvalCtxt::enter_canonical( tcx, search_graph, canonical_input, - goal_evaluation, + canonical_goal_evaluation, |ecx, goal| { let result = ecx.compute_goal(goal); ecx.inspect.query_result(result); @@ -328,7 +329,11 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ) }, ) - }) + }); + + canonical_goal_evaluation.query_result(result); + goal_evaluation.canonical_goal_evaluation(canonical_goal_evaluation); + result } /// Recursively evaluates `goal`, returning whether any inference vars have @@ -347,7 +352,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { canonical_goal, &mut goal_evaluation, ); - goal_evaluation.query_result(canonical_response); let canonical_response = match canonical_response { Err(e) => { self.inspect.goal_evaluation(goal_evaluation); diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs index cda68396321..05c180f5049 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect.rs @@ -12,36 +12,47 @@ use super::GenerateProofTree; #[derive(Eq, PartialEq, Debug, Hash, HashStable)] pub struct WipGoalEvaluation<'tcx> { pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>, - pub canonicalized_goal: Option>, - - pub evaluation_steps: Vec>, - - pub cache_hit: Option, + pub evaluation: Option>, pub is_normalizes_to_hack: IsNormalizesToHack, pub returned_goals: Vec>>, - - pub result: Option>, } impl<'tcx> WipGoalEvaluation<'tcx> { pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> { inspect::GoalEvaluation { uncanonicalized_goal: self.uncanonicalized_goal, - canonicalized_goal: self.canonicalized_goal.unwrap(), - kind: match self.cache_hit { - Some(hit) => inspect::GoalEvaluationKind::CacheHit(hit), - None => inspect::GoalEvaluationKind::Uncached { + evaluation: self.evaluation.unwrap().finalize(), + is_normalizes_to_hack: self.is_normalizes_to_hack, + returned_goals: self.returned_goals, + } + } +} + +#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +pub struct WipCanonicalGoalEvaluation<'tcx> { + pub goal: CanonicalInput<'tcx>, + pub cache_hit: Option, + pub evaluation_steps: Vec>, + pub result: Option>, +} + +impl<'tcx> WipCanonicalGoalEvaluation<'tcx> { + pub fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> { + let kind = match self.cache_hit { + Some(hit) => inspect::GoalEvaluationKind::CacheHit(hit), + None => { + assert!(!self.evaluation_steps.is_empty()); + inspect::GoalEvaluationKind::Uncached { revisions: self .evaluation_steps .into_iter() .map(WipGoalEvaluationStep::finalize) .collect(), - }, - }, - is_normalizes_to_hack: self.is_normalizes_to_hack, - returned_goals: self.returned_goals, - result: self.result.unwrap(), - } + } + } + }; + + inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() } } } @@ -70,7 +81,7 @@ impl<'tcx> WipAddedGoalsEvaluation<'tcx> { pub struct WipGoalEvaluationStep<'tcx> { pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, - pub nested_goal_evaluations: Vec>, + pub added_goals_evaluations: Vec>, pub candidates: Vec>, pub result: Option>, @@ -80,8 +91,8 @@ impl<'tcx> WipGoalEvaluationStep<'tcx> { pub fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> { inspect::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, - nested_goal_evaluations: self - .nested_goal_evaluations + added_goals_evaluations: self + .added_goals_evaluations .into_iter() .map(WipAddedGoalsEvaluation::finalize) .collect(), @@ -93,7 +104,7 @@ impl<'tcx> WipGoalEvaluationStep<'tcx> { #[derive(Eq, PartialEq, Debug, Hash, HashStable)] pub struct WipGoalCandidate<'tcx> { - pub nested_goal_evaluations: Vec>, + pub added_goals_evaluations: Vec>, pub candidates: Vec>, pub kind: Option>, } @@ -101,8 +112,8 @@ pub struct WipGoalCandidate<'tcx> { impl<'tcx> WipGoalCandidate<'tcx> { pub fn finalize(self) -> inspect::GoalCandidate<'tcx> { inspect::GoalCandidate { - nested_goal_evaluations: self - .nested_goal_evaluations + added_goals_evaluations: self + .added_goals_evaluations .into_iter() .map(WipAddedGoalsEvaluation::finalize) .collect(), @@ -116,6 +127,7 @@ impl<'tcx> WipGoalCandidate<'tcx> { pub enum DebugSolver<'tcx> { Root, GoalEvaluation(WipGoalEvaluation<'tcx>), + CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<'tcx>), AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>), GoalEvaluationStep(WipGoalEvaluationStep<'tcx>), GoalCandidate(WipGoalCandidate<'tcx>), @@ -127,6 +139,12 @@ impl<'tcx> From> for DebugSolver<'tcx> { } } +impl<'tcx> From> for DebugSolver<'tcx> { + fn from(g: WipCanonicalGoalEvaluation<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::CanonicalGoalEvaluation(g) + } +} + impl<'tcx> From> for DebugSolver<'tcx> { fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> { DebugSolver::AddedGoalsEvaluation(g) @@ -243,21 +261,35 @@ impl<'tcx> ProofTreeBuilder<'tcx> { self.nested(WipGoalEvaluation { uncanonicalized_goal: goal, - canonicalized_goal: None, - evaluation_steps: vec![], + evaluation: None, is_normalizes_to_hack, - cache_hit: None, returned_goals: vec![], + }) + } + + pub fn new_canonical_goal_evaluation( + &mut self, + goal: CanonicalInput<'tcx>, + ) -> ProofTreeBuilder<'tcx> { + if self.state.is_none() { + return ProofTreeBuilder { state: None }; + } + + self.nested(WipCanonicalGoalEvaluation { + goal, + cache_hit: None, + evaluation_steps: vec![], result: None, }) } - pub fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) { + pub fn canonical_goal_evaluation(&mut self, canonical_goal_evaluation: ProofTreeBuilder<'tcx>) { if let Some(this) = self.as_mut() { - match this { - DebugSolver::GoalEvaluation(goal_evaluation) => { - assert_eq!(goal_evaluation.canonicalized_goal.replace(canonical_goal), None); - } + match (this, canonical_goal_evaluation.state.unwrap().tree) { + ( + DebugSolver::GoalEvaluation(goal_evaluation), + DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation), + ) => goal_evaluation.evaluation = Some(canonical_goal_evaluation), _ => unreachable!(), } } @@ -266,8 +298,8 @@ impl<'tcx> ProofTreeBuilder<'tcx> { pub fn cache_hit(&mut self, cache_hit: CacheHit) { if let Some(this) = self.as_mut() { match this { - DebugSolver::GoalEvaluation(goal_evaluation) => { - assert_eq!(goal_evaluation.cache_hit.replace(cache_hit), None); + DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => { + assert_eq!(canonical_goal_evaluation.cache_hit.replace(cache_hit), None); } _ => unreachable!(), }; @@ -310,16 +342,19 @@ impl<'tcx> ProofTreeBuilder<'tcx> { self.nested(WipGoalEvaluationStep { instantiated_goal, - nested_goal_evaluations: vec![], + added_goals_evaluations: vec![], candidates: vec![], result: None, }) } - pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) { + pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) { if let Some(this) = self.as_mut() { - match (this, goal_eval_step.state.unwrap().tree) { - (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => { - goal_eval.evaluation_steps.push(step); + match (this, goal_evaluation_step.state.unwrap().tree) { + ( + DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations), + DebugSolver::GoalEvaluationStep(goal_evaluation_step), + ) => { + canonical_goal_evaluations.evaluation_steps.push(goal_evaluation_step); } _ => unreachable!(), } @@ -332,7 +367,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } self.nested(WipGoalCandidate { - nested_goal_evaluations: vec![], + added_goals_evaluations: vec![], candidates: vec![], kind: None, }) @@ -392,19 +427,19 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } - pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) { + pub fn added_goals_evaluation(&mut self, added_goals_evaluation: ProofTreeBuilder<'tcx>) { if let Some(this) = self.as_mut() { - match (this, goals_evaluation.state.unwrap().tree) { + match (this, added_goals_evaluation.state.unwrap().tree) { ( DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { - nested_goal_evaluations, + added_goals_evaluations, .. }) | DebugSolver::GoalCandidate(WipGoalCandidate { - nested_goal_evaluations, .. + added_goals_evaluations, .. }), DebugSolver::AddedGoalsEvaluation(added_goals_evaluation), - ) => nested_goal_evaluations.push(added_goals_evaluation), + ) => added_goals_evaluations.push(added_goals_evaluation), _ => unreachable!(), } } @@ -413,15 +448,13 @@ impl<'tcx> ProofTreeBuilder<'tcx> { pub fn query_result(&mut self, result: QueryResult<'tcx>) { if let Some(this) = self.as_mut() { match this { - DebugSolver::GoalEvaluation(goal_evaluation) => { - assert_eq!(goal_evaluation.result.replace(result), None); + DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => { + assert_eq!(canonical_goal_evaluation.result.replace(result), None); } DebugSolver::GoalEvaluationStep(evaluation_step) => { assert_eq!(evaluation_step.result.replace(result), None); } - DebugSolver::Root - | DebugSolver::AddedGoalsEvaluation(_) - | DebugSolver::GoalCandidate(_) => unreachable!(), + _ => unreachable!(), } } } 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 52a11abd545..60c348d892b 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -200,6 +200,7 @@ impl<'tcx> SearchGraph<'tcx> { available_depth, ) { + inspect.cache_hit(CacheHit::Global); self.on_cache_hit(reached_depth, encountered_overflow); return result; }