From 688dc25b34a6ae36e05bf2bc549a61b9271e7b51 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 3 May 2024 22:13:58 -0400 Subject: [PATCH 1/2] Canonicalize all nested goals together at once when making response --- .../rustc_middle/src/traits/solve/inspect.rs | 8 ++- .../src/traits/solve/inspect/format.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 15 +++++ .../rustc_middle/src/ty/structural_impls.rs | 12 ++++ .../src/solve/eval_ctxt/canonical.rs | 4 +- .../src/solve/eval_ctxt/mod.rs | 4 +- .../src/solve/inspect/analyse.rs | 61 ++++++++----------- .../src/solve/inspect/build.rs | 59 ++++++++++-------- 8 files changed, 96 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index cddf9d5f874a3..8ab3ab9eccd01 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -116,7 +116,7 @@ impl Debug for Probe<'_> { pub enum ProbeStep<'tcx> { /// We added a goal to the `EvalCtxt` which will get proven /// the next time `EvalCtxt::try_evaluate_added_goals` is called. - AddGoal(GoalSource, CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>), + AddGoal(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>), /// The inside of a `EvalCtxt::try_evaluate_added_goals` call. EvaluateGoals(AddedGoalsEvaluation<'tcx>), /// A call to `probe` while proving the current goal. This is @@ -128,7 +128,11 @@ pub enum ProbeStep<'tcx> { /// with the certainty of the `try_evaluate_added_goals` that is done within; /// if it's `Certainty::Yes`, then we can trust that the candidate is "finished" /// and we didn't force ambiguity for some reason. - MakeCanonicalResponse { shallow_certainty: Certainty }, + MakeCanonicalResponse { + shallow_certainty: Certainty, + added_goals: + CanonicalState<'tcx, &'tcx ty::List<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>>, + }, } /// What kind of probe we're in. In case the probe represents a candidate, or diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 11aa0e10931cb..6d20fcd6685b3 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -133,7 +133,7 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { } ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?, ProbeStep::NestedProbe(probe) => this.format_probe(probe)?, - ProbeStep::MakeCanonicalResponse { shallow_certainty } => { + ProbeStep::MakeCanonicalResponse { shallow_certainty, added_goals: _ } => { writeln!(this.f, "EVALUATE GOALS AND MAKE RESPONSE: {shallow_certainty:?}")? } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d2eacdf762f1b..fc31dd369b2a6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -157,6 +157,8 @@ pub struct CtxtInterners<'tcx> { poly_existential_predicates: InternedSet<'tcx, List>>, predicate: InternedSet<'tcx, WithCachedTypeInfo>>>, clauses: InternedSet<'tcx, ListWithCachedTypeInfo>>, + nested_goals: + InternedSet<'tcx, List<(solve::GoalSource, solve::Goal<'tcx, ty::Predicate<'tcx>>)>>, projs: InternedSet<'tcx, List>, place_elems: InternedSet<'tcx, List>>, const_: InternedSet<'tcx, WithCachedTypeInfo>>, @@ -186,6 +188,7 @@ impl<'tcx> CtxtInterners<'tcx> { canonical_var_infos: Default::default(), predicate: Default::default(), clauses: Default::default(), + nested_goals: Default::default(), projs: Default::default(), place_elems: Default::default(), const_: Default::default(), @@ -1933,6 +1936,7 @@ slice_interners!( poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>), projs: pub mk_projs(ProjectionKind), place_elems: pub mk_place_elems(PlaceElem<'tcx>), + nested_goals: pub mk_nested_goals((solve::GoalSource, solve::Goal<'tcx, ty::Predicate<'tcx>>)), bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind), fields: pub mk_fields(FieldIdx), local_def_ids: intern_local_def_ids(LocalDefId), @@ -2320,6 +2324,17 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_clauses(xs)) } + pub fn mk_nested_goals_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: CollectAndApply< + (solve::GoalSource, solve::Goal<'tcx, ty::Predicate<'tcx>>), + &'tcx List<(solve::GoalSource, solve::Goal<'tcx, ty::Predicate<'tcx>>)>, + >, + { + T::collect_and_apply(iter, |xs| self.mk_nested_goals(xs)) + } + pub fn mk_type_list_from_iter(self, iter: I) -> T::Output where I: Iterator, diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 14a77d4b37ec7..f5cb4b9585761 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -4,6 +4,7 @@ //! to help with the tedium. use crate::mir::interpret; +use crate::traits::solve; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer}; use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; @@ -560,6 +561,17 @@ impl<'tcx> TypeFoldable> for &'tcx ty::List> { } } +impl<'tcx> TypeFoldable> + for &'tcx ty::List<(solve::GoalSource, solve::Goal<'tcx, ty::Predicate<'tcx>>)> +{ + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + ty::util::fold_list(self, folder, |tcx, v| tcx.mk_nested_goals(v)) + } +} + impl<'tcx> TypeFoldable> for Pattern<'tcx> { fn try_fold_with>>( self, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index d6bf2b596ef1e..bc8230f6fca72 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -90,7 +90,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, certainty: Certainty, ) -> QueryResult<'tcx> { - self.inspect.make_canonical_response(certainty); + self.inspect.make_canonical_response(self.infcx, self.max_input_universe, certainty); let goals_certainty = self.try_evaluate_added_goals()?; assert_eq!( @@ -444,5 +444,5 @@ pub(in crate::solve) fn instantiate_canonical_state<'tcx, T: TypeFoldable EvalCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub(super) fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { - self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, goal); + self.inspect.add_normalizes_to_goal(self.tcx(), goal); self.nested_goals.normalizes_to_goals.push(goal); } #[instrument(level = "debug", skip(self))] pub(super) fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) { - self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal); + self.inspect.add_goal(source, goal); self.nested_goals.goals.push((source, goal)); } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 4f79f1b2aafe0..bf51fcfdf7146 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -91,7 +91,10 @@ impl<'tcx> NormalizesToTermHack<'tcx> { pub struct InspectCandidate<'a, 'tcx> { goal: &'a InspectGoal<'a, 'tcx>, kind: inspect::ProbeKind<'tcx>, - nested_goals: Vec<(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>)>, + nested_goals: inspect::CanonicalState< + 'tcx, + &'tcx ty::List<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>, + >, final_state: inspect::CanonicalState<'tcx, ()>, result: QueryResult<'tcx>, shallow_certainty: Certainty, @@ -139,23 +142,15 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { let infcx = self.goal.infcx; let param_env = self.goal.goal.param_env; let mut orig_values = self.goal.orig_values.to_vec(); - let instantiated_goals: Vec<_> = self - .nested_goals - .iter() - .map(|(source, goal)| { - ( - *source, - canonical::instantiate_canonical_state( - infcx, - span, - param_env, - &mut orig_values, - *goal, - ), - ) - }) - .collect(); - + let instantiated_goals = canonical::instantiate_canonical_state( + infcx, + span, + param_env, + &mut orig_values, + self.nested_goals, + ); + // FIXME: Should we truncate orig_values back down? Any new infer vars + // between `nested_goals` and `final_state` are definitely unrelated... let () = canonical::instantiate_canonical_state( infcx, span, @@ -172,7 +167,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { } instantiated_goals - .into_iter() + .iter() .map(|(source, goal)| match goal.predicate.kind().no_bound_vars() { Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => { let unconstrained_term = match term.unpack() { @@ -238,27 +233,24 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { fn candidates_recur( &'a self, candidates: &mut Vec>, - nested_goals: &mut Vec<( - GoalSource, - inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>, - )>, probe: &inspect::Probe<'tcx>, ) { - let mut shallow_certainty = None; + let mut shallow_certainty_and_goals = None; for step in &probe.steps { - match step { - &inspect::ProbeStep::AddGoal(source, goal) => nested_goals.push((source, goal)), + match *step { inspect::ProbeStep::NestedProbe(ref probe) => { // Nested probes have to prove goals added in their parent // but do not leak them, so we truncate the added goals // afterwards. - let num_goals = nested_goals.len(); - self.candidates_recur(candidates, nested_goals, probe); - nested_goals.truncate(num_goals); + self.candidates_recur(candidates, probe); } - inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { - assert_eq!(shallow_certainty.replace(*c), None); + inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty, added_goals } => { + assert_eq!( + shallow_certainty_and_goals.replace((shallow_certainty, added_goals)), + None + ); } + inspect::ProbeStep::AddGoal(..) => {} inspect::ProbeStep::EvaluateGoals(_) => (), } } @@ -276,11 +268,11 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { | inspect::ProbeKind::OpaqueTypeStorageLookup { result } => { // We only add a candidate if `shallow_certainty` was set, which means // that we ended up calling `evaluate_added_goals_and_make_canonical_response`. - if let Some(shallow_certainty) = shallow_certainty { + if let Some((shallow_certainty, nested_goals)) = shallow_certainty_and_goals { candidates.push(InspectCandidate { goal: self, kind: probe.kind, - nested_goals: nested_goals.clone(), + nested_goals, final_state: probe.final_state, result, shallow_certainty, @@ -308,8 +300,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { } }; - let mut nested_goals = vec![]; - self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step.evaluation); + self.candidates_recur(&mut candidates, &last_eval_step.evaluation); candidates } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index 466d0d8006018..ad2d3a9a3deca 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -238,10 +238,16 @@ impl<'tcx> WipProbe<'tcx> { #[derive(Eq, PartialEq, Debug)] enum WipProbeStep<'tcx> { - AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>), + AddGoal(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>), EvaluateGoals(WipAddedGoalsEvaluation<'tcx>), NestedProbe(WipProbe<'tcx>), - MakeCanonicalResponse { shallow_certainty: Certainty }, + MakeCanonicalResponse { + shallow_certainty: Certainty, + added_goals: inspect::CanonicalState< + 'tcx, + &'tcx ty::List<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>, + >, + }, } impl<'tcx> WipProbeStep<'tcx> { @@ -250,8 +256,8 @@ impl<'tcx> WipProbeStep<'tcx> { WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal), WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()), WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()), - WipProbeStep::MakeCanonicalResponse { shallow_certainty } => { - inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty } + WipProbeStep::MakeCanonicalResponse { shallow_certainty, added_goals } => { + inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty, added_goals } } } } @@ -500,47 +506,46 @@ impl<'tcx> ProofTreeBuilder<'tcx> { pub fn add_normalizes_to_goal( &mut self, - infcx: &InferCtxt<'tcx>, - max_input_universe: ty::UniverseIndex, + tcx: TyCtxt<'tcx>, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, ) { - self.add_goal( - infcx, - max_input_universe, - GoalSource::Misc, - goal.with(infcx.tcx, goal.predicate), - ); + self.add_goal(GoalSource::Misc, goal.with(tcx, goal.predicate)); } - pub fn add_goal( - &mut self, - infcx: &InferCtxt<'tcx>, - max_input_universe: ty::UniverseIndex, - source: GoalSource, - goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) { + pub fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) { match self.as_mut() { None => {} Some(DebugSolver::GoalEvaluationStep(state)) => { - let goal = canonical::make_canonical_state( - infcx, - &state.var_values, - max_input_universe, - goal, - ); state.current_evaluation_scope().steps.push(WipProbeStep::AddGoal(source, goal)) } _ => bug!(), } } - pub fn make_canonical_response(&mut self, shallow_certainty: Certainty) { + pub fn make_canonical_response( + &mut self, + infcx: &InferCtxt<'tcx>, + max_input_universe: ty::UniverseIndex, + shallow_certainty: Certainty, + ) { match self.as_mut() { Some(DebugSolver::GoalEvaluationStep(state)) => { + let added_goals = infcx.tcx.mk_nested_goals_from_iter( + state.current_evaluation_scope().steps.iter().filter_map(|step| match *step { + WipProbeStep::AddGoal(source, goal) => Some((source, goal)), + _ => None, + }), + ); + let added_goals = canonical::make_canonical_state( + infcx, + &state.var_values, + max_input_universe, + added_goals, + ); state .current_evaluation_scope() .steps - .push(WipProbeStep::MakeCanonicalResponse { shallow_certainty }); + .push(WipProbeStep::MakeCanonicalResponse { shallow_certainty, added_goals }); } None => {} _ => {} From f17bb9655ae4b97268d6dfa7f488522d30fe038c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 3 May 2024 23:28:37 -0400 Subject: [PATCH 2/2] Collect *all* added goals, not just those from our probe --- compiler/rustc_trait_selection/src/lib.rs | 6 ++-- .../src/solve/inspect/build.rs | 35 +++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index f1f03b810a961..3cfd395e72972 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -12,7 +12,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] @@ -20,11 +19,14 @@ #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(control_flow_enum)] +#![feature(coroutines)] #![feature(extract_if)] #![feature(if_let_guard)] +#![feature(iter_from_coroutine)] #![feature(let_chains)] -#![feature(option_take_if)] #![feature(never_type)] +#![feature(option_take_if)] +#![feature(rustdoc_internals)] #![feature(type_alias_impl_trait)] #![recursion_limit = "512"] // For rustdoc diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index ad2d3a9a3deca..8c734dbb24fa1 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -216,6 +216,34 @@ impl<'tcx> WipGoalEvaluationStep<'tcx> { } inspect::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, evaluation } } + + // Returns all added goals from this scope and all containing scopes. + fn all_added_goals( + &self, + ) -> impl Iterator>)> + '_ { + std::iter::from_coroutine( + #[coroutine] + || { + let mut current = &self.evaluation; + for i in 0.. { + for step in ¤t.steps { + if let WipProbeStep::AddGoal(source, goal) = *step { + yield (source, goal); + } + } + + if i < self.probe_depth { + let Some(WipProbeStep::NestedProbe(p)) = current.steps.last() else { + bug!(); + }; + current = p; + } else { + break; + } + } + }, + ) + } } #[derive(Eq, PartialEq, Debug)] @@ -530,12 +558,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { ) { match self.as_mut() { Some(DebugSolver::GoalEvaluationStep(state)) => { - let added_goals = infcx.tcx.mk_nested_goals_from_iter( - state.current_evaluation_scope().steps.iter().filter_map(|step| match *step { - WipProbeStep::AddGoal(source, goal) => Some((source, goal)), - _ => None, - }), - ); + let added_goals = infcx.tcx.mk_nested_goals_from_iter(state.all_added_goals()); let added_goals = canonical::make_canonical_state( infcx, &state.var_values,