From fc452e2ed3a173dfbfc28fce8bca08170da53111 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 14 Aug 2023 11:29:42 +0200 Subject: [PATCH 1/5] split GoalEvaluation and CanonicalGoalEvaluation the unnormalized goal is in the callers inference context, while anything inside of the `CanonicalGoalEvaluation` is inside of a new one. --- .../rustc_middle/src/traits/solve/inspect.rs | 28 ++-- .../src/traits/solve/inspect/format.rs | 65 +++++---- .../src/solve/eval_ctxt.rs | 26 ++-- .../src/solve/inspect.rs | 131 +++++++++++------- .../src/solve/search_graph/mod.rs | 1 + 5 files changed, 141 insertions(+), 110 deletions(-) diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 4e2af3816ac65..dc51ded1b80ab 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 8759fecb05a2d..a9f2b76baf919 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(()) + } + } + + pub(super) fn format_canonical_goal_evaluation( + &mut self, + eval: &CanonicalGoalEvaluation<'_>, + ) -> std::fmt::Result { + writeln!(self.f, "GOAL: {:?}", eval.goal)?; - match &goal.kind { + 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 63ae83c8ef4a2..2901baa017e79 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 cda68396321ce..05c180f5049fd 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 52a11abd545d1..60c348d892b43 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; } From 01f3da6b244c8cd9c72f2cf32321ec09d97145e6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 14 Aug 2023 11:48:40 +0200 Subject: [PATCH 2/5] inspect: handle `None` in `nested` --- .../src/solve/inspect.rs | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs index 05c180f5049fd..b78abf0d5442d 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect.rs @@ -182,11 +182,11 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } - fn nested(&self, state: impl Into>) -> Self { + fn nested>>(&self, state: impl FnOnce() -> T) -> Self { match &self.state { Some(prev_state) => Self { state: Some(Box::new(BuilderData { - tree: state.into(), + tree: state().into(), use_global_cache: prev_state.use_global_cache, })), }, @@ -255,11 +255,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { goal: Goal<'tcx, ty::Predicate<'tcx>>, is_normalizes_to_hack: IsNormalizesToHack, ) -> ProofTreeBuilder<'tcx> { - if self.state.is_none() { - return ProofTreeBuilder { state: None }; - } - - self.nested(WipGoalEvaluation { + self.nested(|| WipGoalEvaluation { uncanonicalized_goal: goal, evaluation: None, is_normalizes_to_hack, @@ -271,11 +267,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { &mut self, goal: CanonicalInput<'tcx>, ) -> ProofTreeBuilder<'tcx> { - if self.state.is_none() { - return ProofTreeBuilder { state: None }; - } - - self.nested(WipCanonicalGoalEvaluation { + self.nested(|| WipCanonicalGoalEvaluation { goal, cache_hit: None, evaluation_steps: vec![], @@ -336,11 +328,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { &mut self, instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, ) -> ProofTreeBuilder<'tcx> { - if self.state.is_none() { - return ProofTreeBuilder { state: None }; - } - - self.nested(WipGoalEvaluationStep { + self.nested(|| WipGoalEvaluationStep { instantiated_goal, added_goals_evaluations: vec![], candidates: vec![], @@ -362,11 +350,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> { - if self.state.is_none() { - return ProofTreeBuilder { state: None }; - } - - self.nested(WipGoalCandidate { + self.nested(|| WipGoalCandidate { added_goals_evaluations: vec![], candidates: vec![], kind: None, @@ -398,11 +382,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { - if self.state.is_none() { - return ProofTreeBuilder { state: None }; - } - - self.nested(WipAddedGoalsEvaluation { evaluations: vec![], result: None }) + self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None }) } pub fn evaluate_added_goals_loop_start(&mut self) { From 006d5994356c0188df5ef88b472853636af19326 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 14 Aug 2023 11:49:20 +0200 Subject: [PATCH 3/5] revision -> iteration for added_goals_evaluation --- compiler/rustc_middle/src/traits/solve/inspect/format.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index a9f2b76baf919..487d0fe6bbd26 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -133,10 +133,10 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { ) -> std::fmt::Result { writeln!(self.f, "TRY_EVALUATE_ADDED_GOALS: {:?}", added_goals_evaluation.result)?; - for (n, revision) in added_goals_evaluation.evaluations.iter().enumerate() { - writeln!(self.f, "REVISION {n}")?; + for (n, iterations) in added_goals_evaluation.evaluations.iter().enumerate() { + writeln!(self.f, "ITERATION {n}")?; self.nested(|this| { - for goal_evaluation in revision { + for goal_evaluation in iterations { this.format_goal_evaluation(goal_evaluation)?; } Ok(()) From 8225a2e9ecf6664f4ad9ccb4d2d969478479a206 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 11 Sep 2023 11:34:57 +0200 Subject: [PATCH 4/5] inspect: strongly typed CandidateKind --- compiler/rustc_middle/src/traits/solve.rs | 63 +++++++++++++++++ .../rustc_middle/src/traits/solve/inspect.rs | 33 +++++---- .../src/traits/solve/inspect/format.rs | 11 +-- .../src/solve/alias_relate.rs | 6 +- .../src/solve/assembly/mod.rs | 70 ++----------------- .../src/solve/eval_ctxt.rs | 2 +- .../src/solve/eval_ctxt/probe.rs | 38 +++++++--- .../src/solve/eval_ctxt/select.rs | 4 +- .../src/solve/inspect.rs | 18 ++--- .../src/solve/project_goals.rs | 13 ++-- .../src/solve/trait_goals.rs | 18 ++--- 11 files changed, 155 insertions(+), 121 deletions(-) diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 9d63d29185470..27a1e64a78bd5 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -9,6 +9,9 @@ use crate::ty::{ self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, }; +use rustc_span::def_id::DefId; + +use super::BuiltinImplSource; mod cache; pub mod inspect; @@ -235,3 +238,63 @@ pub enum IsNormalizesToHack { Yes, No, } + +/// Possible ways the given goal can be proven. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CandidateSource { + /// A user written impl. + /// + /// ## Examples + /// + /// ```rust + /// fn main() { + /// let x: Vec = Vec::new(); + /// // This uses the impl from the standard library to prove `Vec: Clone`. + /// let y = x.clone(); + /// } + /// ``` + Impl(DefId), + /// A builtin impl generated by the compiler. When adding a new special + /// trait, try to use actual impls whenever possible. Builtin impls should + /// only be used in cases where the impl cannot be manually be written. + /// + /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`. + /// For a list of all traits with builtin impls, check out the + /// `EvalCtxt::assemble_builtin_impl_candidates` method. + BuiltinImpl(BuiltinImplSource), + /// An assumption from the environment. + /// + /// More precisely we've used the `n-th` assumption in the `param_env`. + /// + /// ## Examples + /// + /// ```rust + /// fn is_clone(x: T) -> (T, T) { + /// // This uses the assumption `T: Clone` from the `where`-bounds + /// // to prove `T: Clone`. + /// (x.clone(), x) + /// } + /// ``` + ParamEnv(usize), + /// If the self type is an alias type, e.g. an opaque type or a projection, + /// we know the bounds on that alias to hold even without knowing its concrete + /// underlying type. + /// + /// More precisely this candidate is using the `n-th` bound in the `item_bounds` of + /// the self type. + /// + /// ## Examples + /// + /// ```rust + /// trait Trait { + /// type Assoc: Clone; + /// } + /// + /// fn foo(x: ::Assoc) { + /// // We prove `::Assoc` by looking at the bounds on `Assoc` in + /// // in the trait definition. + /// let _y = x.clone(); + /// } + /// ``` + AliasBound, +} diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index dc51ded1b80ab..a2dfde2f0d453 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -1,5 +1,6 @@ use super::{ - CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput, QueryResult, + CandidateSource, CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput, + QueryResult, }; use crate::ty; use format::ProofTreeFormatter; @@ -7,13 +8,13 @@ use std::fmt::{Debug, Write}; mod format; -#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +#[derive(Debug, Eq, PartialEq)] pub enum CacheHit { Provisional, Global, } -#[derive(Eq, PartialEq, Hash, HashStable)] +#[derive(Eq, PartialEq)] pub struct GoalEvaluation<'tcx> { pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>, pub is_normalizes_to_hack: IsNormalizesToHack, @@ -21,14 +22,14 @@ pub struct GoalEvaluation<'tcx> { pub returned_goals: Vec>>, } -#[derive(Eq, PartialEq, Hash, HashStable)] +#[derive(Eq, PartialEq)] pub struct CanonicalGoalEvaluation<'tcx> { pub goal: CanonicalInput<'tcx>, pub kind: GoalEvaluationKind<'tcx>, pub result: QueryResult<'tcx>, } -#[derive(Eq, PartialEq, Hash, HashStable)] +#[derive(Eq, PartialEq)] pub enum GoalEvaluationKind<'tcx> { CacheHit(CacheHit), Uncached { revisions: Vec> }, @@ -39,13 +40,13 @@ impl Debug for GoalEvaluation<'_> { } } -#[derive(Eq, PartialEq, Hash, HashStable)] +#[derive(Eq, PartialEq)] pub struct AddedGoalsEvaluation<'tcx> { pub evaluations: Vec>>, pub result: Result, } -#[derive(Eq, PartialEq, Hash, HashStable)] +#[derive(Eq, PartialEq)] pub struct GoalEvaluationStep<'tcx> { pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, @@ -55,24 +56,28 @@ pub struct GoalEvaluationStep<'tcx> { pub result: QueryResult<'tcx>, } -#[derive(Eq, PartialEq, Hash, HashStable)] +#[derive(Eq, PartialEq)] pub struct GoalCandidate<'tcx> { pub added_goals_evaluations: Vec>, pub candidates: Vec>, - pub kind: CandidateKind<'tcx>, + pub kind: ProbeKind<'tcx>, } -#[derive(Eq, PartialEq, Debug, Hash, HashStable)] -pub enum CandidateKind<'tcx> { +#[derive(Debug, PartialEq, Eq)] +pub enum ProbeKind<'tcx> { /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, - /// A normal candidate for proving a goal - Candidate { name: String, result: QueryResult<'tcx> }, + /// Some candidate to prove the current goal. + /// + /// FIXME: Remove this in favor of always using more strongly typed variants. + MiscCandidate { name: &'static str, result: QueryResult<'tcx> }, + /// A candidate for proving a trait or alias-relate goal. + TraitCandidate { source: CandidateSource, result: QueryResult<'tcx> }, /// Used in the probe that wraps normalizing the non-self type for the unsize /// trait, which is also structurally matched on. UnsizeAssembly, /// During upcasting from some source object to target object type, used to /// do a probe to find out what projection type(s) may be used to prove that /// the source type upholds all of the target type's object bounds. - UpcastProbe, + UpcastProjectionCompatibility, } diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 487d0fe6bbd26..7c077beeed640 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -102,18 +102,21 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result { match &candidate.kind { - CandidateKind::NormalizedSelfTyAssembly => { + ProbeKind::NormalizedSelfTyAssembly => { writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } - CandidateKind::UnsizeAssembly => { + ProbeKind::UnsizeAssembly => { writeln!(self.f, "ASSEMBLING CANDIDATES FOR UNSIZING:") } - CandidateKind::UpcastProbe => { + ProbeKind::UpcastProjectionCompatibility => { writeln!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:") } - CandidateKind::Candidate { name, result } => { + ProbeKind::MiscCandidate { name, result } => { writeln!(self.f, "CANDIDATE {name}: {result:?}") } + ProbeKind::TraitCandidate { source, result } => { + writeln!(self.f, "CANDIDATE {source:?}: {result:?}") + } }?; self.nested(|this| { diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 6b839d64b87ca..f7031c5f4933e 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -125,7 +125,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { direction: ty::AliasRelationDirection, invert: Invert, ) -> QueryResult<'tcx> { - self.probe_candidate("normalizes-to").enter(|ecx| { + self.probe_misc_candidate("normalizes-to").enter(|ecx| { ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) @@ -175,7 +175,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { alias_rhs: ty::AliasTy<'tcx>, direction: ty::AliasRelationDirection, ) -> QueryResult<'tcx> { - self.probe_candidate("args relate").enter(|ecx| { + self.probe_misc_candidate("args relate").enter(|ecx| { match direction { ty::AliasRelationDirection::Equate => { ecx.eq(param_env, alias_lhs, alias_rhs)?; @@ -196,7 +196,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { rhs: ty::Term<'tcx>, direction: ty::AliasRelationDirection, ) -> QueryResult<'tcx> { - self.probe_candidate("bidir normalizes-to").enter(|ecx| { + self.probe_misc_candidate("bidir normalizes-to").enter(|ecx| { ecx.normalizes_to_inner( param_env, lhs.to_alias_ty(ecx.tcx()).unwrap(), diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 36194f973b572..128b9ad7e472b 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -5,8 +5,10 @@ use crate::traits::coherence; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::Reveal; -use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::solve::inspect::ProbeKind; +use rustc_middle::traits::solve::{ + CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, +}; use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -27,66 +29,6 @@ pub(super) struct Candidate<'tcx> { pub(super) result: CanonicalResponse<'tcx>, } -/// Possible ways the given goal can be proven. -#[derive(Debug, Clone, Copy)] -pub(super) enum CandidateSource { - /// A user written impl. - /// - /// ## Examples - /// - /// ```rust - /// fn main() { - /// let x: Vec = Vec::new(); - /// // This uses the impl from the standard library to prove `Vec: Clone`. - /// let y = x.clone(); - /// } - /// ``` - Impl(DefId), - /// A builtin impl generated by the compiler. When adding a new special - /// trait, try to use actual impls whenever possible. Builtin impls should - /// only be used in cases where the impl cannot be manually be written. - /// - /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`. - /// For a list of all traits with builtin impls, check out the - /// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not - BuiltinImpl(BuiltinImplSource), - /// An assumption from the environment. - /// - /// More precisely we've used the `n-th` assumption in the `param_env`. - /// - /// ## Examples - /// - /// ```rust - /// fn is_clone(x: T) -> (T, T) { - /// // This uses the assumption `T: Clone` from the `where`-bounds - /// // to prove `T: Clone`. - /// (x.clone(), x) - /// } - /// ``` - ParamEnv(usize), - /// If the self type is an alias type, e.g. an opaque type or a projection, - /// we know the bounds on that alias to hold even without knowing its concrete - /// underlying type. - /// - /// More precisely this candidate is using the `n-th` bound in the `item_bounds` of - /// the self type. - /// - /// ## Examples - /// - /// ```rust - /// trait Trait { - /// type Assoc: Clone; - /// } - /// - /// fn foo(x: ::Assoc) { - /// // We prove `::Assoc` by looking at the bounds on `Assoc` in - /// // in the trait definition. - /// let _y = x.clone(); - /// } - /// ``` - AliasBound, -} - /// Methods used to assemble candidates for either trait or projection goals. pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq + std::fmt::Display @@ -399,7 +341,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let tcx = self.tcx(); let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return }; - candidates.extend(self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| { + candidates.extend(self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { if num_steps < ecx.local_overflow_limit() { let normalized_ty = ecx.next_ty_infer(); let normalizes_to_goal = goal.with( @@ -910,7 +852,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { SolverMode::Coherence => {} }; - let result = self.probe_candidate("coherence unknowable").enter(|ecx| { + let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| { let trait_ref = goal.predicate.trait_ref(tcx); #[derive(Debug)] diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 2901baa017e79..307c0516f703a 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -920,7 +920,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if candidate_key.def_id != key.def_id { continue; } - values.extend(self.probe_candidate("opaque type storage").enter(|ecx| { + values.extend(self.probe_misc_candidate("opaque type storage").enter(|ecx| { for (a, b) in std::iter::zip(candidate_key.args, key.args) { ecx.eq(param_env, a, b)?; } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs index 317c43baf8f51..f88cfbac3f3eb 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -1,5 +1,5 @@ use super::EvalCtxt; -use rustc_middle::traits::solve::{inspect, QueryResult}; +use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult}; use std::marker::PhantomData; pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> { @@ -10,7 +10,7 @@ pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> { impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T> where - F: FnOnce(&T) -> inspect::CandidateKind<'tcx>, + F: FnOnce(&T) -> inspect::ProbeKind<'tcx>, { pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T { let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self; @@ -28,8 +28,8 @@ where }; let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx)); if !outer_ecx.inspect.is_noop() { - let cand_kind = probe_kind(&r); - nested_ecx.inspect.candidate_kind(cand_kind); + let probe_kind = probe_kind(&r); + nested_ecx.inspect.probe_kind(probe_kind); outer_ecx.inspect.goal_candidate(nested_ecx.inspect); } r @@ -41,25 +41,45 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// as expensive as necessary to output the desired information. pub(in crate::solve) fn probe(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, 'tcx, F, T> where - F: FnOnce(&T) -> inspect::CandidateKind<'tcx>, + F: FnOnce(&T) -> inspect::ProbeKind<'tcx>, { ProbeCtxt { ecx: self, probe_kind, _result: PhantomData } } - pub(in crate::solve) fn probe_candidate( + pub(in crate::solve) fn probe_misc_candidate( &mut self, name: &'static str, ) -> ProbeCtxt< '_, 'a, 'tcx, - impl FnOnce(&QueryResult<'tcx>) -> inspect::CandidateKind<'tcx>, + impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, QueryResult<'tcx>, > { ProbeCtxt { ecx: self, - probe_kind: move |result: &QueryResult<'tcx>| inspect::CandidateKind::Candidate { - name: name.to_string(), + probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::MiscCandidate { + name, + result: *result, + }, + _result: PhantomData, + } + } + + pub(in crate::solve) fn probe_trait_candidate( + &mut self, + source: CandidateSource, + ) -> ProbeCtxt< + '_, + 'a, + 'tcx, + impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, + QueryResult<'tcx>, + > { + ProbeCtxt { + ecx: self, + probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { + source, result: *result, }, _result: PhantomData, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 42d7a587cac4a..315df06be417c 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -4,14 +4,14 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt}; use rustc_infer::traits::{ Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine, }; -use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal}; +use rustc_middle::traits::solve::{CandidateSource, CanonicalInput, Certainty, Goal}; use rustc_middle::traits::{ BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError, }; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -use crate::solve::assembly::{Candidate, CandidateSource}; +use crate::solve::assembly::Candidate; use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; use crate::solve::inspect::ProofTreeBuilder; use crate::traits::StructurallyNormalizeExt; diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs index b78abf0d5442d..f38edd8b9a032 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect.rs @@ -1,5 +1,5 @@ use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::inspect::{self, CacheHit, CandidateKind}; +use rustc_middle::traits::solve::inspect::{self, CacheHit, ProbeKind}; use rustc_middle::traits::solve::{ CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult, }; @@ -9,7 +9,7 @@ use rustc_session::config::DumpSolverProofTree; use super::eval_ctxt::UseGlobalCache; use super::GenerateProofTree; -#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +#[derive(Eq, PartialEq, Debug)] pub struct WipGoalEvaluation<'tcx> { pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>, pub evaluation: Option>, @@ -28,7 +28,7 @@ impl<'tcx> WipGoalEvaluation<'tcx> { } } -#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +#[derive(Eq, PartialEq, Debug)] pub struct WipCanonicalGoalEvaluation<'tcx> { pub goal: CanonicalInput<'tcx>, pub cache_hit: Option, @@ -56,7 +56,7 @@ impl<'tcx> WipCanonicalGoalEvaluation<'tcx> { } } -#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +#[derive(Eq, PartialEq, Debug)] pub struct WipAddedGoalsEvaluation<'tcx> { pub evaluations: Vec>>, pub result: Option>, @@ -77,7 +77,7 @@ impl<'tcx> WipAddedGoalsEvaluation<'tcx> { } } -#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +#[derive(Eq, PartialEq, Debug)] pub struct WipGoalEvaluationStep<'tcx> { pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, @@ -102,11 +102,11 @@ impl<'tcx> WipGoalEvaluationStep<'tcx> { } } -#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +#[derive(Eq, PartialEq, Debug)] pub struct WipGoalCandidate<'tcx> { pub added_goals_evaluations: Vec>, pub candidates: Vec>, - pub kind: Option>, + pub kind: Option>, } impl<'tcx> WipGoalCandidate<'tcx> { @@ -357,11 +357,11 @@ impl<'tcx> ProofTreeBuilder<'tcx> { }) } - pub fn candidate_kind(&mut self, candidate_kind: CandidateKind<'tcx>) { + pub fn probe_kind(&mut self, probe_kind: ProbeKind<'tcx>) { if let Some(this) = self.as_mut() { match this { DebugSolver::GoalCandidate(this) => { - assert_eq!(this.kind.replace(candidate_kind), None) + assert_eq!(this.kind.replace(probe_kind), None) } _ => unreachable!(), } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index df094af4f1f1a..3fe914e50be11 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -8,8 +8,9 @@ use rustc_hir::LangItem; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; -use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::solve::{ + CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, +}; use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::ProjectionPredicate; @@ -113,7 +114,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { if let Some(projection_pred) = assumption.as_projection_clause() { if projection_pred.projection_def_id() == goal.predicate.def_id() { let tcx = ecx.tcx(); - ecx.probe_candidate("assumption").enter(|ecx| { + ecx.probe_misc_candidate("assumption").enter(|ecx| { let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); ecx.eq( @@ -155,7 +156,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { return Err(NoSolution); } - ecx.probe(|r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter(|ecx| { + ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { let impl_args = ecx.fresh_args_for_item(impl_def_id); let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args); @@ -369,7 +370,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); - ecx.probe_candidate("builtin pointee").enter(|ecx| { + ecx.probe_misc_candidate("builtin pointee").enter(|ecx| { let metadata_ty = match goal.predicate.self_ty().kind() { ty::Bool | ty::Char @@ -578,7 +579,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ), }; - ecx.probe_candidate("builtin discriminant kind").enter(|ecx| { + ecx.probe_misc_candidate("builtin discriminant kind").enter(|ecx| { ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into()) .expect("expected goal term to be fully unconstrained"); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 8685f3100a822..f18eed94a6844 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -5,7 +5,7 @@ use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; -use rustc_middle::traits::solve::inspect::CandidateKind; +use rustc_middle::traits::solve::inspect::ProbeKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::traits::{BuiltinImplSource, Reveal}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; @@ -61,7 +61,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }, }; - ecx.probe_candidate("impl").enter(|ecx| { + ecx.probe_misc_candidate("impl").enter(|ecx| { let impl_args = ecx.fresh_args_for_item(impl_def_id); let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args); @@ -96,7 +96,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { && trait_clause.polarity() == goal.predicate.polarity { // FIXME: Constness - ecx.probe_candidate("assumption").enter(|ecx| { + ecx.probe_misc_candidate("assumption").enter(|ecx| { let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); ecx.eq( goal.param_env, @@ -167,7 +167,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let tcx = ecx.tcx(); - ecx.probe_candidate("trait alias").enter(|ecx| { + ecx.probe_misc_candidate("trait alias").enter(|ecx| { let nested_obligations = tcx .predicates_of(goal.predicate.def_id()) .instantiate(tcx, goal.predicate.trait_ref.args); @@ -427,7 +427,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| { + ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| { let a_ty = goal.predicate.self_ty(); // We need to normalize the b_ty since it's destructured as a `dyn Trait`. let Some(b_ty) = @@ -491,7 +491,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { Err(NoSolution) => vec![], }; - ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| { + ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| { let a_ty = goal.predicate.self_ty(); // We need to normalize the b_ty since it's matched structurally // in the other functions below. @@ -597,7 +597,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.walk_vtable( a_principal.with_self_ty(tcx, a_ty), |ecx, new_a_principal, _, vtable_vptr_slot| { - if let Ok(resp) = ecx.probe_candidate("dyn upcast").enter(|ecx| { + if let Ok(resp) = ecx.probe_misc_candidate("dyn upcast").enter(|ecx| { ecx.consider_builtin_upcast_to_principal( goal, a_data, @@ -640,7 +640,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { target_projection: ty::PolyExistentialProjection<'tcx>| { source_projection.item_def_id() == target_projection.item_def_id() && ecx - .probe(|_| CandidateKind::UpcastProbe) + .probe(|_| ProbeKind::UpcastProjectionCompatibility) .enter(|ecx| -> Result<(), NoSolution> { ecx.eq(param_env, source_projection, target_projection)?; let _ = ecx.try_evaluate_added_goals()?; @@ -918,7 +918,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, TraitPredicate<'tcx>>, constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result>, NoSolution>, ) -> QueryResult<'tcx> { - self.probe_candidate("constituent tys").enter(|ecx| { + self.probe_misc_candidate("constituent tys").enter(|ecx| { ecx.add_goals( constituent_tys(ecx, goal.predicate.self_ty())? .into_iter() From eac55eec9eb7803a91b8fb271919a86a79b00fc6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 11 Sep 2023 15:48:04 +0200 Subject: [PATCH 5/5] dedup `GoalEvaluationStep` and `GoalCandidate` also handle 2 panics when dumping proof trees for the whole test suite - need to actually tell the proof tree builder about overflow - need to handle a recursion_limit of 0 :< --- .../rustc_middle/src/traits/solve/inspect.rs | 15 +++- .../src/traits/solve/inspect/format.rs | 18 ++--- .../src/solve/inspect.rs | 81 ++++++++++--------- .../rustc_trait_selection/src/solve/mod.rs | 6 +- .../src/solve/search_graph/mod.rs | 11 ++- 5 files changed, 75 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index a2dfde2f0d453..d8b3a061b77d5 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -31,6 +31,7 @@ pub struct CanonicalGoalEvaluation<'tcx> { #[derive(Eq, PartialEq)] pub enum GoalEvaluationKind<'tcx> { + Overflow, CacheHit(CacheHit), Uncached { revisions: Vec> }, } @@ -50,10 +51,8 @@ pub struct AddedGoalsEvaluation<'tcx> { pub struct GoalEvaluationStep<'tcx> { pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, - pub added_goals_evaluations: Vec>, - pub candidates: Vec>, - - pub result: QueryResult<'tcx>, + /// The actual evaluation of the goal, always `ProbeKind::Root`. + pub evaluation: GoalCandidate<'tcx>, } #[derive(Eq, PartialEq)] @@ -63,8 +62,16 @@ pub struct GoalCandidate<'tcx> { pub kind: ProbeKind<'tcx>, } +impl Debug for GoalCandidate<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + ProofTreeFormatter::new(f).format_candidate(self) + } +} + #[derive(Debug, PartialEq, Eq)] pub enum ProbeKind<'tcx> { + /// The root inference context while proving a goal. + Root { result: QueryResult<'tcx> }, /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, /// Some candidate to prove the current goal. diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 7c077beeed640..d916e80a625d0 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -68,6 +68,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { writeln!(self.f, "GOAL: {:?}", eval.goal)?; match &eval.kind { + GoalEvaluationKind::Overflow => { + writeln!(self.f, "OVERFLOW: {:?}", eval.result) + } GoalEvaluationKind::CacheHit(CacheHit::Global) => { writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result) } @@ -76,7 +79,7 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { } GoalEvaluationKind::Uncached { revisions } => { for (n, step) in revisions.iter().enumerate() { - writeln!(self.f, "REVISION {n}: {:?}", step.result)?; + writeln!(self.f, "REVISION {n}")?; self.nested(|this| this.format_evaluation_step(step))?; } writeln!(self.f, "RESULT: {:?}", eval.result) @@ -89,19 +92,14 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { evaluation_step: &GoalEvaluationStep<'_>, ) -> std::fmt::Result { writeln!(self.f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?; - - for candidate in &evaluation_step.candidates { - self.nested(|this| this.format_candidate(candidate))?; - } - for nested in &evaluation_step.added_goals_evaluations { - self.nested(|this| this.format_added_goals_evaluation(nested))?; - } - - Ok(()) + self.format_candidate(&evaluation_step.evaluation) } pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result { match &candidate.kind { + ProbeKind::Root { result } => { + writeln!(self.f, "ROOT RESULT: {result:?}") + } ProbeKind::NormalizedSelfTyAssembly => { writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs index f38edd8b9a032..46025da768326 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect.rs @@ -28,28 +28,34 @@ impl<'tcx> WipGoalEvaluation<'tcx> { } } +#[derive(Eq, PartialEq, Debug)] +pub enum WipGoalEvaluationKind { + Overflow, + CacheHit(CacheHit), +} + #[derive(Eq, PartialEq, Debug)] pub struct WipCanonicalGoalEvaluation<'tcx> { pub goal: CanonicalInput<'tcx>, - pub cache_hit: Option, - pub evaluation_steps: Vec>, + pub kind: Option, + pub revisions: 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(), - } + let kind = match self.kind { + Some(WipGoalEvaluationKind::Overflow) => inspect::GoalEvaluationKind::Overflow, + Some(WipGoalEvaluationKind::CacheHit(hit)) => { + inspect::GoalEvaluationKind::CacheHit(hit) } + None => inspect::GoalEvaluationKind::Uncached { + revisions: self + .revisions + .into_iter() + .map(WipGoalEvaluationStep::finalize) + .collect(), + }, }; inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() } @@ -81,24 +87,17 @@ impl<'tcx> WipAddedGoalsEvaluation<'tcx> { pub struct WipGoalEvaluationStep<'tcx> { pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, - pub added_goals_evaluations: Vec>, - pub candidates: Vec>, - - pub result: Option>, + pub evaluation: WipGoalCandidate<'tcx>, } impl<'tcx> WipGoalEvaluationStep<'tcx> { pub fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> { - inspect::GoalEvaluationStep { - instantiated_goal: self.instantiated_goal, - added_goals_evaluations: self - .added_goals_evaluations - .into_iter() - .map(WipAddedGoalsEvaluation::finalize) - .collect(), - candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(), - result: self.result.unwrap(), + let evaluation = self.evaluation.finalize(); + match evaluation.kind { + ProbeKind::Root { .. } => (), + _ => unreachable!("unexpected root evaluation: {evaluation:?}"), } + inspect::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, evaluation } } } @@ -269,8 +268,8 @@ impl<'tcx> ProofTreeBuilder<'tcx> { ) -> ProofTreeBuilder<'tcx> { self.nested(|| WipCanonicalGoalEvaluation { goal, - cache_hit: None, - evaluation_steps: vec![], + kind: None, + revisions: vec![], result: None, }) } @@ -287,11 +286,11 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } - pub fn cache_hit(&mut self, cache_hit: CacheHit) { + pub fn goal_evaluation_kind(&mut self, kind: WipGoalEvaluationKind) { if let Some(this) = self.as_mut() { match this { DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => { - assert_eq!(canonical_goal_evaluation.cache_hit.replace(cache_hit), None); + assert_eq!(canonical_goal_evaluation.kind.replace(kind), None); } _ => unreachable!(), }; @@ -330,9 +329,11 @@ impl<'tcx> ProofTreeBuilder<'tcx> { ) -> ProofTreeBuilder<'tcx> { self.nested(|| WipGoalEvaluationStep { instantiated_goal, - added_goals_evaluations: vec![], - candidates: vec![], - result: None, + evaluation: WipGoalCandidate { + added_goals_evaluations: vec![], + candidates: vec![], + kind: None, + }, }) } pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) { @@ -342,7 +343,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations), DebugSolver::GoalEvaluationStep(goal_evaluation_step), ) => { - canonical_goal_evaluations.evaluation_steps.push(goal_evaluation_step); + canonical_goal_evaluations.revisions.push(goal_evaluation_step); } _ => unreachable!(), } @@ -373,7 +374,10 @@ impl<'tcx> ProofTreeBuilder<'tcx> { match (this, candidate.state.unwrap().tree) { ( DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. }) - | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }), + | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + evaluation: WipGoalCandidate { candidates, .. }, + .. + }), DebugSolver::GoalCandidate(candidate), ) => candidates.push(candidate), _ => unreachable!(), @@ -412,7 +416,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { match (this, added_goals_evaluation.state.unwrap().tree) { ( DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { - added_goals_evaluations, + evaluation: WipGoalCandidate { added_goals_evaluations, .. }, .. }) | DebugSolver::GoalCandidate(WipGoalCandidate { @@ -432,7 +436,10 @@ impl<'tcx> ProofTreeBuilder<'tcx> { assert_eq!(canonical_goal_evaluation.result.replace(result), None); } DebugSolver::GoalEvaluationStep(evaluation_step) => { - assert_eq!(evaluation_step.result.replace(result), None); + assert_eq!( + evaluation_step.evaluation.kind.replace(ProbeKind::Root { result }), + None + ); } _ => unreachable!(), } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 75a99f799a24c..c492408bc7630 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -233,9 +233,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self, goals))] fn add_goals(&mut self, goals: impl IntoIterator>>) { - let current_len = self.nested_goals.goals.len(); - self.nested_goals.goals.extend(goals); - debug!("added_goals={:?}", &self.nested_goals.goals[current_len..]); + for goal in goals { + self.add_goal(goal); + } } /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`. 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 60c348d892b43..c816b51f67acd 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -1,6 +1,7 @@ mod cache; use self::cache::ProvisionalEntry; +use super::inspect; use super::inspect::ProofTreeBuilder; use super::SolverMode; use cache::ProvisionalCache; @@ -185,6 +186,8 @@ impl<'tcx> SearchGraph<'tcx> { if let Some(last) = self.stack.raw.last_mut() { last.encountered_overflow = true; } + + inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::Overflow); return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); }; @@ -200,7 +203,9 @@ impl<'tcx> SearchGraph<'tcx> { available_depth, ) { - inspect.cache_hit(CacheHit::Global); + inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::CacheHit( + CacheHit::Global, + )); self.on_cache_hit(reached_depth, encountered_overflow); return result; } @@ -235,7 +240,9 @@ impl<'tcx> SearchGraph<'tcx> { // Finally we can return either the provisional response for that goal if we have a // coinductive cycle or an ambiguous result if the cycle is inductive. Entry::Occupied(entry_index) => { - inspect.cache_hit(CacheHit::Provisional); + inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::CacheHit( + CacheHit::Provisional, + )); let entry_index = *entry_index.get(); let stack_depth = cache.depth(entry_index);