about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection')
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs28
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs35
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs101
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs140
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs11
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs99
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/build.rs318
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs49
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph.rs31
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs51
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs21
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs63
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs15
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs21
29 files changed, 500 insertions, 572 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index 43e61de955a..4d7e2fc2cef 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -26,8 +26,9 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         &mut self,
         goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
     ) -> QueryResult<'tcx> {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
+        debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some());
 
         // Structurally normalize the lhs.
         let lhs = if let Some(alias) = lhs.to_alias_term() {
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 11524410692..aae6fa9f635 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -83,7 +83,7 @@ pub(super) trait GoalKind<'tcx>:
         assumption: ty::Clause<'tcx>,
     ) -> Result<Candidate<'tcx>, NoSolution> {
         Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
-            let tcx = ecx.tcx();
+            let tcx = ecx.interner();
             let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
                 bug!("expected object type in `probe_and_consider_object_bound_candidate`");
             };
@@ -288,8 +288,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect();
         }
 
-        let goal: Goal<'tcx, G> =
-            goal.with(self.tcx(), goal.predicate.with_self_ty(self.tcx(), normalized_self_ty));
+        let goal: Goal<'tcx, G> = goal.with(
+            self.interner(),
+            goal.predicate.with_self_ty(self.interner(), normalized_self_ty),
+        );
         // Vars that show up in the rest of the goal substs may have been constrained by
         // normalizing the self type as well, since type variables are not uniquified.
         let goal = self.resolve_vars_if_possible(goal);
@@ -339,7 +341,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         goal: Goal<'tcx, G>,
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let self_ty = goal.predicate.self_ty();
         let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
         let mut consider_impls_for_simplified_type = |simp| {
@@ -455,7 +457,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         goal: Goal<'tcx, G>,
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
         for &impl_def_id in trait_impls.blanket_impls() {
             // For every `default impl`, there's always a non-default `impl`
@@ -478,7 +480,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         goal: Goal<'tcx, G>,
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let lang_items = tcx.lang_items();
         let trait_def_id = goal.predicate.trait_def_id(tcx);
 
@@ -505,9 +507,9 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             G::consider_builtin_pointer_like_candidate(self, goal)
         } else if lang_items.fn_ptr_trait() == Some(trait_def_id) {
             G::consider_builtin_fn_ptr_trait_candidate(self, goal)
-        } else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) {
+        } else if let Some(kind) = self.interner().fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_fn_trait_candidates(self, goal, kind)
-        } else if let Some(kind) = self.tcx().async_fn_trait_kind_from_def_id(trait_def_id) {
+        } else if let Some(kind) = self.interner().async_fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_async_fn_trait_candidates(self, goal, kind)
         } else if lang_items.async_fn_kind_helper() == Some(trait_def_id) {
             G::consider_builtin_async_fn_kind_helper_candidate(self, goal)
@@ -634,7 +636,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 
             ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty),
             ty::Alias(ty::Inherent | ty::Weak, _) => {
-                self.tcx().sess.dcx().span_delayed_bug(
+                self.interner().sess.dcx().span_delayed_bug(
                     DUMMY_SP,
                     format!("could not normalize {self_ty}, it is not WF"),
                 );
@@ -643,7 +645,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         };
 
         for assumption in
-            self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args)
+            self.interner().item_bounds(alias_ty.def_id).instantiate(self.interner(), alias_ty.args)
         {
             candidates.extend(G::probe_and_consider_implied_clause(
                 self,
@@ -673,7 +675,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         goal: Goal<'tcx, G>,
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         if !tcx.trait_def(goal.predicate.trait_def_id(tcx)).implement_via_object {
             return;
         }
@@ -764,7 +766,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         goal: Goal<'tcx, G>,
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
-        let tcx = self.tcx();
+        let tcx = self.interner();
 
         candidates.extend(self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(
             |ecx| {
@@ -793,7 +795,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         goal: Goal<'tcx, G>,
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> =
             goal.with(tcx, goal.predicate.trait_ref(tcx));
 
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 930ae5af811..64d5f725a1f 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -22,7 +22,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
     ecx: &EvalCtxt<'_, InferCtxt<'tcx>>,
     ty: Ty<'tcx>,
 ) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
-    let tcx = ecx.tcx();
+    let tcx = ecx.interner();
     match *ty.kind() {
         ty::Uint(_)
         | ty::Int(_)
@@ -75,7 +75,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
         }
 
         ty::CoroutineWitness(def_id, args) => Ok(ecx
-            .tcx()
+            .interner()
             .bound_coroutine_hidden_types(def_id)
             .map(|bty| bty.instantiate(tcx, args))
             .collect()),
@@ -151,8 +151,8 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
         //   "best effort" optimization and `sized_constraint` may return `Some`, even
         //   if the ADT is sized for all possible args.
         ty::Adt(def, args) => {
-            if let Some(sized_crit) = def.sized_constraint(ecx.tcx()) {
-                Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.tcx(), args))])
+            if let Some(sized_crit) = def.sized_constraint(ecx.interner()) {
+                Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.interner(), args))])
             } else {
                 Ok(vec![])
             }
@@ -210,10 +210,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
 
         // only when `coroutine_clone` is enabled and the coroutine is movable
         // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses)
-        ty::Coroutine(def_id, args) => match ecx.tcx().coroutine_movability(def_id) {
+        ty::Coroutine(def_id, args) => match ecx.interner().coroutine_movability(def_id) {
             Movability::Static => Err(NoSolution),
             Movability::Movable => {
-                if ecx.tcx().features().coroutine_clone {
+                if ecx.interner().features().coroutine_clone {
                     let coroutine = args.as_coroutine();
                     Ok(vec![
                         ty::Binder::dummy(coroutine.tupled_upvars_ty()),
@@ -227,9 +227,9 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
 
         // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types
         ty::CoroutineWitness(def_id, args) => Ok(ecx
-            .tcx()
+            .interner()
             .bound_coroutine_hidden_types(def_id)
-            .map(|bty| bty.instantiate(ecx.tcx(), args))
+            .map(|bty| bty.instantiate(ecx.interner(), args))
             .collect()),
     }
 }
@@ -300,14 +300,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
                     return Err(NoSolution);
                 }
 
-                // If `Fn`/`FnMut`, we only implement this goal if we
-                // have no captures.
-                let no_borrows = match args.tupled_upvars_ty().kind() {
-                    ty::Tuple(tys) => tys.is_empty(),
-                    ty::Error(_) => false,
-                    _ => bug!("tuple_fields called on non-tuple"),
-                };
-                if closure_kind != ty::ClosureKind::FnOnce && !no_borrows {
+                // A coroutine-closure implements `FnOnce` *always*, since it may
+                // always be called once. It additionally implements `Fn`/`FnMut`
+                // only if it has no upvars referencing the closure-env lifetime,
+                // and if the closure kind permits it.
+                if closure_kind != ty::ClosureKind::FnOnce && args.has_self_borrows() {
                     return Err(NoSolution);
                 }
 
@@ -669,7 +666,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
     trait_ref: ty::TraitRef<'tcx>,
     object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
 ) -> Vec<Goal<'tcx, ty::Predicate<'tcx>>> {
-    let tcx = ecx.tcx();
+    let tcx = ecx.interner();
     let mut requirements = vec![];
     requirements.extend(
         tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.args).predicates,
@@ -725,7 +722,7 @@ struct ReplaceProjectionWith<'a, 'tcx> {
 
 impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
     fn interner(&self) -> TyCtxt<'tcx> {
-        self.ecx.tcx()
+        self.ecx.interner()
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
@@ -742,7 +739,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
                     .eq_and_get_goals(
                         self.param_env,
                         alias_ty,
-                        proj.projection_term.expect_ty(self.ecx.tcx()),
+                        proj.projection_term.expect_ty(self.ecx.interner()),
                     )
                     .expect("expected to be able to unify goal projection with dyn's projection"),
             );
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 9590a82c067..690c1797f23 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -16,7 +16,6 @@ use crate::solve::{
 use rustc_data_structures::fx::FxHashSet;
 use rustc_index::IndexVec;
 use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
-use rustc_infer::infer::canonical::CanonicalVarValues;
 use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
 use rustc_infer::infer::RegionVariableOrigin;
 use rustc_infer::infer::{InferCtxt, InferOk};
@@ -32,22 +31,24 @@ use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}
 use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer};
 use rustc_next_trait_solver::resolve::EagerResolver;
 use rustc_span::{Span, DUMMY_SP};
+use rustc_type_ir::CanonicalVarValues;
+use rustc_type_ir::{InferCtxtLike, Interner};
 use std::assert_matches::assert_matches;
 use std::iter;
 use std::ops::Deref;
 
 trait ResponseT<'tcx> {
-    fn var_values(&self) -> CanonicalVarValues<'tcx>;
+    fn var_values(&self) -> CanonicalVarValues<TyCtxt<'tcx>>;
 }
 
 impl<'tcx> ResponseT<'tcx> for Response<TyCtxt<'tcx>> {
-    fn var_values(&self) -> CanonicalVarValues<'tcx> {
+    fn var_values(&self) -> CanonicalVarValues<TyCtxt<'tcx>> {
         self.var_values
     }
 }
 
 impl<'tcx, T> ResponseT<'tcx> for inspect::State<TyCtxt<'tcx>, T> {
-    fn var_values(&self) -> CanonicalVarValues<'tcx> {
+    fn var_values(&self) -> CanonicalVarValues<TyCtxt<'tcx>> {
         self.var_values
     }
 }
@@ -71,7 +72,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             QueryInput {
                 goal,
                 predefined_opaques_in_body: self
-                    .tcx()
+                    .interner()
                     .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }),
             },
         );
@@ -99,6 +100,13 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             previous call to `try_evaluate_added_goals!`"
         );
 
+        // We only check for leaks from universes which were entered inside
+        // of the query.
+        self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
+            trace!(?e, "failed the leak check");
+            NoSolution
+        })?;
+
         // When normalizing, we've replaced the expected term with an unconstrained
         // inference variable. This means that we dropped information which could
         // have been important. We handle this by instead returning the nested goals
@@ -121,7 +129,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         };
 
         let external_constraints =
-            self.compute_external_query_constraints(normalization_nested_goals)?;
+            self.compute_external_query_constraints(certainty, normalization_nested_goals);
         let (var_values, mut external_constraints) =
             (self.var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx));
         // Remove any trivial region constraints once we've resolved regions
@@ -137,7 +145,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             Response {
                 var_values,
                 certainty,
-                external_constraints: self.tcx().mk_external_constraints(external_constraints),
+                external_constraints: self.interner().mk_external_constraints(external_constraints),
             },
         );
 
@@ -153,7 +161,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         maybe_cause: MaybeCause,
     ) -> CanonicalResponse<'tcx> {
         response_no_constraints_raw(
-            self.tcx(),
+            self.interner(),
             self.max_input_universe,
             self.variables,
             Certainty::Maybe(maybe_cause),
@@ -170,30 +178,37 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     #[instrument(level = "trace", skip(self), ret)]
     fn compute_external_query_constraints(
         &self,
+        certainty: Certainty,
         normalization_nested_goals: NestedNormalizationGoals<'tcx>,
-    ) -> Result<ExternalConstraintsData<'tcx>, NoSolution> {
-        // We only check for leaks from universes which were entered inside
-        // of the query.
-        self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
-            trace!(?e, "failed the leak check");
-            NoSolution
-        })?;
-
-        // Cannot use `take_registered_region_obligations` as we may compute the response
-        // inside of a `probe` whenever we have multiple choices inside of the solver.
-        let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
-        let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| {
-            make_query_region_constraints(
-                self.tcx(),
-                region_obligations
-                    .iter()
-                    .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())),
-                region_constraints,
-            )
-        });
-
-        let mut seen = FxHashSet::default();
-        region_constraints.outlives.retain(|outlives| seen.insert(*outlives));
+    ) -> ExternalConstraintsData<'tcx> {
+        // We only return region constraints once the certainty is `Yes`. This
+        // is necessary as we may drop nested goals on ambiguity, which may result
+        // in unconstrained inference variables in the region constraints. It also
+        // prevents us from emitting duplicate region constraints, avoiding some
+        // unnecessary work. This slightly weakens the leak check in case it uses
+        // region constraints from an ambiguous nested goal. This is tested in both
+        // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and
+        // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`.
+        let region_constraints = if certainty == Certainty::Yes {
+            // Cannot use `take_registered_region_obligations` as we may compute the response
+            // inside of a `probe` whenever we have multiple choices inside of the solver.
+            let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
+            let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| {
+                make_query_region_constraints(
+                    self.interner(),
+                    region_obligations.iter().map(|r_o| {
+                        (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())
+                    }),
+                    region_constraints,
+                )
+            });
+
+            let mut seen = FxHashSet::default();
+            region_constraints.outlives.retain(|outlives| seen.insert(*outlives));
+            region_constraints
+        } else {
+            Default::default()
+        };
 
         let mut opaque_types = self.infcx.clone_opaque_types_for_query_response();
         // Only return opaque type keys for newly-defined opaques
@@ -201,7 +216,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
         });
 
-        Ok(ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals })
+        ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals }
     }
 
     /// After calling a canonical query, we apply the constraints returned
@@ -225,7 +240,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         );
 
         let Response { var_values, external_constraints, certainty } =
-            response.instantiate(self.tcx(), &instantiation);
+            response.instantiate(self.interner(), &instantiation);
 
         Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values);
 
@@ -246,7 +261,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         infcx: &InferCtxt<'tcx>,
         original_values: &[ty::GenericArg<'tcx>],
         response: &Canonical<'tcx, T>,
-    ) -> CanonicalVarValues<'tcx> {
+    ) -> CanonicalVarValues<TyCtxt<'tcx>> {
         // FIXME: Longterm canonical queries should deal with all placeholders
         // created inside of the query directly instead of returning them to the
         // caller.
@@ -340,7 +355,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         infcx: &InferCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         original_values: &[ty::GenericArg<'tcx>],
-        var_values: CanonicalVarValues<'tcx>,
+        var_values: CanonicalVarValues<TyCtxt<'tcx>>,
     ) {
         assert_eq!(original_values.len(), var_values.len());
 
@@ -348,7 +363,6 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         for (&orig, response) in iter::zip(original_values, var_values.var_values) {
             let InferOk { value: (), obligations } = infcx
                 .at(&cause, param_env)
-                .trace(orig, response)
                 .eq_structurally_relating_aliases(orig, response)
                 .unwrap();
             assert!(obligations.is_empty());
@@ -379,13 +393,18 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 /// evaluating a goal. The `var_values` not only include the bound variables
 /// of the query input, but also contain all unconstrained inference vars
 /// created while evaluating this goal.
-pub(in crate::solve) fn make_canonical_state<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
-    infcx: &InferCtxt<'tcx>,
-    var_values: &[ty::GenericArg<'tcx>],
+pub(in crate::solve) fn make_canonical_state<Infcx, T, I>(
+    infcx: &Infcx,
+    var_values: &[I::GenericArg],
     max_input_universe: ty::UniverseIndex,
     data: T,
-) -> inspect::CanonicalState<TyCtxt<'tcx>, T> {
-    let var_values = CanonicalVarValues { var_values: infcx.tcx.mk_args(var_values) };
+) -> inspect::CanonicalState<I, T>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    T: TypeFoldable<I>,
+{
+    let var_values = CanonicalVarValues { var_values: infcx.interner().mk_args(var_values) };
     let state = inspect::State { var_values, data };
     let state = state.fold_with(&mut EagerResolver::new(infcx));
     Canonicalizer::canonicalize(
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
index 7e1d7d73e0b..b18b59d9a75 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -1,6 +1,3 @@
-use std::io::Write;
-use std::ops::ControlFlow;
-
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::at::ToTrace;
@@ -16,14 +13,17 @@ use rustc_middle::traits::solve::{
     inspect, CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaquesData, QueryResult,
 };
 use rustc_middle::traits::specialization_graph;
+use rustc_middle::ty::AliasRelationDirection;
+use rustc_middle::ty::TypeFolder;
 use rustc_middle::ty::{
     self, InferCtxtLike, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
     TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
-use rustc_session::config::DumpSolverProofTree;
 use rustc_span::DUMMY_SP;
+use rustc_type_ir::fold::TypeSuperFoldable;
 use rustc_type_ir::{self as ir, CanonicalVarValues, Interner};
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
+use std::ops::ControlFlow;
 
 use crate::traits::coherence;
 use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
@@ -98,7 +98,7 @@ pub struct EvalCtxt<
     // evaluation code.
     tainted: Result<(), NoSolution>,
 
-    pub(super) inspect: ProofTreeBuilder<I>,
+    pub(super) inspect: ProofTreeBuilder<Infcx>,
 }
 
 #[derive(derivative::Derivative)]
@@ -135,8 +135,7 @@ impl<I: Interner> NestedGoals<I> {
 #[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
 pub enum GenerateProofTree {
     Yes,
-    IfEnabled,
-    Never,
+    No,
 }
 
 #[extension(pub trait InferCtxtEvalExt<'tcx>)]
@@ -182,7 +181,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
             infcx,
             search_graph: &mut search_graph,
             nested_goals: NestedGoals::new(),
-            inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
+            inspect: ProofTreeBuilder::new_maybe_root(generate_proof_tree),
 
             // Only relevant when canonicalizing the response,
             // which we don't do within this evaluation context.
@@ -197,23 +196,14 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         };
         let result = f(&mut ecx);
 
-        let tree = ecx.inspect.finalize();
-        if let (Some(tree), DumpSolverProofTree::Always) = (
-            &tree,
-            infcx.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default(),
-        ) {
-            let mut lock = std::io::stdout().lock();
-            let _ = lock.write_fmt(format_args!("{tree:?}\n"));
-            let _ = lock.flush();
-        }
-
+        let proof_tree = ecx.inspect.finalize();
         assert!(
             ecx.nested_goals.is_empty(),
             "root `EvalCtxt` should not have any goals added to it"
         );
 
         assert!(search_graph.is_empty());
-        (result, tree)
+        (result, proof_tree)
     }
 
     /// Creates a nested evaluation context that shares the same search graph as the
@@ -228,7 +218,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         tcx: TyCtxt<'tcx>,
         search_graph: &'a mut search_graph::SearchGraph<TyCtxt<'tcx>>,
         canonical_input: CanonicalInput<'tcx>,
-        canonical_goal_evaluation: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
+        canonical_goal_evaluation: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
         f: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
     ) -> R {
         let intercrate = match search_graph.solver_mode() {
@@ -290,7 +280,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         tcx: TyCtxt<'tcx>,
         search_graph: &'a mut search_graph::SearchGraph<TyCtxt<'tcx>>,
         canonical_input: CanonicalInput<'tcx>,
-        goal_evaluation: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
+        goal_evaluation: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
     ) -> QueryResult<'tcx> {
         let mut canonical_goal_evaluation =
             goal_evaluation.new_canonical_goal_evaluation(canonical_input);
@@ -357,7 +347,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         let mut goal_evaluation =
             self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
         let canonical_response = EvalCtxt::evaluate_canonical_goal(
-            self.tcx(),
+            self.interner(),
             self.search_graph,
             canonical_goal,
             &mut goal_evaluation,
@@ -460,7 +450,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
             }
         } else {
             self.infcx.enter_forall(kind, |kind| {
-                let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
+                let goal = goal.with(self.interner(), ty::Binder::dummy(kind));
                 self.add_goal(GoalSource::InstantiateHigherRanked, goal);
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             })
@@ -468,13 +458,23 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     }
 
     #[instrument(level = "trace", skip(self))]
-    pub(super) fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
+    pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
+        goal.predicate = goal
+            .predicate
+            .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
         self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, 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>>) {
+    pub(super) fn add_goal(
+        &mut self,
+        source: GoalSource,
+        mut goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    ) {
+        goal.predicate = goal
+            .predicate
+            .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
         self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal);
         self.nested_goals.goals.push((source, goal));
     }
@@ -483,7 +483,6 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     // the certainty of all the goals.
     #[instrument(level = "trace", skip(self))]
     pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> {
-        self.inspect.start_evaluate_added_goals();
         let mut response = Ok(Certainty::overflow(false));
         for _ in 0..FIXPOINT_STEP_LIMIT {
             // FIXME: This match is a bit ugly, it might be nice to change the inspect
@@ -501,8 +500,6 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
             }
         }
 
-        self.inspect.evaluate_added_goals_result(response);
-
         if response.is_err() {
             self.tainted = Err(NoSolution);
         }
@@ -514,8 +511,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     ///
     /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
     fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
-        let tcx = self.tcx();
-        self.inspect.start_evaluate_added_goals_step();
+        let tcx = self.interner();
         let mut goals = core::mem::take(&mut self.nested_goals);
 
         // If this loop did not result in any progress, what's our final certainty.
@@ -601,11 +597,13 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     }
 }
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
-    pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
-        self.infcx.tcx
+impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
+    pub(super) fn interner(&self) -> I {
+        self.infcx.interner()
     }
+}
 
+impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     pub(super) fn next_ty_infer(&mut self) -> Ty<'tcx> {
         let ty = self.infcx.next_ty_var(DUMMY_SP);
         self.inspect.add_var_value(ty);
@@ -763,7 +761,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         // NOTE: this check is purely an optimization, the structural eq would
         // always fail if the term is not an inference variable.
         if term.is_infer() {
-            let tcx = self.tcx();
+            let tcx = self.interner();
             // We need to relate `alias` to `term` treating only the outermost
             // constructor as rigid, relating any contained generic arguments as
             // normal. We do this by first structurally equating the `term`
@@ -778,7 +776,6 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             let InferOk { value: (), obligations } = self
                 .infcx
                 .at(&ObligationCause::dummy(), param_env)
-                .trace(term, ctor_term)
                 .eq_structurally_relating_aliases(term, ctor_term)?;
             debug_assert!(obligations.is_empty());
             self.relate(param_env, alias, variance, rigid_ctor)
@@ -798,11 +795,8 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         rhs: T,
     ) -> Result<(), NoSolution> {
         let cause = ObligationCause::dummy();
-        let InferOk { value: (), obligations } = self
-            .infcx
-            .at(&cause, param_env)
-            .trace(lhs, rhs)
-            .eq_structurally_relating_aliases(lhs, rhs)?;
+        let InferOk { value: (), obligations } =
+            self.infcx.at(&cause, param_env).eq_structurally_relating_aliases(lhs, rhs)?;
         assert!(obligations.is_empty());
         Ok(())
     }
@@ -1058,10 +1052,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     ) -> Option<ty::Const<'tcx>> {
         use rustc_middle::mir::interpret::ErrorHandled;
         match self.infcx.const_eval_resolve(param_env, unevaluated, DUMMY_SP) {
-            Ok(Some(val)) => Some(ty::Const::new_value(self.tcx(), val, ty)),
+            Ok(Some(val)) => Some(ty::Const::new_value(self.interner(), val, ty)),
             Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None,
             Err(ErrorHandled::Reported(e, _)) => {
-                Some(ty::Const::new_error(self.tcx(), e.into(), ty))
+                Some(ty::Const::new_error(self.interner(), e.into(), ty))
             }
         }
     }
@@ -1074,7 +1068,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         principal: ty::PolyTraitRef<'tcx>,
         mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option<usize>),
     ) {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let mut offset = 0;
         prepare_vtable_segments::<()>(tcx, principal, |segment| {
             match segment {
@@ -1101,3 +1095,63 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         });
     }
 }
+
+/// Eagerly replace aliases with inference variables, emitting `AliasRelate`
+/// goals, used when adding goals to the `EvalCtxt`. We compute the
+/// `AliasRelate` goals before evaluating the actual goal to get all the
+/// constraints we can.
+///
+/// This is a performance optimization to more eagerly detect cycles during trait
+/// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs.
+struct ReplaceAliasWithInfer<'me, 'a, 'tcx> {
+    ecx: &'me mut EvalCtxt<'a, InferCtxt<'tcx>>,
+    param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'_, '_, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.ecx.interner()
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        match *ty.kind() {
+            ty::Alias(..) if !ty.has_escaping_bound_vars() => {
+                let infer_ty = self.ecx.next_ty_infer();
+                let normalizes_to = ty::PredicateKind::AliasRelate(
+                    ty.into(),
+                    infer_ty.into(),
+                    AliasRelationDirection::Equate,
+                );
+                self.ecx.add_goal(
+                    GoalSource::Misc,
+                    Goal::new(self.interner(), self.param_env, normalizes_to),
+                );
+                infer_ty
+            }
+            _ => ty.super_fold_with(self),
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        match ct.kind() {
+            ty::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => {
+                let infer_ct = self.ecx.next_const_infer(ct.ty());
+                let normalizes_to = ty::PredicateKind::AliasRelate(
+                    ct.into(),
+                    infer_ct.into(),
+                    AliasRelationDirection::Equate,
+                );
+                self.ecx.add_goal(
+                    GoalSource::Misc,
+                    Goal::new(self.interner(), self.param_env, normalizes_to),
+                );
+                infer_ct
+            }
+            _ => ct.super_fold_with(self),
+        }
+    }
+
+    fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if predicate.allow_normalization() { predicate.super_fold_with(self) } else { predicate }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 4933080451d..d28cf834032 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -79,7 +79,7 @@ impl<'tcx> ObligationStorage<'tcx> {
             // change.
             self.overflowed.extend(self.pending.extract_if(|o| {
                 let goal = o.clone().into();
-                let result = infcx.evaluate_root_goal(goal, GenerateProofTree::Never).0;
+                let result = infcx.evaluate_root_goal(goal, GenerateProofTree::No).0;
                 match result {
                     Ok((has_changed, _)) => has_changed,
                     _ => false,
@@ -159,7 +159,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
             let mut has_changed = false;
             for obligation in self.obligations.unstalled_for_select() {
                 let goal = obligation.clone().into();
-                let result = infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0;
+                let result = infcx.evaluate_root_goal(goal, GenerateProofTree::No).0;
                 self.inspect_evaluated_obligation(infcx, &obligation, &result);
                 let (changed, certainty) = match result {
                     Ok(result) => result,
@@ -246,7 +246,7 @@ fn fulfillment_error_for_stalled<'tcx>(
     root_obligation: PredicateObligation<'tcx>,
 ) -> FulfillmentError<'tcx> {
     let (code, refine_obligation) = infcx.probe(|_| {
-        match infcx.evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::Never).0 {
+        match infcx.evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No).0 {
             Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
                 (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
             }
@@ -380,7 +380,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
             source: CandidateSource::Impl(impl_def_id),
             result: _,
         } = candidate.kind()
-            && goal.infcx().tcx.has_attr(impl_def_id, sym::do_not_recommend)
+            && goal
+                .infcx()
+                .tcx
+                .has_attrs_with_path(impl_def_id, &[sym::diagnostic, sym::do_not_recommend])
         {
             return ControlFlow::Break(self.obligation.clone());
         }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 737d03f73f0..1f27978e5a6 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -89,10 +89,8 @@ impl<'tcx> NormalizesToTermHack<'tcx> {
 pub struct InspectCandidate<'a, 'tcx> {
     goal: &'a InspectGoal<'a, 'tcx>,
     kind: inspect::ProbeKind<TyCtxt<'tcx>>,
-    nested_goals:
-        Vec<(GoalSource, inspect::CanonicalState<TyCtxt<'tcx>, Goal<'tcx, ty::Predicate<'tcx>>>)>,
+    steps: Vec<&'a inspect::ProbeStep<TyCtxt<'tcx>>>,
     final_state: inspect::CanonicalState<TyCtxt<'tcx>, ()>,
-    impl_args: Option<inspect::CanonicalState<TyCtxt<'tcx>, ty::GenericArgsRef<'tcx>>>,
     result: QueryResult<'tcx>,
     shallow_certainty: Certainty,
 }
@@ -148,7 +146,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
     #[instrument(
         level = "debug",
         skip_all,
-        fields(goal = ?self.goal.goal, nested_goals = ?self.nested_goals)
+        fields(goal = ?self.goal.goal, steps = ?self.steps)
     )]
     pub fn instantiate_nested_goals_and_opt_impl_args(
         &self,
@@ -157,22 +155,34 @@ 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,
+
+        let mut instantiated_goals = vec![];
+        let mut opt_impl_args = None;
+        for step in &self.steps {
+            match **step {
+                inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push((
+                    source,
                     canonical::instantiate_canonical_state(
                         infcx,
                         span,
                         param_env,
                         &mut orig_values,
-                        *goal,
+                        goal,
                     ),
-                )
-            })
-            .collect();
+                )),
+                inspect::ProbeStep::RecordImplArgs { impl_args } => {
+                    opt_impl_args = Some(canonical::instantiate_canonical_state(
+                        infcx,
+                        span,
+                        param_env,
+                        &mut orig_values,
+                        impl_args,
+                    ));
+                }
+                inspect::ProbeStep::MakeCanonicalResponse { .. }
+                | inspect::ProbeStep::NestedProbe(_) => unreachable!(),
+            }
+        }
 
         let () = canonical::instantiate_canonical_state(
             infcx,
@@ -182,17 +192,6 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
             self.final_state,
         );
 
-        let impl_args = self.impl_args.map(|impl_args| {
-            canonical::instantiate_canonical_state(
-                infcx,
-                span,
-                param_env,
-                &mut orig_values,
-                impl_args,
-            )
-            .fold_with(&mut EagerResolver::new(infcx))
-        });
-
         if let Some(term_hack) = self.goal.normalizes_to_term_hack {
             // FIXME: We ignore the expected term of `NormalizesTo` goals
             // when computing the result of its candidates. This is
@@ -200,6 +199,9 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
             let _ = term_hack.constrain(infcx, span, param_env);
         }
 
+        let opt_impl_args =
+            opt_impl_args.map(|impl_args| impl_args.fold_with(&mut EagerResolver::new(infcx)));
+
         let goals = instantiated_goals
             .into_iter()
             .map(|(source, goal)| match goal.predicate.kind().no_bound_vars() {
@@ -249,7 +251,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
             })
             .collect();
 
-        (goals, impl_args)
+        (goals, opt_impl_args)
     }
 
     /// Visit all nested goals of this candidate, rolling back
@@ -279,17 +281,18 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
     fn candidates_recur(
         &'a self,
         candidates: &mut Vec<InspectCandidate<'a, 'tcx>>,
-        nested_goals: &mut Vec<(
-            GoalSource,
-            inspect::CanonicalState<TyCtxt<'tcx>, Goal<'tcx, ty::Predicate<'tcx>>>,
-        )>,
-        probe: &inspect::Probe<TyCtxt<'tcx>>,
+        steps: &mut Vec<&'a inspect::ProbeStep<TyCtxt<'tcx>>>,
+        probe: &'a inspect::Probe<TyCtxt<'tcx>>,
     ) {
         let mut shallow_certainty = None;
-        let mut impl_args = None;
         for step in &probe.steps {
             match *step {
-                inspect::ProbeStep::AddGoal(source, goal) => nested_goals.push((source, goal)),
+                inspect::ProbeStep::AddGoal(..) | inspect::ProbeStep::RecordImplArgs { .. } => {
+                    steps.push(step)
+                }
+                inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
+                    assert_eq!(shallow_certainty.replace(c), None);
+                }
                 inspect::ProbeStep::NestedProbe(ref probe) => {
                     match probe.kind {
                         // These never assemble candidates for the goal we're trying to solve.
@@ -305,19 +308,12 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
                             // 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);
+                            let num_steps = steps.len();
+                            self.candidates_recur(candidates, steps, probe);
+                            steps.truncate(num_steps);
                         }
                     }
                 }
-                inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
-                    assert_eq!(shallow_certainty.replace(c), None);
-                }
-                inspect::ProbeStep::RecordImplArgs { impl_args: i } => {
-                    assert_eq!(impl_args.replace(i), None);
-                }
-                inspect::ProbeStep::EvaluateGoals(_) => (),
             }
         }
 
@@ -339,11 +335,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
                     candidates.push(InspectCandidate {
                         goal: self,
                         kind: probe.kind,
-                        nested_goals: nested_goals.clone(),
+                        steps: steps.clone(),
                         final_state: probe.final_state,
-                        result,
                         shallow_certainty,
-                        impl_args,
+                        result,
                     });
                 }
             }
@@ -359,13 +354,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
                 warn!("unexpected root evaluation: {:?}", self.evaluation_kind);
                 return vec![];
             }
-            inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } => {
-                if let Some(last) = revisions.last() {
-                    last
-                } else {
-                    return vec![];
-                }
-            }
+            inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } => final_revision,
         };
 
         let mut nested_goals = vec![];
@@ -392,9 +381,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
         normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
         source: GoalSource,
     ) -> Self {
-        let inspect::GoalEvaluation { uncanonicalized_goal, kind, evaluation } = root;
-        let inspect::GoalEvaluationKind::Root { orig_values } = kind else { unreachable!() };
-
+        let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, evaluation } = root;
         let result = evaluation.result.and_then(|ok| {
             if let Some(term_hack) = normalizes_to_term_hack {
                 infcx
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
index 803300c5196..84c04900ae4 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
@@ -3,21 +3,16 @@
 //! This code is *a bit* of a mess and can hopefully be
 //! mostly ignored. For a general overview of how it works,
 //! see the comment on [ProofTreeBuilder].
+use std::marker::PhantomData;
 use std::mem;
 
-use rustc_infer::infer::InferCtxt;
+use crate::solve::eval_ctxt::canonical;
+use crate::solve::{self, inspect, GenerateProofTree};
 use rustc_middle::bug;
-use rustc_middle::infer::canonical::CanonicalVarValues;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::ty::{self, TyCtxt};
 use rustc_next_trait_solver::solve::{
     CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult,
 };
-use rustc_session::config::DumpSolverProofTree;
-use rustc_type_ir::Interner;
-
-use crate::solve::eval_ctxt::canonical;
-use crate::solve::{self, inspect, GenerateProofTree};
+use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
 
 /// The core data structure when building proof trees.
 ///
@@ -39,7 +34,11 @@ use crate::solve::{self, inspect, GenerateProofTree};
 /// trees. At the end of trait solving `ProofTreeBuilder::finalize`
 /// is called to recursively convert the whole structure to a
 /// finished proof tree.
-pub(in crate::solve) struct ProofTreeBuilder<I: Interner> {
+pub(in crate::solve) struct ProofTreeBuilder<
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner = <Infcx as InferCtxtLike>::Interner,
+> {
+    _infcx: PhantomData<Infcx>,
     state: Option<Box<DebugSolver<I>>>,
 }
 
@@ -53,7 +52,7 @@ enum DebugSolver<I: Interner> {
     Root,
     GoalEvaluation(WipGoalEvaluation<I>),
     CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<I>),
-    GoalEvaluationStep(WipGoalEvaluationStep<I>),
+    CanonicalGoalEvaluationStep(WipCanonicalGoalEvaluationStep<I>),
 }
 
 impl<I: Interner> From<WipGoalEvaluation<I>> for DebugSolver<I> {
@@ -68,9 +67,9 @@ impl<I: Interner> From<WipCanonicalGoalEvaluation<I>> for DebugSolver<I> {
     }
 }
 
-impl<I: Interner> From<WipGoalEvaluationStep<I>> for DebugSolver<I> {
-    fn from(g: WipGoalEvaluationStep<I>) -> DebugSolver<I> {
-        DebugSolver::GoalEvaluationStep(g)
+impl<I: Interner> From<WipCanonicalGoalEvaluationStep<I>> for DebugSolver<I> {
+    fn from(g: WipCanonicalGoalEvaluationStep<I>) -> DebugSolver<I> {
+        DebugSolver::CanonicalGoalEvaluationStep(g)
     }
 }
 
@@ -78,7 +77,7 @@ impl<I: Interner> From<WipGoalEvaluationStep<I>> for DebugSolver<I> {
 #[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))]
 struct WipGoalEvaluation<I: Interner> {
     pub uncanonicalized_goal: Goal<I, I::Predicate>,
-    pub kind: WipGoalEvaluationKind<I>,
+    pub orig_values: Vec<I::GenericArg>,
     pub evaluation: Option<WipCanonicalGoalEvaluation<I>>,
 }
 
@@ -86,31 +85,19 @@ impl<I: Interner> WipGoalEvaluation<I> {
     fn finalize(self) -> inspect::GoalEvaluation<I> {
         inspect::GoalEvaluation {
             uncanonicalized_goal: self.uncanonicalized_goal,
-            kind: match self.kind {
-                WipGoalEvaluationKind::Root { orig_values } => {
-                    inspect::GoalEvaluationKind::Root { orig_values }
-                }
-                WipGoalEvaluationKind::Nested => inspect::GoalEvaluationKind::Nested,
-            },
+            orig_values: self.orig_values,
             evaluation: self.evaluation.unwrap().finalize(),
         }
     }
 }
 
 #[derive(derivative::Derivative)]
-#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))]
-pub(in crate::solve) enum WipGoalEvaluationKind<I: Interner> {
-    Root { orig_values: Vec<I::GenericArg> },
-    Nested,
-}
-
-#[derive(derivative::Derivative)]
 #[derivative(PartialEq(bound = ""), Eq(bound = ""))]
 pub(in crate::solve) enum WipCanonicalGoalEvaluationKind<I: Interner> {
     Overflow,
     CycleInStack,
     ProvisionalCacheHit,
-    Interned { revisions: I::GoalEvaluationSteps },
+    Interned { final_revision: I::CanonicalGoalEvaluationStepRef },
 }
 
 impl<I: Interner> std::fmt::Debug for WipCanonicalGoalEvaluationKind<I> {
@@ -119,7 +106,9 @@ impl<I: Interner> std::fmt::Debug for WipCanonicalGoalEvaluationKind<I> {
             Self::Overflow => write!(f, "Overflow"),
             Self::CycleInStack => write!(f, "CycleInStack"),
             Self::ProvisionalCacheHit => write!(f, "ProvisionalCacheHit"),
-            Self::Interned { revisions: _ } => f.debug_struct("Interned").finish_non_exhaustive(),
+            Self::Interned { final_revision: _ } => {
+                f.debug_struct("Interned").finish_non_exhaustive()
+            }
         }
     }
 }
@@ -131,13 +120,15 @@ struct WipCanonicalGoalEvaluation<I: Interner> {
     kind: Option<WipCanonicalGoalEvaluationKind<I>>,
     /// Only used for uncached goals. After we finished evaluating
     /// the goal, this is interned and moved into `kind`.
-    revisions: Vec<WipGoalEvaluationStep<I>>,
+    final_revision: Option<WipCanonicalGoalEvaluationStep<I>>,
     result: Option<QueryResult<I>>,
 }
 
 impl<I: Interner> WipCanonicalGoalEvaluation<I> {
     fn finalize(self) -> inspect::CanonicalGoalEvaluation<I> {
-        assert!(self.revisions.is_empty());
+        // We've already interned the final revision in
+        // `fn finalize_canonical_goal_evaluation`.
+        assert!(self.final_revision.is_none());
         let kind = match self.kind.unwrap() {
             WipCanonicalGoalEvaluationKind::Overflow => {
                 inspect::CanonicalGoalEvaluationKind::Overflow
@@ -148,8 +139,8 @@ impl<I: Interner> WipCanonicalGoalEvaluation<I> {
             WipCanonicalGoalEvaluationKind::ProvisionalCacheHit => {
                 inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit
             }
-            WipCanonicalGoalEvaluationKind::Interned { revisions } => {
-                inspect::CanonicalGoalEvaluationKind::Evaluation { revisions }
+            WipCanonicalGoalEvaluationKind::Interned { final_revision } => {
+                inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision }
             }
         };
 
@@ -159,29 +150,7 @@ impl<I: Interner> WipCanonicalGoalEvaluation<I> {
 
 #[derive(derivative::Derivative)]
 #[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))]
-struct WipAddedGoalsEvaluation<I: Interner> {
-    evaluations: Vec<Vec<WipGoalEvaluation<I>>>,
-    result: Option<Result<Certainty, NoSolution>>,
-}
-
-impl<I: Interner> WipAddedGoalsEvaluation<I> {
-    fn finalize(self) -> inspect::AddedGoalsEvaluation<I> {
-        inspect::AddedGoalsEvaluation {
-            evaluations: self
-                .evaluations
-                .into_iter()
-                .map(|evaluations| {
-                    evaluations.into_iter().map(WipGoalEvaluation::finalize).collect()
-                })
-                .collect(),
-            result: self.result.unwrap(),
-        }
-    }
-}
-
-#[derive(derivative::Derivative)]
-#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))]
-struct WipGoalEvaluationStep<I: Interner> {
+struct WipCanonicalGoalEvaluationStep<I: Interner> {
     /// Unlike `EvalCtxt::var_values`, we append a new
     /// generic arg here whenever we create a new inference
     /// variable.
@@ -194,7 +163,7 @@ struct WipGoalEvaluationStep<I: Interner> {
     evaluation: WipProbe<I>,
 }
 
-impl<I: Interner> WipGoalEvaluationStep<I> {
+impl<I: Interner> WipCanonicalGoalEvaluationStep<I> {
     fn current_evaluation_scope(&mut self) -> &mut WipProbe<I> {
         let mut current = &mut self.evaluation;
         for _ in 0..self.probe_depth {
@@ -206,24 +175,16 @@ impl<I: Interner> WipGoalEvaluationStep<I> {
         current
     }
 
-    fn added_goals_evaluation(&mut self) -> &mut WipAddedGoalsEvaluation<I> {
-        let mut current = &mut self.evaluation;
-        loop {
-            match current.steps.last_mut() {
-                Some(WipProbeStep::NestedProbe(p)) => current = p,
-                Some(WipProbeStep::EvaluateGoals(evaluation)) => return evaluation,
-                _ => bug!(),
-            }
-        }
-    }
-
-    fn finalize(self) -> inspect::GoalEvaluationStep<I> {
+    fn finalize(self) -> inspect::CanonicalGoalEvaluationStep<I> {
         let evaluation = self.evaluation.finalize();
         match evaluation.kind {
             inspect::ProbeKind::Root { .. } => (),
             _ => unreachable!("unexpected root evaluation: {evaluation:?}"),
         }
-        inspect::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, evaluation }
+        inspect::CanonicalGoalEvaluationStep {
+            instantiated_goal: self.instantiated_goal,
+            evaluation,
+        }
     }
 }
 
@@ -250,7 +211,6 @@ impl<I: Interner> WipProbe<I> {
 #[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))]
 enum WipProbeStep<I: Interner> {
     AddGoal(GoalSource, inspect::CanonicalState<I, Goal<I, I::Predicate>>),
-    EvaluateGoals(WipAddedGoalsEvaluation<I>),
     NestedProbe(WipProbe<I>),
     MakeCanonicalResponse { shallow_certainty: Certainty },
     RecordImplArgs { impl_args: inspect::CanonicalState<I, I::GenericArgs> },
@@ -260,7 +220,6 @@ impl<I: Interner> WipProbeStep<I> {
     fn finalize(self) -> inspect::ProbeStep<I> {
         match self {
             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::RecordImplArgs { impl_args } => {
                 inspect::ProbeStep::RecordImplArgs { impl_args }
@@ -272,27 +231,36 @@ impl<I: Interner> WipProbeStep<I> {
     }
 }
 
-// FIXME: Genericize this impl.
-impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
-    fn new(state: impl Into<DebugSolver<TyCtxt<'tcx>>>) -> ProofTreeBuilder<TyCtxt<'tcx>> {
-        ProofTreeBuilder { state: Some(Box::new(state.into())) }
+impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
+    fn new(state: impl Into<DebugSolver<I>>) -> ProofTreeBuilder<Infcx> {
+        ProofTreeBuilder { state: Some(Box::new(state.into())), _infcx: PhantomData }
     }
 
-    fn nested<T: Into<DebugSolver<TyCtxt<'tcx>>>>(&self, state: impl FnOnce() -> T) -> Self {
-        ProofTreeBuilder { state: self.state.as_ref().map(|_| Box::new(state().into())) }
+    fn opt_nested<T: Into<DebugSolver<I>>>(&self, state: impl FnOnce() -> Option<T>) -> Self {
+        ProofTreeBuilder {
+            state: self.state.as_ref().and_then(|_| Some(state()?.into())).map(Box::new),
+            _infcx: PhantomData,
+        }
+    }
+
+    fn nested<T: Into<DebugSolver<I>>>(&self, state: impl FnOnce() -> T) -> Self {
+        ProofTreeBuilder {
+            state: self.state.as_ref().map(|_| Box::new(state().into())),
+            _infcx: PhantomData,
+        }
     }
 
-    fn as_mut(&mut self) -> Option<&mut DebugSolver<TyCtxt<'tcx>>> {
+    fn as_mut(&mut self) -> Option<&mut DebugSolver<I>> {
         self.state.as_deref_mut()
     }
 
-    pub fn take_and_enter_probe(&mut self) -> ProofTreeBuilder<TyCtxt<'tcx>> {
-        let mut nested = ProofTreeBuilder { state: self.state.take() };
+    pub fn take_and_enter_probe(&mut self) -> ProofTreeBuilder<Infcx> {
+        let mut nested = ProofTreeBuilder { state: self.state.take(), _infcx: PhantomData };
         nested.enter_probe();
         nested
     }
 
-    pub fn finalize(self) -> Option<inspect::GoalEvaluation<TyCtxt<'tcx>>> {
+    pub fn finalize(self) -> Option<inspect::GoalEvaluation<I>> {
         match *self.state? {
             DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
                 Some(wip_goal_evaluation.finalize())
@@ -301,33 +269,19 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
         }
     }
 
-    pub fn new_maybe_root(
-        tcx: TyCtxt<'tcx>,
-        generate_proof_tree: GenerateProofTree,
-    ) -> ProofTreeBuilder<TyCtxt<'tcx>> {
+    pub fn new_maybe_root(generate_proof_tree: GenerateProofTree) -> ProofTreeBuilder<Infcx> {
         match generate_proof_tree {
-            GenerateProofTree::Never => ProofTreeBuilder::new_noop(),
-            GenerateProofTree::IfEnabled => {
-                let opts = &tcx.sess.opts.unstable_opts;
-                match opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() {
-                    DumpSolverProofTree::Always => ProofTreeBuilder::new_root(),
-                    // `OnError` is handled by reevaluating goals in error
-                    // reporting with `GenerateProofTree::Yes`.
-                    DumpSolverProofTree::OnError | DumpSolverProofTree::Never => {
-                        ProofTreeBuilder::new_noop()
-                    }
-                }
-            }
+            GenerateProofTree::No => ProofTreeBuilder::new_noop(),
             GenerateProofTree::Yes => ProofTreeBuilder::new_root(),
         }
     }
 
-    pub fn new_root() -> ProofTreeBuilder<TyCtxt<'tcx>> {
+    pub fn new_root() -> ProofTreeBuilder<Infcx> {
         ProofTreeBuilder::new(DebugSolver::Root)
     }
 
-    pub fn new_noop() -> ProofTreeBuilder<TyCtxt<'tcx>> {
-        ProofTreeBuilder { state: None }
+    pub fn new_noop() -> ProofTreeBuilder<Infcx> {
+        ProofTreeBuilder { state: None, _infcx: PhantomData }
     }
 
     pub fn is_noop(&self) -> bool {
@@ -336,47 +290,44 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
 
     pub(in crate::solve) fn new_goal_evaluation(
         &mut self,
-        goal: Goal<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
-        orig_values: &[ty::GenericArg<'tcx>],
+        goal: Goal<I, I::Predicate>,
+        orig_values: &[I::GenericArg],
         kind: solve::GoalEvaluationKind,
-    ) -> ProofTreeBuilder<TyCtxt<'tcx>> {
-        self.nested(|| WipGoalEvaluation {
-            uncanonicalized_goal: goal,
-            kind: match kind {
-                solve::GoalEvaluationKind::Root => {
-                    WipGoalEvaluationKind::Root { orig_values: orig_values.to_vec() }
-                }
-                solve::GoalEvaluationKind::Nested => WipGoalEvaluationKind::Nested,
-            },
-            evaluation: None,
+    ) -> ProofTreeBuilder<Infcx> {
+        self.opt_nested(|| match kind {
+            solve::GoalEvaluationKind::Root => Some(WipGoalEvaluation {
+                uncanonicalized_goal: goal,
+                orig_values: orig_values.to_vec(),
+                evaluation: None,
+            }),
+            solve::GoalEvaluationKind::Nested => None,
         })
     }
 
     pub fn new_canonical_goal_evaluation(
         &mut self,
-        goal: CanonicalInput<TyCtxt<'tcx>>,
-    ) -> ProofTreeBuilder<TyCtxt<'tcx>> {
+        goal: CanonicalInput<I>,
+    ) -> ProofTreeBuilder<Infcx> {
         self.nested(|| WipCanonicalGoalEvaluation {
             goal,
             kind: None,
-            revisions: vec![],
+            final_revision: None,
             result: None,
         })
     }
 
-    pub fn finalize_evaluation(
+    pub fn finalize_canonical_goal_evaluation(
         &mut self,
-        tcx: TyCtxt<'tcx>,
-    ) -> Option<&'tcx [inspect::GoalEvaluationStep<TyCtxt<'tcx>>]> {
+        tcx: I,
+    ) -> Option<I::CanonicalGoalEvaluationStepRef> {
         self.as_mut().map(|this| match this {
             DebugSolver::CanonicalGoalEvaluation(evaluation) => {
-                let revisions = mem::take(&mut evaluation.revisions)
-                    .into_iter()
-                    .map(WipGoalEvaluationStep::finalize);
-                let revisions = &*tcx.arena.alloc_from_iter(revisions);
-                let kind = WipCanonicalGoalEvaluationKind::Interned { revisions };
+                let final_revision = mem::take(&mut evaluation.final_revision).unwrap();
+                let final_revision =
+                    tcx.intern_canonical_goal_evaluation_step(final_revision.finalize());
+                let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision };
                 assert_eq!(evaluation.kind.replace(kind), None);
-                revisions
+                final_revision
             }
             _ => unreachable!(),
         })
@@ -384,7 +335,7 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
 
     pub fn canonical_goal_evaluation(
         &mut self,
-        canonical_goal_evaluation: ProofTreeBuilder<TyCtxt<'tcx>>,
+        canonical_goal_evaluation: ProofTreeBuilder<Infcx>,
     ) {
         if let Some(this) = self.as_mut() {
             match (this, *canonical_goal_evaluation.state.unwrap()) {
@@ -400,7 +351,7 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
         }
     }
 
-    pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind<TyCtxt<'tcx>>) {
+    pub fn canonical_goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind<I>) {
         if let Some(this) = self.as_mut() {
             match this {
                 DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
@@ -411,19 +362,13 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
         }
     }
 
-    pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<TyCtxt<'tcx>>) {
+    pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<Infcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, *goal_evaluation.state.unwrap()) {
-                (
-                    DebugSolver::GoalEvaluationStep(state),
-                    DebugSolver::GoalEvaluation(goal_evaluation),
-                ) => state
-                    .added_goals_evaluation()
-                    .evaluations
-                    .last_mut()
-                    .unwrap()
-                    .push(goal_evaluation),
-                (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation,
+            match this {
+                DebugSolver::Root => *this = *goal_evaluation.state.unwrap(),
+                DebugSolver::CanonicalGoalEvaluationStep(_) => {
+                    assert!(goal_evaluation.state.is_none())
+                }
                 _ => unreachable!(),
             }
         }
@@ -431,10 +376,10 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
 
     pub fn new_goal_evaluation_step(
         &mut self,
-        var_values: CanonicalVarValues<'tcx>,
-        instantiated_goal: QueryInput<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
-    ) -> ProofTreeBuilder<TyCtxt<'tcx>> {
-        self.nested(|| WipGoalEvaluationStep {
+        var_values: ty::CanonicalVarValues<I>,
+        instantiated_goal: QueryInput<I, I::Predicate>,
+    ) -> ProofTreeBuilder<Infcx> {
+        self.nested(|| WipCanonicalGoalEvaluationStep {
             var_values: var_values.var_values.to_vec(),
             instantiated_goal,
             evaluation: WipProbe {
@@ -447,24 +392,24 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
         })
     }
 
-    pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<TyCtxt<'tcx>>) {
+    pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<Infcx>) {
         if let Some(this) = self.as_mut() {
             match (this, *goal_evaluation_step.state.unwrap()) {
                 (
                     DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations),
-                    DebugSolver::GoalEvaluationStep(goal_evaluation_step),
+                    DebugSolver::CanonicalGoalEvaluationStep(goal_evaluation_step),
                 ) => {
-                    canonical_goal_evaluations.revisions.push(goal_evaluation_step);
+                    canonical_goal_evaluations.final_revision = Some(goal_evaluation_step);
                 }
                 _ => unreachable!(),
             }
         }
     }
 
-    pub fn add_var_value<T: Into<ty::GenericArg<'tcx>>>(&mut self, arg: T) {
+    pub fn add_var_value<T: Into<I::GenericArg>>(&mut self, arg: T) {
         match self.as_mut() {
             None => {}
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
+            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 state.var_values.push(arg.into());
             }
             Some(s) => bug!("tried to add var values to {s:?}"),
@@ -474,7 +419,7 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
     pub fn enter_probe(&mut self) {
         match self.as_mut() {
             None => {}
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
+            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 let initial_num_var_values = state.var_values.len();
                 state.current_evaluation_scope().steps.push(WipProbeStep::NestedProbe(WipProbe {
                     initial_num_var_values,
@@ -488,10 +433,10 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
         }
     }
 
-    pub fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<TyCtxt<'tcx>>) {
+    pub fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<I>) {
         match self.as_mut() {
             None => {}
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
+            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 let prev = state.current_evaluation_scope().kind.replace(probe_kind);
                 assert_eq!(prev, None);
             }
@@ -499,14 +444,10 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
         }
     }
 
-    pub fn probe_final_state(
-        &mut self,
-        infcx: &InferCtxt<'tcx>,
-        max_input_universe: ty::UniverseIndex,
-    ) {
+    pub fn probe_final_state(&mut self, infcx: &Infcx, max_input_universe: ty::UniverseIndex) {
         match self.as_mut() {
             None => {}
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
+            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 let final_state = canonical::make_canonical_state(
                     infcx,
                     &state.var_values,
@@ -522,28 +463,28 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
 
     pub fn add_normalizes_to_goal(
         &mut self,
-        infcx: &InferCtxt<'tcx>,
+        infcx: &Infcx,
         max_input_universe: ty::UniverseIndex,
-        goal: Goal<TyCtxt<'tcx>, ty::NormalizesTo<'tcx>>,
+        goal: Goal<I, ty::NormalizesTo<I>>,
     ) {
         self.add_goal(
             infcx,
             max_input_universe,
             GoalSource::Misc,
-            goal.with(infcx.tcx, goal.predicate),
+            goal.with(infcx.interner(), goal.predicate),
         );
     }
 
     pub fn add_goal(
         &mut self,
-        infcx: &InferCtxt<'tcx>,
+        infcx: &Infcx,
         max_input_universe: ty::UniverseIndex,
         source: GoalSource,
-        goal: Goal<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
+        goal: Goal<I, I::Predicate>,
     ) {
         match self.as_mut() {
             None => {}
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
+            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 let goal = canonical::make_canonical_state(
                     infcx,
                     &state.var_values,
@@ -558,12 +499,12 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
 
     pub(crate) fn record_impl_args(
         &mut self,
-        infcx: &InferCtxt<'tcx>,
+        infcx: &Infcx,
         max_input_universe: ty::UniverseIndex,
-        impl_args: ty::GenericArgsRef<'tcx>,
+        impl_args: I::GenericArgs,
     ) {
         match self.as_mut() {
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
+            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 let impl_args = canonical::make_canonical_state(
                     infcx,
                     &state.var_values,
@@ -582,7 +523,7 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
 
     pub fn make_canonical_response(&mut self, shallow_certainty: Certainty) {
         match self.as_mut() {
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
+            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 state
                     .current_evaluation_scope()
                     .steps
@@ -593,10 +534,10 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
         }
     }
 
-    pub fn finish_probe(mut self) -> ProofTreeBuilder<TyCtxt<'tcx>> {
+    pub fn finish_probe(mut self) -> ProofTreeBuilder<Infcx> {
         match self.as_mut() {
             None => {}
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
+            Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 assert_ne!(state.probe_depth, 0);
                 let num_var_values = state.current_evaluation_scope().initial_num_var_values;
                 state.var_values.truncate(num_var_values);
@@ -608,46 +549,13 @@ impl<'tcx> ProofTreeBuilder<TyCtxt<'tcx>> {
         self
     }
 
-    pub fn start_evaluate_added_goals(&mut self) {
-        match self.as_mut() {
-            None => {}
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
-                state.current_evaluation_scope().steps.push(WipProbeStep::EvaluateGoals(
-                    WipAddedGoalsEvaluation { evaluations: vec![], result: None },
-                ));
-            }
-            _ => bug!(),
-        }
-    }
-
-    pub fn start_evaluate_added_goals_step(&mut self) {
-        match self.as_mut() {
-            None => {}
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
-                state.added_goals_evaluation().evaluations.push(vec![]);
-            }
-            _ => bug!(),
-        }
-    }
-
-    pub fn evaluate_added_goals_result(&mut self, result: Result<Certainty, NoSolution>) {
-        match self.as_mut() {
-            None => {}
-            Some(DebugSolver::GoalEvaluationStep(state)) => {
-                let prev = state.added_goals_evaluation().result.replace(result);
-                assert_eq!(prev, None);
-            }
-            _ => bug!(),
-        }
-    }
-
-    pub fn query_result(&mut self, result: QueryResult<TyCtxt<'tcx>>) {
+    pub fn query_result(&mut self, result: QueryResult<I>) {
         if let Some(this) = self.as_mut() {
             match this {
                 DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
                     assert_eq!(canonical_goal_evaluation.result.replace(result), None);
                 }
-                DebugSolver::GoalEvaluationStep(evaluation_step) => {
+                DebugSolver::CanonicalGoalEvaluationStep(evaluation_step) => {
                     assert_eq!(
                         evaluation_step
                             .evaluation
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 60722d3618f..a432090f78c 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -133,7 +133,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     }
 
     fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
-        if self.tcx().check_is_object_safe(trait_def_id) {
+        if self.interner().check_is_object_safe(trait_def_id) {
             self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         } else {
             Err(NoSolution)
@@ -274,7 +274,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         if let ty::Alias(..) = ty.kind() {
             let normalized_ty = self.next_ty_infer();
             let alias_relate_goal = Goal::new(
-                self.tcx(),
+                self.interner(),
                 param_env,
                 ty::PredicateKind::AliasRelate(
                     ty.into(),
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
index c9621e705e5..362c4072278 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
@@ -12,7 +12,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         if let Some(normalized_const) = self.try_const_eval_resolve(
             goal.param_env,
             ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args),
-            self.tcx()
+            self.interner()
                 .type_of(goal.predicate.alias.def_id)
                 .no_bound_vars()
                 .expect("const ty should not rely on other generics"),
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
index 2146a2c2f08..41b2b9cd4d2 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
@@ -15,7 +15,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         &mut self,
         goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
     ) -> QueryResult<'tcx> {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let inherent = goal.predicate.alias.expect_ty(tcx);
 
         let impl_def_id = tcx.parent(inherent.def_id);
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index 7ef8373663b..7fd2a3801cc 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -54,7 +54,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         &mut self,
         goal: Goal<'tcx, NormalizesTo<'tcx>>,
     ) -> QueryResult<'tcx> {
-        match goal.predicate.alias.kind(self.tcx()) {
+        match goal.predicate.alias.kind(self.interner()) {
             ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
                 let candidates = self.assemble_and_evaluate_candidates(goal);
                 self.merge_candidates(candidates)
@@ -107,7 +107,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     ) -> Result<Candidate<'tcx>, NoSolution> {
         if let Some(projection_pred) = assumption.as_projection_clause() {
             if projection_pred.projection_def_id() == goal.predicate.def_id() {
-                let tcx = ecx.tcx();
+                let tcx = ecx.interner();
                 ecx.probe_trait_candidate(source).enter(|ecx| {
                     let assumption_projection_pred =
                         ecx.instantiate_binder_with_infer(projection_pred);
@@ -142,7 +142,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         goal: Goal<'tcx, NormalizesTo<'tcx>>,
         impl_def_id: DefId,
     ) -> Result<Candidate<'tcx>, NoSolution> {
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
 
         let goal_trait_ref = goal.predicate.alias.trait_ref(tcx);
         let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
@@ -290,8 +290,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
     ) -> Result<Candidate<'tcx>, NoSolution> {
-        ecx.tcx().dcx().span_delayed_bug(
-            ecx.tcx().def_span(goal.predicate.def_id()),
+        ecx.interner().dcx().span_delayed_bug(
+            ecx.interner().def_span(goal.predicate.def_id()),
             "associated types not allowed on auto traits",
         );
         Err(NoSolution)
@@ -337,7 +337,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         goal: Goal<'tcx, Self>,
         goal_kind: ty::ClosureKind,
     ) -> Result<Candidate<'tcx>, NoSolution> {
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         let tupled_inputs_and_output =
             match structural_traits::extract_tupled_inputs_and_output_from_callable(
                 tcx,
@@ -380,7 +380,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         goal: Goal<'tcx, Self>,
         goal_kind: ty::ClosureKind,
     ) -> Result<Candidate<'tcx>, NoSolution> {
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
 
         let env_region = match goal_kind {
             ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2),
@@ -493,7 +493,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         }
 
         let upvars_ty = ty::CoroutineClosureSignature::tupled_upvars_by_closure_kind(
-            ecx.tcx(),
+            ecx.interner(),
             goal_kind,
             tupled_inputs_ty.expect_ty(),
             tupled_upvars_ty.expect_ty(),
@@ -518,7 +518,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
     ) -> Result<Candidate<'tcx>, NoSolution> {
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
         assert_eq!(metadata_def_id, goal.predicate.def_id());
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
@@ -606,7 +606,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         };
 
         // Coroutines are not futures unless they come from `async` desugaring
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         if !tcx.coroutine_is_async(def_id) {
             return Err(NoSolution);
         }
@@ -618,7 +618,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
             goal,
             ty::ProjectionPredicate {
-                projection_term: ty::AliasTerm::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]),
+                projection_term: ty::AliasTerm::new(
+                    ecx.interner(),
+                    goal.predicate.def_id(),
+                    [self_ty],
+                ),
                 term,
             }
             .upcast(tcx),
@@ -638,7 +642,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         };
 
         // Coroutines are not Iterators unless they come from `gen` desugaring
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         if !tcx.coroutine_is_gen(def_id) {
             return Err(NoSolution);
         }
@@ -650,7 +654,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
             goal,
             ty::ProjectionPredicate {
-                projection_term: ty::AliasTerm::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]),
+                projection_term: ty::AliasTerm::new(
+                    ecx.interner(),
+                    goal.predicate.def_id(),
+                    [self_ty],
+                ),
                 term,
             }
             .upcast(tcx),
@@ -677,7 +685,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         };
 
         // Coroutines are not AsyncIterators unless they come from `gen` desugaring
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         if !tcx.coroutine_is_async_gen(def_id) {
             return Err(NoSolution);
         }
@@ -713,7 +721,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         };
 
         // `async`-desugared coroutines do not implement the coroutine trait
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         if !tcx.is_general_coroutine(def_id) {
             return Err(NoSolution);
         }
@@ -735,7 +743,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             goal,
             ty::ProjectionPredicate {
                 projection_term: ty::AliasTerm::new(
-                    ecx.tcx(),
+                    ecx.interner(),
                     goal.predicate.def_id(),
                     [self_ty, coroutine.resume_ty()],
                 ),
@@ -784,7 +792,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             | ty::Slice(_)
             | ty::Dynamic(_, _, _)
             | ty::Tuple(_)
-            | ty::Error(_) => self_ty.discriminant_ty(ecx.tcx()),
+            | ty::Error(_) => self_ty.discriminant_ty(ecx.interner()),
 
             // We do not call `Ty::discriminant_ty` on alias, param, or placeholder
             // types, which return `<self_ty as DiscriminantKind>::Discriminant`
@@ -831,7 +839,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             | ty::Str
             | ty::Slice(_)
             | ty::Tuple(_)
-            | ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env),
+            | ty::Error(_) => self_ty.async_destructor_ty(ecx.interner(), goal.param_env),
 
             // We do not call `Ty::async_destructor_ty` on alias, param, or placeholder
             // types, which return `<self_ty as AsyncDestruct>::AsyncDestructor`
@@ -887,8 +895,9 @@ fn fetch_eligible_assoc_item_def<'tcx>(
     trait_assoc_def_id: DefId,
     impl_def_id: DefId,
 ) -> Result<Option<LeafDef>, NoSolution> {
-    let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id)
-        .map_err(|ErrorGuaranteed { .. }| NoSolution)?;
+    let node_item =
+        specialization_graph::assoc_def(ecx.interner(), impl_def_id, trait_assoc_def_id)
+            .map_err(|ErrorGuaranteed { .. }| NoSolution)?;
 
     let eligible = if node_item.is_final() {
         // Non-specializable items are always projectable.
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs
index 3b83d347276..67ec2f3be48 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs
@@ -15,7 +15,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         &mut self,
         goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
     ) -> QueryResult<'tcx> {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let opaque_ty = goal.predicate.alias;
         let expected = goal.predicate.term.ty().expect("no such thing as an opaque const");
 
@@ -31,7 +31,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                     return Err(NoSolution);
                 }
                 // FIXME: This may have issues when the args contain aliases...
-                match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.args) {
+                match self.interner().uses_unique_placeholders_ignoring_regions(opaque_ty.args) {
                     Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
                         return self.evaluate_added_goals_and_make_canonical_response(
                             Certainty::AMBIGUOUS,
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
index 109a9e9671f..5442b9ccffc 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
@@ -14,7 +14,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         &mut self,
         goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
     ) -> QueryResult<'tcx> {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let weak_ty = goal.predicate.alias;
 
         // Check where clauses
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 8fa78e49dc6..cae73cc2d07 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -11,7 +11,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         &mut self,
         goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
     ) -> QueryResult<'tcx> {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let projection_term = goal.predicate.projection_term.to_term(tcx);
         let goal = goal.with(
             tcx,
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs
index bcd210f789b..f1b8bf4676e 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs
@@ -3,6 +3,7 @@ use std::mem;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_index::Idx;
 use rustc_index::IndexVec;
+use rustc_infer::infer::InferCtxt;
 use rustc_middle::dep_graph::dep_kinds;
 use rustc_middle::traits::solve::CacheData;
 use rustc_middle::traits::solve::EvaluationCache;
@@ -261,10 +262,10 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
         &mut self,
         tcx: TyCtxt<'tcx>,
         input: CanonicalInput<TyCtxt<'tcx>>,
-        inspect: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
+        inspect: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
         mut prove_goal: impl FnMut(
             &mut Self,
-            &mut ProofTreeBuilder<TyCtxt<'tcx>>,
+            &mut ProofTreeBuilder<InferCtxt<'tcx>>,
         ) -> QueryResult<TyCtxt<'tcx>>,
     ) -> QueryResult<TyCtxt<'tcx>> {
         self.check_invariants();
@@ -274,7 +275,8 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
                 last.encountered_overflow = true;
             }
 
-            inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
+            inspect
+                .canonical_goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
             return Self::response_no_constraints(tcx, input, Certainty::overflow(true));
         };
 
@@ -302,8 +304,9 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
             // We have a nested goal which is already in the provisional cache, use
             // its result. We do not provide any usage kind as that should have been
             // already set correctly while computing the cache entry.
-            inspect
-                .goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::ProvisionalCacheHit);
+            inspect.canonical_goal_evaluation_kind(
+                inspect::WipCanonicalGoalEvaluationKind::ProvisionalCacheHit,
+            );
             Self::tag_cycle_participants(&mut self.stack, HasBeenUsed::empty(), entry.head);
             return entry.result;
         } else if let Some(stack_depth) = cache_entry.stack_depth {
@@ -314,7 +317,9 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
             //
             // Finally we can return either the provisional response or the initial response
             // in case we're in the first fixpoint iteration for this goal.
-            inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CycleInStack);
+            inspect.canonical_goal_evaluation_kind(
+                inspect::WipCanonicalGoalEvaluationKind::CycleInStack,
+            );
             let is_coinductive_cycle = Self::stack_coinductive_from(tcx, &self.stack, stack_depth);
             let usage_kind = if is_coinductive_cycle {
                 HasBeenUsed::COINDUCTIVE_CYCLE
@@ -371,7 +376,7 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
                 (current_entry, result)
             });
 
-        let proof_tree = inspect.finalize_evaluation(tcx);
+        let proof_tree = inspect.finalize_canonical_goal_evaluation(tcx);
 
         // We're now done with this goal. In case this goal is involved in a larger cycle
         // do not remove it from the provisional cache and update its provisional result.
@@ -422,7 +427,7 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
         tcx: TyCtxt<'tcx>,
         input: CanonicalInput<TyCtxt<'tcx>>,
         available_depth: Limit,
-        inspect: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
+        inspect: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
     ) -> Option<QueryResult<TyCtxt<'tcx>>> {
         let CacheData { result, proof_tree, additional_depth, encountered_overflow } = self
             .global_cache(tcx)
@@ -433,9 +438,9 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
         // the goal. We simply overwrite the existing entry once we're done,
         // caching the proof tree.
         if !inspect.is_noop() {
-            if let Some(revisions) = proof_tree {
-                let kind = inspect::WipCanonicalGoalEvaluationKind::Interned { revisions };
-                inspect.goal_evaluation_kind(kind);
+            if let Some(final_revision) = proof_tree {
+                let kind = inspect::WipCanonicalGoalEvaluationKind::Interned { final_revision };
+                inspect.canonical_goal_evaluation_kind(kind);
             } else {
                 return None;
             }
@@ -469,11 +474,11 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
         &mut self,
         tcx: TyCtxt<'tcx>,
         input: CanonicalInput<TyCtxt<'tcx>>,
-        inspect: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
+        inspect: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
         prove_goal: &mut F,
     ) -> StepResult<TyCtxt<'tcx>>
     where
-        F: FnMut(&mut Self, &mut ProofTreeBuilder<TyCtxt<'tcx>>) -> QueryResult<TyCtxt<'tcx>>,
+        F: FnMut(&mut Self, &mut ProofTreeBuilder<InferCtxt<'tcx>>) -> QueryResult<TyCtxt<'tcx>>,
     {
         let result = prove_goal(self, inspect);
         let stack_entry = self.pop_stack();
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index e59eef22f41..67dd3fa85fa 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -42,7 +42,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         goal: Goal<'tcx, TraitPredicate<'tcx>>,
         impl_def_id: DefId,
     ) -> Result<Candidate<'tcx>, NoSolution> {
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
 
         let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
         let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
@@ -181,7 +181,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return Err(NoSolution);
         }
 
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
 
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             let nested_obligations = tcx
@@ -235,7 +235,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         }
 
         // The regions of a type don't affect the size of the type
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         // We should erase regions from both the param-env and type, since both
         // may have infer regions. Specifically, after canonicalizing and instantiating,
         // early bound regions turn into region vars in both the new and old solver.
@@ -296,7 +296,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return Err(NoSolution);
         }
 
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         let tupled_inputs_and_output =
             match structural_traits::extract_tupled_inputs_and_output_from_callable(
                 tcx,
@@ -337,7 +337,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return Err(NoSolution);
         }
 
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         let (tupled_inputs_and_output_and_coroutine, nested_preds) =
             structural_traits::extract_tupled_inputs_and_output_from_async_callable(
                 tcx,
@@ -447,7 +447,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         };
 
         // Coroutines are not futures unless they come from `async` desugaring
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         if !tcx.coroutine_is_async(def_id) {
             return Err(NoSolution);
         }
@@ -473,7 +473,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         };
 
         // Coroutines are not iterators unless they come from `gen` desugaring
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         if !tcx.coroutine_is_gen(def_id) {
             return Err(NoSolution);
         }
@@ -499,7 +499,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         };
 
         // Coroutines are not iterators unless they come from `gen` desugaring
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         if !tcx.coroutine_is_gen(def_id) {
             return Err(NoSolution);
         }
@@ -523,7 +523,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         };
 
         // Coroutines are not iterators unless they come from `gen` desugaring
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         if !tcx.coroutine_is_async_gen(def_id) {
             return Err(NoSolution);
         }
@@ -550,7 +550,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         };
 
         // `async`-desugared coroutines do not implement the coroutine trait
-        let tcx = ecx.tcx();
+        let tcx = ecx.interner();
         if !tcx.is_general_coroutine(def_id) {
             return Err(NoSolution);
         }
@@ -625,10 +625,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
         // Erase regions because we compute layouts in `rustc_transmute`,
         // which will ICE for region vars.
-        let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args);
+        let args = ecx.interner().erase_regions(goal.predicate.trait_ref.args);
 
         let Some(assume) =
-            rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(2))
+            rustc_transmute::Assume::from_const(ecx.interner(), goal.param_env, args.const_at(2))
         else {
             return Err(NoSolution);
         };
@@ -675,7 +675,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                 return vec![];
             };
 
-            let goal = goal.with(ecx.tcx(), (a_ty, b_ty));
+            let goal = goal.with(ecx.interner(), (a_ty, b_ty));
             match (a_ty.kind(), b_ty.kind()) {
                 (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"),
 
@@ -741,7 +741,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
         b_region: ty::Region<'tcx>,
     ) -> Vec<Candidate<'tcx>> {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let Goal { predicate: (a_ty, _b_ty), .. } = goal;
 
         let mut responses = vec![];
@@ -787,7 +787,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
         b_region: ty::Region<'tcx>,
     ) -> Result<Candidate<'tcx>, NoSolution> {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let Goal { predicate: (a_ty, _), .. } = goal;
 
         // Can only unsize to an object-safe trait.
@@ -837,8 +837,8 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         let a_auto_traits: FxIndexSet<DefId> = a_data
             .auto_traits()
             .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
-                supertrait_def_ids(self.tcx(), principal_def_id)
-                    .filter(|def_id| self.tcx().trait_is_auto(*def_id))
+                supertrait_def_ids(self.interner(), principal_def_id)
+                    .filter(|def_id| self.interner().trait_is_auto(*def_id))
             }))
             .collect();
 
@@ -907,7 +907,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             ecx.add_goal(
                 GoalSource::ImplWhereBound,
                 Goal::new(
-                    ecx.tcx(),
+                    ecx.interner(),
                     param_env,
                     ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
                 ),
@@ -956,7 +956,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         a_args: ty::GenericArgsRef<'tcx>,
         b_args: ty::GenericArgsRef<'tcx>,
     ) -> Result<Candidate<'tcx>, NoSolution> {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
         let unsizing_params = tcx.unsizing_params_for_adt(def.did());
@@ -1017,7 +1017,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         a_tys: &'tcx ty::List<Ty<'tcx>>,
         b_tys: &'tcx ty::List<Ty<'tcx>>,
     ) -> Result<Candidate<'tcx>, NoSolution> {
-        let tcx = self.tcx();
+        let tcx = self.interner();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
         let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
@@ -1077,9 +1077,9 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             // takes precedence over the structural auto trait candidate being
             // assembled.
             ty::Coroutine(def_id, _)
-                if Some(goal.predicate.def_id()) == self.tcx().lang_items().unpin_trait() =>
+                if Some(goal.predicate.def_id()) == self.interner().lang_items().unpin_trait() =>
             {
-                match self.tcx().coroutine_movability(def_id) {
+                match self.interner().coroutine_movability(def_id) {
                     Movability::Static => Some(Err(NoSolution)),
                     Movability::Movable => Some(
                         self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
@@ -1124,7 +1124,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             | ty::Tuple(_)
             | ty::Adt(_, _) => {
                 let mut disqualifying_impl = None;
-                self.tcx().for_each_relevant_impl(
+                self.interner().for_each_relevant_impl(
                     goal.predicate.def_id(),
                     goal.predicate.self_ty(),
                     |impl_def_id| {
@@ -1164,7 +1164,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                     .into_iter()
                     .map(|ty| {
                         ecx.enter_forall(ty, |ty| {
-                            goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty))
+                            goal.with(
+                                ecx.interner(),
+                                goal.predicate.with_self_ty(ecx.interner(), ty),
+                            )
                         })
                     })
                     .collect::<Vec<_>>(),
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index e50edfdc656..633d488f7be 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -7,15 +7,11 @@ pub mod suggestions;
 mod type_err_ctxt_ext;
 
 use super::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
-use crate::infer::InferCtxt;
-use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
-use rustc_middle::traits::solve::Goal;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
-use std::io::Write;
 use std::ops::ControlFlow;
 
 pub use self::infer_ctxt_ext::*;
@@ -184,16 +180,3 @@ pub enum DefIdOrName {
     DefId(DefId),
     Name(&'static str),
 }
-
-pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &InferCtxt<'tcx>) {
-    infcx.probe(|_| {
-        let goal = Goal { predicate: o.predicate, param_env: o.param_env };
-        let tree = infcx
-            .evaluate_root_goal(goal, GenerateProofTree::Yes)
-            .1
-            .expect("proof tree should have been generated");
-        let mut lock = std::io::stdout().lock();
-        let _ = lock.write_fmt(format_args!("{tree:?}\n"));
-        let _ = lock.flush();
-    });
-}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index f9b6b281f92..6c56ebb62ae 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -47,7 +47,8 @@ use crate::infer::InferCtxtExt as _;
 use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_middle::ty::print::{
-    with_forced_trimmed_paths, with_no_trimmed_paths, PrintTraitPredicateExt as _,
+    with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _,
+    PrintTraitPredicateExt as _,
 };
 
 use itertools::EitherOrBoth;
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 20dfb5e6642..46953a61296 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -46,7 +46,6 @@ use rustc_middle::ty::{
     TypeVisitableExt, Upcast,
 };
 use rustc_middle::{bug, span_bug};
-use rustc_session::config::DumpSolverProofTree;
 use rustc_session::Limit;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::symbol::sym;
@@ -56,8 +55,8 @@ use std::fmt;
 use std::iter;
 
 use super::{
-    dump_proof_tree, ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam,
-    GetSafeTransmuteErrorAndReason, HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst,
+    ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, GetSafeTransmuteErrorAndReason,
+    HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst,
 };
 
 pub use rustc_infer::traits::error_reporting::*;
@@ -369,13 +368,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         error: &SelectionError<'tcx>,
     ) -> ErrorGuaranteed {
         let tcx = self.tcx;
-
-        if tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default()
-            == DumpSolverProofTree::OnError
-        {
-            dump_proof_tree(root_obligation, self.infcx);
-        }
-
         let mut span = obligation.cause.span;
 
         let mut err = match *error {
@@ -1012,7 +1004,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let mut base_cause = obligation.cause.code().clone();
         loop {
             if let ObligationCauseCode::ImplDerived(ref c) = base_cause {
-                if self.tcx.has_attr(c.impl_or_alias_def_id, sym::do_not_recommend) {
+                if self.tcx.has_attrs_with_path(
+                    c.impl_or_alias_def_id,
+                    &[sym::diagnostic, sym::do_not_recommend],
+                ) {
                     let code = (*c.derived.parent_code).clone();
                     obligation.cause.map_code(|_| code);
                     obligation.predicate = c.derived.parent_trait_pred.upcast(self.tcx);
@@ -1526,12 +1521,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     #[instrument(skip(self), level = "debug")]
     fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed {
-        if self.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default()
-            == DumpSolverProofTree::OnError
-        {
-            dump_proof_tree(&error.root_obligation, self.infcx);
-        }
-
         match error.code {
             FulfillmentErrorCode::Select(ref selection_error) => self.report_selection_error(
                 error.obligation.clone(),
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 07fcf109fda..629f6f394af 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -566,10 +566,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                             {
                                 if let Ok(new_obligations) = infcx
                                     .at(&obligation.cause, obligation.param_env)
-                                    .trace(c1, c2)
                                     // Can define opaque types as this is only reachable with
                                     // `generic_const_exprs`
-                                    .eq(DefineOpaqueTypes::Yes, a.args, b.args)
+                                    .eq(
+                                        DefineOpaqueTypes::Yes,
+                                        ty::AliasTerm::from(a),
+                                        ty::AliasTerm::from(b),
+                                    )
                                 {
                                     return ProcessResult::Changed(mk_pending(
                                         new_obligations.into_obligations(),
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index da2b004761f..a1094d98276 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -1,17 +1,14 @@
 //! Miscellaneous type-system utilities that are too small to deserve their own modules.
 
 use crate::regions::InferCtxtRegionExt;
-use crate::traits::{self, ObligationCause, ObligationCtxt};
+use crate::traits::{self, ObligationCause};
 
 use hir::LangItem;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir as hir;
-use rustc_infer::infer::canonical::Canonical;
 use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
-use rustc_infer::traits::query::NoSolution;
 use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt};
-use rustc_span::DUMMY_SP;
 
 use super::outlives_bounds::InferCtxtExt;
 
@@ -207,19 +204,3 @@ pub fn all_fields_implement_trait<'tcx>(
 
     if infringing.is_empty() { Ok(()) } else { Err(infringing) }
 }
-
-pub fn check_tys_might_be_eq<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    canonical: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>>,
-) -> Result<(), NoSolution> {
-    let (infcx, key, _) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical);
-    let (param_env, (ty_a, ty_b)) = key.into_parts();
-    let ocx = ObligationCtxt::new(&infcx);
-
-    let result = ocx.eq(&ObligationCause::dummy(), param_env, ty_a, ty_b);
-    // use `select_where_possible` instead of `select_all_or_error` so that
-    // we don't get errors from obligations being ambiguous.
-    let errors = ocx.select_where_possible();
-
-    if errors.len() > 0 || result.is_err() { Err(NoSolution) } else { Ok(()) }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 204bb487c86..ab4d06f2660 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -185,6 +185,7 @@ fn do_normalize_predicates<'tcx>(
     predicates: Vec<ty::Clause<'tcx>>,
 ) -> Result<Vec<ty::Clause<'tcx>>, ErrorGuaranteed> {
     let span = cause.span;
+
     // FIXME. We should really... do something with these region
     // obligations. But this call just continues the older
     // behavior (i.e., doesn't cause any new bugs), and it would
@@ -551,7 +552,6 @@ pub fn provide(providers: &mut Providers) {
         specialization_graph_of: specialize::specialization_graph_provider,
         specializes: specialize::specializes,
         instantiate_and_check_impossible_predicates,
-        check_tys_might_be_eq: misc::check_tys_might_be_eq,
         is_impossible_associated_item,
         ..*providers
     };
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 8ce1271fc17..f4051561dae 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -13,7 +13,7 @@ use super::elaborate;
 use crate::infer::TyCtxtInferExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::{self, Obligation, ObligationCause};
-use rustc_errors::{DelayDm, FatalError, MultiSpan};
+use rustc_errors::{FatalError, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::query::Providers;
@@ -162,41 +162,36 @@ fn lint_object_unsafe_trait(
 ) {
     // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id.
     // It's also hard to get a use site span, so we use the method definition span.
-    tcx.node_span_lint(
-        WHERE_CLAUSES_OBJECT_SAFETY,
-        hir::CRATE_HIR_ID,
-        span,
-        DelayDm(|| format!("the trait `{}` cannot be made into an object", tcx.def_path_str(trait_def_id))),
-        |err| {
-            let node = tcx.hir().get_if_local(trait_def_id);
-            let mut spans = MultiSpan::from_span(span);
-            if let Some(hir::Node::Item(item)) = node {
-                spans.push_span_label(
-                    item.ident.span,
-                    "this trait cannot be made into an object...",
-                );
-                spans.push_span_label(span, format!("...because {}", violation.error_msg()));
-            } else {
-                spans.push_span_label(
-                    span,
-                    format!(
-                        "the trait cannot be made into an object because {}",
-                        violation.error_msg()
-                    ),
-                );
-            };
-            err.span_note(
-                spans,
-                "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \
+    tcx.node_span_lint(WHERE_CLAUSES_OBJECT_SAFETY, hir::CRATE_HIR_ID, span, |err| {
+        err.primary_message(format!(
+            "the trait `{}` cannot be made into an object",
+            tcx.def_path_str(trait_def_id)
+        ));
+        let node = tcx.hir().get_if_local(trait_def_id);
+        let mut spans = MultiSpan::from_span(span);
+        if let Some(hir::Node::Item(item)) = node {
+            spans.push_span_label(item.ident.span, "this trait cannot be made into an object...");
+            spans.push_span_label(span, format!("...because {}", violation.error_msg()));
+        } else {
+            spans.push_span_label(
+                span,
+                format!(
+                    "the trait cannot be made into an object because {}",
+                    violation.error_msg()
+                ),
+            );
+        };
+        err.span_note(
+            spans,
+            "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \
                 call to be resolvable dynamically; for more information visit \
                 <https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
-            );
-            if node.is_some() {
-                // Only provide the help if its a local trait, otherwise it's not
-                violation.solution().add_to(err);
-            }
-        },
-    );
+        );
+        if node.is_some() {
+            // Only provide the help if its a local trait, otherwise it's not
+            violation.solution().add_to(err);
+        }
+    });
 }
 
 fn sized_trait_bound_spans<'tcx>(
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 77ac4be35ea..87c8b1cda50 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -2097,7 +2097,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node);
     let ty = tcx.type_of(assoc_ty.item.def_id);
     let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
-    let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
+    let term: ty::EarlyBinder<'tcx, ty::Term<'tcx>> = if is_const {
         let did = assoc_ty.item.def_id;
         let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did);
         let uv = ty::UnevaluatedConst::new(did, identity_args);
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index f7e84a46639..00cc77e71e7 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -162,8 +162,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
     let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
     let mut wf_args = vec![ty.into()];
 
-    let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> =
-        vec![];
+    let mut outlives_bounds: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> = vec![];
 
     while let Some(arg) = wf_args.pop() {
         if !checked_wf_args.insert(arg) {
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index b9e853a0678..fd7c47ad6fb 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -418,20 +418,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     // Ambiguity if upvars haven't been constrained yet
                     && !args.tupled_upvars_ty().is_ty_var()
                 {
-                    let no_borrows = match args.tupled_upvars_ty().kind() {
-                        ty::Tuple(tys) => tys.is_empty(),
-                        ty::Error(_) => false,
-                        _ => bug!("tuple_fields called on non-tuple"),
-                    };
                     // A coroutine-closure implements `FnOnce` *always*, since it may
                     // always be called once. It additionally implements `Fn`/`FnMut`
-                    // only if it has no upvars (therefore no borrows from the closure
-                    // that would need to be represented with a lifetime) and if the
-                    // closure kind permits it.
-                    // FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
-                    // if it takes all of its upvars by copy, and none by ref. This would
-                    // require us to record a bit more information during upvar analysis.
-                    if no_borrows && closure_kind.extends(kind) {
+                    // only if it has no upvars referencing the closure-env lifetime,
+                    // and if the closure kind permits it.
+                    if closure_kind.extends(kind) && !args.has_self_borrows() {
                         candidates.vec.push(ClosureCandidate { is_const });
                     } else if kind == ty::ClosureKind::FnOnce {
                         candidates.vec.push(ClosureCandidate { is_const });
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 4a94643d908..7aec4e1987e 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -910,10 +910,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                                 if let Ok(InferOk { obligations, value: () }) = self
                                     .infcx
                                     .at(&obligation.cause, obligation.param_env)
-                                    .trace(c1, c2)
                                     // Can define opaque types as this is only reachable with
                                     // `generic_const_exprs`
-                                    .eq(DefineOpaqueTypes::Yes, a.args, b.args)
+                                    .eq(
+                                        DefineOpaqueTypes::Yes,
+                                        ty::AliasTerm::from(a),
+                                        ty::AliasTerm::from(b),
+                                    )
                                 {
                                     return self.evaluate_predicates_recursively(
                                         previous_stack,
@@ -2539,7 +2542,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         let InferOk { obligations, .. } = self
             .infcx
             .at(&cause, obligation.param_env)
-            .eq(DefineOpaqueTypes::No, placeholder_obligation_trait_ref, impl_trait_ref)
+            .eq(DefineOpaqueTypes::Yes, placeholder_obligation_trait_ref, impl_trait_ref)
             .map_err(|e| {
                 debug!("match_impl: failed eq_trait_refs due to `{}`", e.to_string(self.tcx()))
             })?;
@@ -2594,7 +2597,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                         self.infcx
                             .at(&obligation.cause, obligation.param_env)
                             .eq(
-                                DefineOpaqueTypes::No,
+                                DefineOpaqueTypes::Yes,
                                 upcast_principal.map_bound(|trait_ref| {
                                     ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
                                 }),
@@ -2631,7 +2634,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                     nested.extend(
                         self.infcx
                             .at(&obligation.cause, obligation.param_env)
-                            .eq(DefineOpaqueTypes::No, source_projection, target_projection)
+                            .eq(DefineOpaqueTypes::Yes, source_projection, target_projection)
                             .map_err(|_| SelectionError::Unimplemented)?
                             .into_obligations(),
                     );
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 826bb706f48..c2727ae6bfd 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -21,7 +21,7 @@ use crate::traits::{
     self, coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt,
 };
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{codes::*, DelayDm, Diag, EmissionGuarantee};
+use rustc_errors::{codes::*, Diag, EmissionGuarantee};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::bug;
 use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt};
@@ -449,7 +449,7 @@ fn report_conflicting_impls<'tcx>(
         }
     }
 
-    let msg = DelayDm(|| {
+    let msg = || {
         format!(
             "conflicting implementations of trait `{}`{}{}",
             overlap.trait_ref.print_trait_sugared(),
@@ -459,7 +459,7 @@ fn report_conflicting_impls<'tcx>(
                 _ => "",
             }
         )
-    });
+    };
 
     // Don't report overlap errors if the header references error
     if let Err(err) = (overlap.trait_ref, overlap.self_ty).error_reported() {
@@ -471,7 +471,7 @@ fn report_conflicting_impls<'tcx>(
             let reported = if overlap.with_impl.is_local()
                 || tcx.ensure().orphan_check_impl(impl_def_id).is_ok()
             {
-                let mut err = tcx.dcx().struct_span_err(impl_span, msg);
+                let mut err = tcx.dcx().struct_span_err(impl_span, msg());
                 err.code(E0119);
                 decorate(tcx, &overlap, impl_span, &mut err);
                 err.emit()
@@ -485,15 +485,10 @@ fn report_conflicting_impls<'tcx>(
                 FutureCompatOverlapErrorKind::OrderDepTraitObjects => ORDER_DEPENDENT_TRAIT_OBJECTS,
                 FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK,
             };
-            tcx.node_span_lint(
-                lint,
-                tcx.local_def_id_to_hir_id(impl_def_id),
-                impl_span,
-                msg,
-                |err| {
-                    decorate(tcx, &overlap, impl_span, err);
-                },
-            );
+            tcx.node_span_lint(lint, tcx.local_def_id_to_hir_id(impl_def_id), impl_span, |err| {
+                err.primary_message(msg());
+                decorate(tcx, &overlap, impl_span, err);
+            });
             Ok(())
         }
     }