diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 739590385820e..f03802049707c 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -991,9 +991,3 @@ pub enum DefiningAnchor { /// Used to catch type mismatch errors when handling opaque types. Error, } - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] -pub enum IsNormalizesToHack { - Yes, - No, -} diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index af482a8896070..73b332fd8ec95 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -228,3 +228,9 @@ impl<'tcx> TypeVisitable> for PredefinedOpaques<'tcx> { self.opaque_types.visit_with(visitor) } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] +pub enum IsNormalizesToHack { + Yes, + No, +} diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 3cdf1ebbd05d6..527afa005b9f7 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -1,5 +1,7 @@ -use super::{CanonicalInput, Certainty, Goal, NoSolution, QueryInput, QueryResult}; -use crate::{traits::IsNormalizesToHack, ty}; +use super::{ + CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput, QueryResult, +}; +use crate::ty; use format::ProofTreeFormatter; use std::fmt::{Debug, Write}; @@ -22,6 +24,7 @@ pub struct GoalEvaluation<'tcx> { pub result: QueryResult<'tcx>, } + #[derive(Eq, PartialEq, Hash, HashStable)] pub enum GoalEvaluationKind<'tcx> { CacheHit(CacheHit), @@ -65,6 +68,7 @@ pub struct GoalCandidate<'tcx> { pub candidates: Vec>, pub kind: CandidateKind<'tcx>, } + #[derive(Eq, PartialEq, Debug, Hash, HashStable)] pub enum CandidateKind<'tcx> { /// Probe entered when normalizing the self ty during candidate assembly diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 1ceb77e91938c..ca7fcfd8ca1be 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -110,12 +110,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { direction: ty::AliasRelationDirection, invert: Invert, ) -> QueryResult<'tcx> { - self.probe( + self.probe(|r| CandidateKind::Candidate { name: "normalizes-to".into(), result: *r }).enter( |ecx| { ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }, - |r| CandidateKind::Candidate { name: "normalizes-to".into(), result: *r }, ) } @@ -157,7 +156,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { alias_rhs: ty::AliasTy<'tcx>, direction: ty::AliasRelationDirection, ) -> QueryResult<'tcx> { - self.probe( + self.probe(|r| CandidateKind::Candidate { name: "substs relate".into(), result: *r }).enter( |ecx| { match direction { ty::AliasRelationDirection::Equate => { @@ -170,7 +169,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }, - |r| CandidateKind::Candidate { name: "substs relate".into(), result: *r }, ) } @@ -181,8 +179,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { rhs: ty::Term<'tcx>, direction: ty::AliasRelationDirection, ) -> QueryResult<'tcx> { - self.probe( - |ecx| { + self.probe(|r| CandidateKind::Candidate { name: "bidir normalizes-to".into(), result: *r }) + .enter(|ecx| { ecx.normalizes_to_inner( param_env, lhs.to_alias_ty(ecx.tcx()).unwrap(), @@ -198,8 +196,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Invert::Yes, )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - |r| CandidateKind::Candidate { name: "bidir normalizes-to".into(), result: *r }, - ) + }) } } diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 716c9ff44a32b..10dd2a2f9d78e 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -337,8 +337,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return }; - let normalized_self_candidates: Result<_, NoSolution> = self.probe( - |ecx| { + let normalized_self_candidates: Result<_, NoSolution> = + self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| { ecx.with_incremented_depth( |ecx| { let result = ecx.evaluate_added_goals_and_make_canonical_response( @@ -368,9 +368,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(ecx.assemble_and_evaluate_candidates(goal)) }, ) - }, - |_| CandidateKind::NormalizedSelfTyAssembly, - ); + }); if let Ok(normalized_self_candidates) = normalized_self_candidates { candidates.extend(normalized_self_candidates); diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 106849190e039..e558b678d156e 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -11,10 +11,10 @@ use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::traits::solve::inspect::{self, CandidateKind}; use rustc_middle::traits::solve::{ - CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques, - PredefinedOpaquesData, QueryResult, + CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, MaybeCause, + PredefinedOpaques, PredefinedOpaquesData, QueryResult, }; -use rustc_middle::traits::{DefiningAnchor, IsNormalizesToHack}; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{ self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -30,6 +30,7 @@ use super::SolverMode; use super::{search_graph::SearchGraph, Goal}; mod canonical; +mod probe; pub struct EvalCtxt<'a, 'tcx> { /// The inference context that backs (mostly) inference and placeholder terms @@ -529,32 +530,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } impl<'tcx> EvalCtxt<'_, 'tcx> { - /// `probe_kind` is only called when proof tree building is enabled so it can be - /// as expensive as necessary to output the desired information. - pub(super) fn probe( - &mut self, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T, - probe_kind: impl FnOnce(&T) -> CandidateKind<'tcx>, - ) -> T { - let mut ecx = EvalCtxt { - infcx: self.infcx, - var_values: self.var_values, - predefined_opaques_in_body: self.predefined_opaques_in_body, - max_input_universe: self.max_input_universe, - search_graph: self.search_graph, - nested_goals: self.nested_goals.clone(), - tainted: self.tainted, - inspect: self.inspect.new_goal_candidate(), - }; - let r = self.infcx.probe(|_| f(&mut ecx)); - if !self.inspect.is_noop() { - let cand_kind = probe_kind(&r); - ecx.inspect.candidate_kind(cand_kind); - self.inspect.goal_candidate(ecx.inspect); - } - r - } - pub(super) fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -868,8 +843,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if candidate_key.def_id != key.def_id { continue; } - values.extend(self.probe( - |ecx| { + values.extend( + self.probe(|r| CandidateKind::Candidate { + name: "opaque type storage".into(), + result: *r, + }) + .enter(|ecx| { for (a, b) in std::iter::zip(candidate_key.substs, key.substs) { ecx.eq(param_env, a, b)?; } @@ -881,9 +860,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidate_ty, ); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - |r| CandidateKind::Candidate { name: "opaque type storage".into(), result: *r }, - )); + }), + ); } values } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs new file mode 100644 index 0000000000000..5d912fc039d59 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -0,0 +1,47 @@ +use super::EvalCtxt; +use rustc_middle::traits::solve::inspect; +use std::marker::PhantomData; + +pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> { + ecx: &'me mut EvalCtxt<'a, 'tcx>, + probe_kind: F, + _result: PhantomData, +} + +impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T> +where + F: FnOnce(&T) -> inspect::CandidateKind<'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; + + let mut nested_ecx = EvalCtxt { + infcx: outer_ecx.infcx, + var_values: outer_ecx.var_values, + predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body, + max_input_universe: outer_ecx.max_input_universe, + search_graph: outer_ecx.search_graph, + nested_goals: outer_ecx.nested_goals.clone(), + tainted: outer_ecx.tainted, + inspect: outer_ecx.inspect.new_goal_candidate(), + }; + 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); + outer_ecx.inspect.goal_candidate(nested_ecx.inspect); + } + r + } +} + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + /// `probe_kind` is only called when proof tree building is enabled so it can be + /// 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>, + { + ProbeCtxt { ecx: self, probe_kind, _result: PhantomData } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs index c3dea5f790a43..6d7804a8fad2d 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect.rs @@ -1,14 +1,9 @@ -use rustc_middle::{ - traits::{ - query::NoSolution, - solve::{ - inspect::{self, CacheHit, CandidateKind}, - CanonicalInput, Certainty, Goal, QueryInput, QueryResult, - }, - IsNormalizesToHack, - }, - ty, +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::inspect::{self, CacheHit, CandidateKind}; +use rustc_middle::traits::solve::{ + CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult, }; +use rustc_middle::ty; pub mod dump; @@ -25,6 +20,7 @@ pub struct WipGoalEvaluation<'tcx> { pub result: Option>, } + impl<'tcx> WipGoalEvaluation<'tcx> { pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> { inspect::GoalEvaluation { @@ -52,6 +48,7 @@ pub struct WipAddedGoalsEvaluation<'tcx> { pub evaluations: Vec>>, pub result: Option>, } + impl<'tcx> WipAddedGoalsEvaluation<'tcx> { pub fn finalize(self) -> inspect::AddedGoalsEvaluation<'tcx> { inspect::AddedGoalsEvaluation { @@ -76,6 +73,7 @@ pub struct WipGoalEvaluationStep<'tcx> { pub result: Option>, } + impl<'tcx> WipGoalEvaluationStep<'tcx> { pub fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> { inspect::GoalEvaluationStep { @@ -97,6 +95,7 @@ pub struct WipGoalCandidate<'tcx> { pub candidates: Vec>, pub kind: Option>, } + impl<'tcx> WipGoalCandidate<'tcx> { pub fn finalize(self) -> inspect::GoalCandidate<'tcx> { inspect::GoalCandidate { @@ -120,10 +119,45 @@ pub enum DebugSolver<'tcx> { GoalCandidate(WipGoalCandidate<'tcx>), } -pub struct ProofTreeBuilder<'tcx>(Option>>); +impl<'tcx> From> for DebugSolver<'tcx> { + fn from(g: WipGoalEvaluation<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::GoalEvaluation(g) + } +} + +impl<'tcx> From> for DebugSolver<'tcx> { + fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::AddedGoalsEvaluation(g) + } +} + +impl<'tcx> From> for DebugSolver<'tcx> { + fn from(g: WipGoalEvaluationStep<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::GoalEvaluationStep(g) + } +} + +impl<'tcx> From> for DebugSolver<'tcx> { + fn from(g: WipGoalCandidate<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::GoalCandidate(g) + } +} + +pub struct ProofTreeBuilder<'tcx> { + state: Option>>, +} + impl<'tcx> ProofTreeBuilder<'tcx> { + fn new(state: impl Into>) -> ProofTreeBuilder<'tcx> { + ProofTreeBuilder { state: Some(Box::new(state.into())) } + } + + fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> { + self.state.as_mut().map(|boxed| &mut **boxed) + } + pub fn finalize(self) -> Option> { - match *(self.0?) { + match *(self.state?) { DebugSolver::GoalEvaluation(wip_goal_evaluation) => { Some(wip_goal_evaluation.finalize()) } @@ -132,15 +166,15 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } pub fn new_root() -> ProofTreeBuilder<'tcx> { - Self(Some(Box::new(DebugSolver::Root))) + ProofTreeBuilder::new(DebugSolver::Root) } pub fn new_noop() -> ProofTreeBuilder<'tcx> { - Self(None) + ProofTreeBuilder { state: None } } pub fn is_noop(&self) -> bool { - self.0.is_none() + self.state.is_none() } pub fn new_goal_evaluation( @@ -148,11 +182,11 @@ impl<'tcx> ProofTreeBuilder<'tcx> { goal: Goal<'tcx, ty::Predicate<'tcx>>, is_normalizes_to_hack: IsNormalizesToHack, ) -> ProofTreeBuilder<'tcx> { - if self.0.is_none() { - return ProofTreeBuilder(None); + if self.state.is_none() { + return ProofTreeBuilder { state: None }; } - Self(Some(Box::new(DebugSolver::GoalEvaluation(WipGoalEvaluation { + ProofTreeBuilder::new(WipGoalEvaluation { uncanonicalized_goal: goal, canonicalized_goal: None, evaluation_steps: vec![], @@ -160,62 +194,54 @@ impl<'tcx> ProofTreeBuilder<'tcx> { cache_hit: None, returned_goals: vec![], result: None, - })))) + }) } + 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) + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalEvaluation(goal_evaluation) => { + assert_eq!(goal_evaluation.canonicalized_goal.replace(canonical_goal), None); + } + _ => unreachable!(), } - _ => unreachable!(), } } + 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!(), - }; + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalEvaluation(goal_evaluation) => { + assert_eq!(goal_evaluation.cache_hit.replace(cache_hit), None); + } + _ => unreachable!(), + }; + } } + pub fn returned_goals(&mut self, goals: &[Goal<'tcx, ty::Predicate<'tcx>>]) { - let this = match self.0.as_mut() { - None => return, - Some(this) => &mut **this, - }; - - match this { - DebugSolver::GoalEvaluation(evaluation) => { - assert!(evaluation.returned_goals.is_empty()); - evaluation.returned_goals.extend(goals); + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalEvaluation(evaluation) => { + assert!(evaluation.returned_goals.is_empty()); + evaluation.returned_goals.extend(goals); + } + _ => unreachable!(), } - _ => unreachable!(), } } 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(WipAddedGoalsEvaluation { evaluations, .. }), - DebugSolver::GoalEvaluation(goal_evaluation), - ) => evaluations.last_mut().unwrap().push(goal_evaluation), - (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation, - _ => unreachable!(), + if let Some(this) = self.as_mut() { + match (this, *goal_evaluation.state.unwrap()) { + ( + DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation { + evaluations, .. + }), + DebugSolver::GoalEvaluation(goal_evaluation), + ) => evaluations.last_mut().unwrap().push(goal_evaluation), + (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation, + _ => unreachable!(), + } } } @@ -223,144 +249,124 @@ impl<'tcx> ProofTreeBuilder<'tcx> { &mut self, instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, ) -> ProofTreeBuilder<'tcx> { - if self.0.is_none() { - return Self(None); + if self.state.is_none() { + return ProofTreeBuilder { state: None }; } - Self(Some(Box::new(DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + ProofTreeBuilder::new(WipGoalEvaluationStep { instantiated_goal, nested_goal_evaluations: vec![], candidates: vec![], result: None, - })))) + }) } 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); + if let Some(this) = self.as_mut() { + match (this, *goal_eval_step.state.unwrap()) { + (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => { + goal_eval.evaluation_steps.push(step); + } + _ => unreachable!(), } - _ => unreachable!(), } } pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> { - if self.0.is_none() { - return Self(None); + if self.state.is_none() { + return ProofTreeBuilder { state: None }; } - Self(Some(Box::new(DebugSolver::GoalCandidate(WipGoalCandidate { + ProofTreeBuilder::new(WipGoalCandidate { nested_goal_evaluations: vec![], candidates: vec![], kind: None, - })))) + }) } - pub fn candidate_kind(&mut self, kind: CandidateKind<'tcx>) { - let this = match self.0.as_mut() { - None => return, - Some(this) => &mut **this, - }; - - match this { - DebugSolver::GoalCandidate(WipGoalCandidate { kind: old_kind @ None, .. }) => { - *old_kind = Some(kind) + + pub fn candidate_kind(&mut self, candidate_kind: CandidateKind<'tcx>) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalCandidate(this) => { + assert_eq!(this.kind.replace(candidate_kind), None) + } + _ => unreachable!(), } - _ => unreachable!(), } } + 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(WipGoalCandidate { candidates, .. }) - | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }), - DebugSolver::GoalCandidate(candidate), - ) => candidates.push(candidate), - _ => unreachable!(), + if let Some(this) = self.as_mut() { + match (this, *candidate.state.unwrap()) { + ( + DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. }) + | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }), + DebugSolver::GoalCandidate(candidate), + ) => candidates.push(candidate), + _ => unreachable!(), + } } } pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { - if self.0.is_none() { - return Self(None); + if self.state.is_none() { + return ProofTreeBuilder { state: None }; } - Self(Some(Box::new(DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation { - evaluations: vec![], - result: None, - })))) + ProofTreeBuilder::new(WipAddedGoalsEvaluation { evaluations: vec![], result: None }) } + 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![]); + if let Some(this) = self.as_mut() { + match this { + DebugSolver::AddedGoalsEvaluation(this) => { + this.evaluations.push(vec![]); + } + _ => unreachable!(), } - _ => unreachable!(), } } + 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); + if let Some(this) = self.as_mut() { + match this { + DebugSolver::AddedGoalsEvaluation(this) => { + assert_eq!(this.result.replace(result), None); + } + _ => unreachable!(), } - _ => unreachable!(), } } + 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(WipGoalEvaluationStep { - nested_goal_evaluations, - .. - }) - | DebugSolver::GoalCandidate(WipGoalCandidate { nested_goal_evaluations, .. }), - DebugSolver::AddedGoalsEvaluation(added_goals_evaluation), - ) => nested_goal_evaluations.push(added_goals_evaluation), - _ => unreachable!(), + if let Some(this) = self.as_mut() { + match (this, *goals_evaluation.state.unwrap()) { + ( + DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + nested_goal_evaluations, + .. + }) + | DebugSolver::GoalCandidate(WipGoalCandidate { + nested_goal_evaluations, .. + }), + DebugSolver::AddedGoalsEvaluation(added_goals_evaluation), + ) => nested_goal_evaluations.push(added_goals_evaluation), + _ => unreachable!(), + } } } 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); - } - DebugSolver::Root - | DebugSolver::AddedGoalsEvaluation(_) - | DebugSolver::GoalCandidate(_) => unreachable!(), - DebugSolver::GoalEvaluationStep(evaluation_step) => { - assert!(evaluation_step.result.is_none()); - evaluation_step.result = Some(result); + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalEvaluation(goal_evaluation) => { + assert_eq!(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!(), } } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index b99c3927862fc..569400a2f7e6d 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -112,8 +112,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) -> QueryResult<'tcx> { if let Some(projection_pred) = assumption.as_projection_clause() { if projection_pred.projection_def_id() == goal.predicate.def_id() { - ecx.probe( - |ecx| { + ecx.probe(|r| CandidateKind::Candidate { name: "assumption".into(), result: *r }) + .enter(|ecx| { let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); ecx.eq( @@ -128,9 +128,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) .expect("expected goal term to be fully unconstrained"); then(ecx) - }, - |r| CandidateKind::Candidate { name: "assumption".into(), result: *r }, - ) + }) } else { Err(NoSolution) } @@ -154,6 +152,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { } ecx.probe( + |r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter( |ecx| { let impl_substs = ecx.fresh_substs_for_item(impl_def_id); let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); @@ -235,7 +234,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { .expect("expected goal term to be fully unconstrained"); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }, - |r| CandidateKind::Candidate { name: "impl".into(), result: *r }, ) } @@ -331,8 +329,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); - ecx.probe( - |ecx| { + ecx.probe(|r| CandidateKind::Candidate { name: "builtin pointee".into(), result: *r }) + .enter(|ecx| { let metadata_ty = match goal.predicate.self_ty().kind() { ty::Bool | ty::Char @@ -415,9 +413,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into()) .expect("expected goal term to be fully unconstrained"); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - |r| CandidateKind::Candidate { name: "builtin pointee".into(), result: *r }, - ) + }) } fn consider_builtin_future_candidate( @@ -552,14 +548,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ), }; - ecx.probe( - |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) - }, - |r| CandidateKind::Candidate { name: "builtin discriminant kind".into(), result: *r }, - ) + ecx.probe(|r| CandidateKind::Candidate { + name: "builtin discriminant kind".into(), + result: *r, + }) + .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) + }) } fn consider_builtin_destruct_candidate( diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index d9f24455a81f8..19a1626b2cc25 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -62,24 +62,21 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }, }; - ecx.probe( - |ecx| { - let impl_substs = ecx.fresh_substs_for_item(impl_def_id); - let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); - - ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; - let where_clause_bounds = tcx - .predicates_of(impl_def_id) - .instantiate(tcx, impl_substs) - .predicates - .into_iter() - .map(|pred| goal.with(tcx, pred)); - ecx.add_goals(where_clause_bounds); - - ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty) - }, - |r| CandidateKind::Candidate { name: "impl".into(), result: *r }, - ) + ecx.probe(|r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter(|ecx| { + let impl_substs = ecx.fresh_substs_for_item(impl_def_id); + let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); + + ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; + let where_clause_bounds = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_substs) + .predicates + .into_iter() + .map(|pred| goal.with(tcx, pred)); + ecx.add_goals(where_clause_bounds); + + ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty) + }) } fn probe_and_match_goal_against_assumption( @@ -93,8 +90,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { && trait_clause.polarity() == goal.predicate.polarity { // FIXME: Constness - ecx.probe( - |ecx| { + ecx.probe(|r| CandidateKind::Candidate { name: "assumption".into(), result: *r }) + .enter(|ecx| { let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); ecx.eq( goal.param_env, @@ -102,9 +99,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { assumption_trait_pred.trait_ref, )?; then(ecx) - }, - |r| CandidateKind::Candidate { name: "assumption".into(), result: *r }, - ) + }) } else { Err(NoSolution) } @@ -141,7 +136,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let tcx = ecx.tcx(); - ecx.probe( + ecx.probe(|r| CandidateKind::Candidate { name: "trait alias".into(), result: *r }).enter( |ecx| { let nested_obligations = tcx .predicates_of(goal.predicate.def_id()) @@ -149,7 +144,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p))); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }, - |r| CandidateKind::Candidate { name: "trait alias".into(), result: *r }, ) } @@ -356,7 +350,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { if b_ty.is_ty_var() { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); } - ecx.probe( + ecx.probe(|r| CandidateKind::Candidate { name: "builtin unsize".into(), result: *r }).enter( |ecx| { match (a_ty.kind(), b_ty.kind()) { // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` @@ -464,7 +458,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { _ => Err(NoSolution), } }, - |r| CandidateKind::Candidate { name: "builtin unsize".into(), result: *r }, ) } @@ -495,38 +488,36 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } let mut unsize_dyn_to_principal = |principal: Option>| { - ecx.probe( - |ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(goal.param_env, new_a_ty, b_ty)?; - ecx.add_goal( - goal.with( - tcx, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - ), + ecx.probe(|r| CandidateKind::Candidate { + name: "upcast dyn to principle".into(), + result: *r, + }) + .enter(|ecx| -> Result<_, NoSolution> { + // Require that all of the trait predicates from A match B, except for + // the auto traits. We do this by constructing a new A type with B's + // auto traits, and equating these types. + let new_a_data = principal + .into_iter() + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) + .chain(a_data.iter().filter(|a| { + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) + })) + .chain( + b_data + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - |r| CandidateKind::Candidate { name: "upcast dyn to principle".into(), result: *r }, - ) + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); + let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn); + + // We also require that A's lifetime outlives B's lifetime. + ecx.eq(goal.param_env, new_a_ty, b_ty)?; + ecx.add_goal( + goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))), + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) }; let mut responses = vec![]; @@ -723,8 +714,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, TraitPredicate<'tcx>>, constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result>, NoSolution>, ) -> QueryResult<'tcx> { - self.probe( - |ecx| { + self.probe(|r| CandidateKind::Candidate { name: "constituent tys".into(), result: *r }) + .enter(|ecx| { ecx.add_goals( constituent_tys(ecx, goal.predicate.self_ty())? .into_iter() @@ -737,9 +728,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .collect::>(), ); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - |r| CandidateKind::Candidate { name: "constituent tys".into(), result: *r }, - ) + }) } #[instrument(level = "debug", skip(self))]