about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-02-22 05:06:27 +0000
committerbors <bors@rust-lang.org>2024-02-22 05:06:27 +0000
commit3815fc06256f6281ee064fc607d24020dc96f05b (patch)
treea9ec036cc3cb6e18d43f62e85574e81288b82cf0 /compiler/rustc_trait_selection/src
parent596dd65cfa1d5f1a55a9a68d7f45bda436e3055d (diff)
parent6f3bc7d938f5a8a772ba67cf41aad16a03b9fb1c (diff)
Auto merge of #3310 - rust-lang:rustup-2024-02-22, r=oli-obk
Automatic Rustup
Diffstat (limited to 'compiler/rustc_trait_selection/src')
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs10
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs34
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs25
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs71
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs)0
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs125
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs56
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs60
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs419
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs843
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs21
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs21
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs342
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs2
22 files changed, 997 insertions, 1079 deletions
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 407fff03e15..10911c0d002 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1,7 +1,7 @@
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
-    EmissionGuarantee, IntoDiagnostic, Level, SubdiagnosticMessageOp,
+    codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee,
+    IntoDiagnostic, Level, SubdiagnosticMessageOp,
 };
 use rustc_macros::Diagnostic;
 use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty};
@@ -102,7 +102,11 @@ pub enum AdjustSignatureBorrow {
 }
 
 impl AddToDiagnostic for AdjustSignatureBorrow {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         match self {
             AdjustSignatureBorrow::Borrow { to_borrow } => {
                 diag.arg("len", to_borrow.len());
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 63555a305d8..97f715b6386 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -109,7 +109,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
         let mut errors = Vec::new();
         for i in 0.. {
             if !infcx.tcx.recursion_limit().value_within_limit(i) {
-                unimplemented!("overflowed on pending obligations: {:?}", self.obligations);
+                // Only return true errors that we have accumulated while processing;
+                // keep ambiguities around, *including overflows*, because they shouldn't
+                // be considered true errors.
+                return errors;
             }
 
             let mut has_changed = false;
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index d87cc89954a..91312c9fdd6 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -1,6 +1,6 @@
 use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
-use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+use crate::traits::{BoundVarReplacer, PlaceholderReplacer};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::infer::at::At;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -85,25 +85,16 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
             ),
         );
 
-        // Do not emit an error if normalization is known to fail but instead
-        // keep the projection unnormalized. This is the case for projections
-        // with a `T: Trait` where-clause and opaque types outside of the defining
-        // scope.
-        let result = if infcx.predicate_may_hold(&obligation) {
-            self.fulfill_cx.register_predicate_obligation(infcx, obligation);
-            let errors = self.fulfill_cx.select_all_or_error(infcx);
-            if !errors.is_empty() {
-                return Err(errors);
-            }
-            let ty = infcx.resolve_vars_if_possible(new_infer_ty);
-
-            // Alias is guaranteed to be fully structurally resolved,
-            // so we can super fold here.
-            ty.try_super_fold_with(self)?
-        } else {
-            alias_ty.try_super_fold_with(self)?
-        };
+        self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+        let errors = self.fulfill_cx.select_all_or_error(infcx);
+        if !errors.is_empty() {
+            return Err(errors);
+        }
 
+        // Alias is guaranteed to be fully structurally resolved,
+        // so we can super fold here.
+        let ty = infcx.resolve_vars_if_possible(new_infer_ty);
+        let result = ty.try_super_fold_with(self)?;
         self.depth -= 1;
         Ok(result)
     }
@@ -178,6 +169,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
         Ok(t)
     }
 
+    #[instrument(level = "debug", skip(self), ret)]
     fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
         let infcx = self.at.infcx;
         debug_assert_eq!(ty, infcx.shallow_resolve(ty));
@@ -204,11 +196,11 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self), ret)]
     fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
-        let reveal = self.at.param_env.reveal();
         let infcx = self.at.infcx;
         debug_assert_eq!(ct, infcx.shallow_resolve(ct));
-        if !needs_normalization(&ct, reveal) {
+        if !ct.has_projections() {
             return Ok(ct);
         }
 
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
new file mode 100644
index 00000000000..911462f4b9a
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
@@ -0,0 +1,25 @@
+use crate::solve::EvalCtxt;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    #[instrument(level = "debug", skip(self), ret)]
+    pub(super) fn normalize_anon_const(
+        &mut self,
+        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+    ) -> QueryResult<'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()
+                .type_of(goal.predicate.alias.def_id)
+                .no_bound_vars()
+                .expect("const ty should not rely on other generics"),
+        ) {
+            self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        } else {
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+        }
+    }
+}
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 d177109c420..5f625831156 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -18,8 +18,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
 use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};
 
+mod anon_const;
 mod inherent;
-mod opaques;
+mod opaque_types;
 mod weak_types;
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -31,34 +32,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let def_id = goal.predicate.def_id();
         match self.tcx().def_kind(def_id) {
             DefKind::AssocTy | DefKind::AssocConst => {
-                // To only compute normalization once for each projection we only
-                // assemble normalization candidates if the expected term is an
-                // unconstrained inference variable.
-                //
-                // Why: For better cache hits, since if we have an unconstrained RHS then
-                // there are only as many cache keys as there are (canonicalized) alias
-                // types in each normalizes-to goal. This also weakens inference in a
-                // forwards-compatible way so we don't use the value of the RHS term to
-                // affect candidate assembly for projections.
-                //
-                // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
-                // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
-                // `U` and equate it with `u32`. This means that we don't need a separate
-                // projection cache in the solver, since we're piggybacking off of regular
-                // goal caching.
-                if self.term_is_fully_unconstrained(goal) {
-                    match self.tcx().associated_item(def_id).container {
-                        ty::AssocItemContainer::TraitContainer => {
+                match self.tcx().associated_item(def_id).container {
+                    ty::AssocItemContainer::TraitContainer => {
+                        // To only compute normalization once for each projection we only
+                        // assemble normalization candidates if the expected term is an
+                        // unconstrained inference variable.
+                        //
+                        // Why: For better cache hits, since if we have an unconstrained RHS then
+                        // there are only as many cache keys as there are (canonicalized) alias
+                        // types in each normalizes-to goal. This also weakens inference in a
+                        // forwards-compatible way so we don't use the value of the RHS term to
+                        // affect candidate assembly for projections.
+                        //
+                        // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
+                        // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
+                        // `U` and equate it with `u32`. This means that we don't need a separate
+                        // projection cache in the solver, since we're piggybacking off of regular
+                        // goal caching.
+                        if self.term_is_fully_unconstrained(goal) {
                             let candidates = self.assemble_and_evaluate_candidates(goal);
                             self.merge_candidates(candidates)
+                        } else {
+                            self.set_normalizes_to_hack_goal(goal);
+                            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                         }
-                        ty::AssocItemContainer::ImplContainer => {
-                            self.normalize_inherent_associated_type(goal)
-                        }
                     }
-                } else {
-                    self.set_normalizes_to_hack_goal(goal);
-                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                    ty::AssocItemContainer::ImplContainer => {
+                        self.normalize_inherent_associated_type(goal)
+                    }
                 }
             }
             DefKind::AnonConst => self.normalize_anon_const(goal),
@@ -67,26 +68,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
         }
     }
-
-    #[instrument(level = "debug", skip(self), ret)]
-    fn normalize_anon_const(
-        &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'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()
-                .type_of(goal.predicate.alias.def_id)
-                .no_bound_vars()
-                .expect("const ty should not rely on other generics"),
-        ) {
-            self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
-            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        } else {
-            self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
-        }
-    }
 }
 
 impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs
index 356c3776c04..356c3776c04 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index f663f02f872..3619d02438d 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -18,7 +18,7 @@ use crate::traits::{
     Obligation, ObligationCause, PredicateObligation, PredicateObligations, SelectionContext,
 };
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::Diagnostic;
+use rustc_errors::{DiagnosticBuilder, EmissionGuarantee};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
@@ -58,7 +58,7 @@ pub struct OverlapResult<'tcx> {
     pub involves_placeholder: bool,
 }
 
-pub fn add_placeholder_note(err: &mut Diagnostic) {
+pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut DiagnosticBuilder<'_, G>) {
     err.note(
         "this behavior recently changed as a result of a bug fix; \
          see rust-lang/rust#56105 for details",
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 1edf6b11fc3..189e1ba54bc 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -62,14 +62,11 @@ pub fn is_const_evaluatable<'tcx>(
 
         match unexpanded_ct.kind() {
             ty::ConstKind::Expr(_) => {
-                // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete,
-                // but currently it is not possible to evaluate `ConstKind::Expr` so we are unable
-                // to tell if it is evaluatable or not. For now we just ICE until this is
-                // implemented.
-                Err(NotConstEvaluatable::Error(tcx.dcx().span_delayed_bug(
-                    span,
-                    "evaluating `ConstKind::Expr` is not currently supported",
-                )))
+                // FIXME(generic_const_exprs): we have a fully concrete `ConstKind::Expr`, but
+                // haven't implemented evaluating `ConstKind::Expr` yet, so we are unable to tell
+                // if it is evaluatable or not. As this is unreachable for now, we can simple ICE
+                // here.
+                tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported");
             }
             ty::ConstKind::Unevaluated(uv) => {
                 let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
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 2d85f84f480..8ae31392b40 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -13,7 +13,7 @@ use hir::def::CtorOf;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
+    codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, EmissionGuarantee,
     MultiSpan, Style, SuggestionStyle,
 };
 use rustc_hir as hir;
@@ -116,12 +116,12 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -
 /// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but
 /// it can also be an `impl Trait` param that needs to be decomposed to a type
 /// param for cleaner code.
-pub fn suggest_restriction<'tcx>(
+pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
     tcx: TyCtxt<'tcx>,
     item_id: LocalDefId,
     hir_generics: &hir::Generics<'tcx>,
     msg: &str,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_, G>,
     fn_sig: Option<&hir::FnSig<'_>>,
     projection: Option<&ty::AliasTy<'_>>,
     trait_pred: ty::PolyTraitPredicate<'tcx>,
@@ -240,7 +240,7 @@ pub fn suggest_restriction<'tcx>(
 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_restricting_param_bound(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         associated_ty: Option<(&'static str, Ty<'tcx>)>,
         mut body_id: LocalDefId,
@@ -450,7 +450,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_dereferences(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         let mut code = obligation.cause.code();
@@ -743,22 +743,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn get_closure_name(
         &self,
         def_id: DefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         msg: Cow<'static, str>,
     ) -> Option<Symbol> {
-        let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> {
-            // Get the local name of this closure. This can be inaccurate because
-            // of the possibility of reassignment, but this should be good enough.
-            match &kind {
-                hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => {
-                    Some(ident.name)
-                }
-                _ => {
-                    err.note(msg);
-                    None
+        let get_name =
+            |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind<'_>| -> Option<Symbol> {
+                // Get the local name of this closure. This can be inaccurate because
+                // of the possibility of reassignment, but this should be good enough.
+                match &kind {
+                    hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => {
+                        Some(ident.name)
+                    }
+                    _ => {
+                        err.note(msg);
+                        None
+                    }
                 }
-            }
-        };
+            };
 
         let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?);
         match self.tcx.parent_hir_node(hir_id) {
@@ -778,7 +779,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_fn_call(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         // It doesn't make sense to make this suggestion outside of typeck...
@@ -894,7 +895,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn check_for_binding_assigned_block_without_tail_expression(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let mut span = obligation.cause.span;
@@ -971,7 +972,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_add_clone_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
@@ -1156,7 +1157,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_add_reference_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         poly_trait_pred: ty::PolyTraitPredicate<'tcx>,
         has_custom_message: bool,
     ) -> bool {
@@ -1282,6 +1283,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             "the full type name has been written to '{}'",
                             file.display()
                         ));
+                        err.note(format!(
+                            "consider using `--verbose` to print full type name to the console"
+                        ));
                     }
 
                     if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred {
@@ -1374,7 +1378,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     // Suggest borrowing the type
     fn suggest_borrowing_for_object_cast(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
         self_ty: Ty<'tcx>,
         target_ty: Ty<'tcx>,
@@ -1410,7 +1414,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_remove_reference(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         let mut span = obligation.cause.span;
@@ -1529,7 +1533,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         false
     }
 
-    fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
+    fn suggest_remove_await(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut DiagnosticBuilder<'_>,
+    ) {
         let hir = self.tcx.hir();
         if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives()
             && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id)
@@ -1599,7 +1607,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_change_mut(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let points_at_arg = matches!(
@@ -1677,7 +1685,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_semicolon_removal(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
@@ -1731,7 +1739,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// emitted.
     fn suggest_impl_trait(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
@@ -1913,7 +1921,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn note_conflicting_fn_args(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         cause: &ObligationCauseCode<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -2120,7 +2128,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn suggest_fully_qualified_path(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         item_def_id: DefId,
         span: Span,
         trait_ref: DefId,
@@ -2185,9 +2193,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     ///
     /// Returns `true` if an async-await specific note was added to the diagnostic.
     #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))]
-    fn maybe_note_obligation_cause_for_async_await(
+    fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         obligation: &PredicateObligation<'tcx>,
     ) -> bool {
         let hir = self.tcx.hir();
@@ -2421,9 +2429,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// Unconditionally adds the diagnostic note described in
     /// `maybe_note_obligation_cause_for_async_await`'s documentation comment.
     #[instrument(level = "debug", skip_all)]
-    fn note_obligation_cause_for_async_await(
+    fn note_obligation_cause_for_async_await<G: EmissionGuarantee>(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         interior_or_upvar_span: CoroutineInteriorOrUpvar,
         is_async: bool,
         outer_coroutine: Option<DefId>,
@@ -2656,10 +2664,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         );
     }
 
-    fn note_obligation_cause_code<T>(
+    fn note_obligation_cause_code<G: EmissionGuarantee, T>(
         &self,
         body_id: LocalDefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         predicate: T,
         param_env: ty::ParamEnv<'tcx>,
         cause_code: &ObligationCauseCode<'tcx>,
@@ -2861,6 +2869,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         "the full name for the type has been written to '{}'",
                         file.display(),
                     ));
+                    err.note(format!(
+                        "consider using `--verbose` to print the full type name to the console"
+                    ));
                 }
             }
             ObligationCauseCode::RepeatElementCopy {
@@ -3328,6 +3339,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         "the full type name has been written to '{}'",
                         file.display(),
                     ));
+                    err.note(format!(
+                        "consider using `--verbose` to print the full type name to the console"
+                    ));
                 }
                 let mut parent_predicate = parent_trait_pred;
                 let mut data = &data.derived;
@@ -3381,6 +3395,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             "the full type name has been written to '{}'",
                             file.display(),
                         ));
+                        err.note(format!(
+                            "consider using `--verbose` to print the full type name to the console"
+                        ));
                     }
                 }
                 // #74711: avoid a stack overflow
@@ -3507,7 +3524,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     )]
     fn suggest_await_before_try(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         span: Span,
@@ -3565,7 +3582,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_floating_point_literal(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_ref: &ty::PolyTraitRef<'tcx>,
     ) {
         let rhs_span = match obligation.cause.code() {
@@ -3589,7 +3606,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_derive(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
@@ -3655,7 +3672,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_dereferencing_index(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code()
@@ -3675,10 +3692,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn note_function_argument_obligation(
+    fn note_function_argument_obligation<G: EmissionGuarantee>(
         &self,
         body_id: LocalDefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         arg_hir_id: HirId,
         parent_code: &ObligationCauseCode<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
@@ -3860,11 +3877,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn suggest_option_method_if_applicable(
+    fn suggest_option_method_if_applicable<G: EmissionGuarantee>(
         &self,
         failed_pred: ty::Predicate<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         expr: &hir::Expr<'_>,
     ) {
         let tcx = self.tcx;
@@ -3934,7 +3951,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn look_for_iterator_item_mistakes(
+    fn look_for_iterator_item_mistakes<G: EmissionGuarantee>(
         &self,
         assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
         typeck_results: &TypeckResults<'tcx>,
@@ -3942,7 +3959,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         path_segment: &hir::PathSegment<'_>,
         args: &[hir::Expr<'_>],
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
     ) {
         let tcx = self.tcx;
         // Special case for iterator chains, we look at potential failures of `Iterator::Item`
@@ -4037,13 +4054,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn point_at_chain(
+    fn point_at_chain<G: EmissionGuarantee>(
         &self,
         expr: &hir::Expr<'_>,
         typeck_results: &TypeckResults<'tcx>,
         type_diffs: Vec<TypeError<'tcx>>,
         param_env: ty::ParamEnv<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
     ) {
         let mut primary_spans = vec![];
         let mut span_labels = vec![];
@@ -4279,7 +4296,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// the array into a slice.
     fn suggest_convert_to_slice(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
         trait_ref: ty::PolyTraitRef<'tcx>,
         candidate_impls: &[ImplCandidate<'tcx>],
@@ -4351,7 +4368,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn explain_hrtb_projection(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         pred: ty::PolyTraitPredicate<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         cause: &ObligationCause<'tcx>,
@@ -4417,7 +4434,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn suggest_desugaring_async_fn_in_trait(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_ref: ty::PolyTraitRef<'tcx>,
     ) {
         // Don't suggest if RTN is active -- we should prefer a where-clause bound instead.
@@ -4508,7 +4525,7 @@ fn hint_missing_borrow<'tcx>(
     found: Ty<'tcx>,
     expected: Ty<'tcx>,
     found_node: Node<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
 ) {
     if matches!(found_node, Node::TraitItem(..)) {
         return;
@@ -4867,9 +4884,9 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>(
 
 /// On `impl` evaluation cycles, look for `Self::AssocTy` restrictions in `where` clauses, explain
 /// they are not allowed and if possible suggest alternatives.
-fn point_at_assoc_type_restriction(
+fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
     tcx: TyCtxt<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_, G>,
     self_ty_str: &str,
     trait_name: &str,
     predicate: ty::Predicate<'_>,
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 67bd18d7404..7186b96b40d 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
@@ -21,8 +21,8 @@ use crate::traits::{
 };
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
-    ErrorGuaranteed, MultiSpan, StashKey, StringPart,
+    codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
+    FatalError, MultiSpan, StashKey, StringPart,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Namespace, Res};
@@ -185,7 +185,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         predicate: &T,
         span: Span,
         suggest_increasing_limit: bool,
-        mutate: impl FnOnce(&mut Diagnostic),
+        mutate: impl FnOnce(&mut DiagnosticBuilder<'_>),
     ) -> !
     where
         T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
@@ -193,14 +193,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit);
         mutate(&mut err);
         err.emit();
-
-        self.dcx().abort_if_errors();
-        // FIXME: this should be something like `build_overflow_error_fatal`, which returns
-        // `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`.
-        unreachable!(
-            "did not expect compilation to continue after `abort_if_errors`, \
-            since an error was definitely emitted!"
-        );
+        FatalError.raise();
     }
 
     fn build_overflow_error<T>(
@@ -272,7 +265,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         );
     }
 
-    fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) {
+    fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) {
         let suggested_limit = match self.tcx.recursion_limit() {
             Limit(0) => Limit(2),
             limit => limit * 2,
@@ -1020,7 +1013,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         obligation: &PredicateObligation<'tcx>,
         trait_ref: ty::TraitRef<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) -> bool {
         let span = obligation.cause.span;
         struct V<'v> {
@@ -1810,7 +1803,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         impl_candidates: &[ImplCandidate<'tcx>],
         trait_ref: ty::PolyTraitRef<'tcx>,
         body_def_id: LocalDefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         other: bool,
         param_env: ty::ParamEnv<'tcx>,
     ) -> bool {
@@ -1897,7 +1890,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
 
         let other = if other { "other " } else { "" };
-        let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
+        let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut DiagnosticBuilder<'_>| {
             if candidates.is_empty() {
                 return false;
             }
@@ -2032,7 +2025,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
         body_def_id: LocalDefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) {
         // This is *almost* equivalent to
         // `obligation.cause.code().peel_derives()`, but it gives us the
@@ -2103,7 +2096,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// a probable version mismatch is added to `err`
     fn note_version_mismatch(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_ref: &ty::PolyTraitRef<'tcx>,
     ) -> bool {
         let get_trait_impls = |trait_def_id| {
@@ -2572,7 +2565,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn annotate_source_of_ambiguity(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         ambiguities: &[ambiguity::Ambiguity],
         predicate: ty::Predicate<'tcx>,
     ) {
@@ -2715,7 +2708,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         })
     }
 
-    fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>) {
+    fn note_obligation_cause(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        obligation: &PredicateObligation<'tcx>,
+    ) {
         // First, attempt to add note to this error with an async-await-specific
         // message, and fall back to regular note otherwise.
         if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
@@ -2744,7 +2741,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     #[instrument(level = "debug", skip_all)]
     fn suggest_unsized_bound_if_applicable(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
     ) {
         let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
@@ -2770,7 +2767,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     }
 
     #[instrument(level = "debug", skip_all)]
-    fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) {
+    fn maybe_suggest_unsized_generics(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        span: Span,
+        node: Node<'tcx>,
+    ) {
         let Some(generics) = node.generics() else {
             return;
         };
@@ -2822,7 +2824,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn maybe_indirection_for_unsized(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         item: &Item<'tcx>,
         param: &GenericParam<'tcx>,
     ) -> bool {
@@ -3016,7 +3018,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn add_tuple_trait_message(
         &self,
         obligation_cause_code: &ObligationCauseCode<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) {
         match obligation_cause_code {
             ObligationCauseCode::RustCall => {
@@ -3041,7 +3043,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         trait_ref: ty::PolyTraitRef<'tcx>,
         trait_predicate: &ty::PolyTraitPredicate<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         is_fn_trait: bool,
         suggested: bool,
@@ -3122,7 +3124,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn add_help_message_for_fn_trait(
         &self,
         trait_ref: ty::PolyTraitRef<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         implemented_kind: ty::ClosureKind,
         params: ty::Binder<'tcx, Ty<'tcx>>,
     ) {
@@ -3178,7 +3180,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn maybe_add_note_for_unsatisfied_const(
         &self,
         _trait_predicate: &ty::PolyTraitPredicate<'tcx>,
-        _err: &mut Diagnostic,
+        _err: &mut DiagnosticBuilder<'_>,
         _span: Span,
     ) -> UnsatisfiedConst {
         let unsatisfied_const = UnsatisfiedConst(false);
@@ -3304,7 +3306,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         expected_trait_ref.self_ty().error_reported()?;
 
         let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else {
-            return Err(self.dcx().delayed_bug("bound vars outside binder"));
+            self.dcx().bug("bound vars outside binder");
         };
 
         let found_did = match *found_trait_ty.kind() {
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 7ad94274634..fd981130af8 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -1,5 +1,6 @@
 use crate::infer::{InferCtxt, TyOrConstInferVar};
 use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::normalize::normalize_with_depth_to;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::obligation_forest::ProcessResult;
 use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
@@ -312,7 +313,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
 
         if obligation.predicate.has_projections() {
             let mut obligations = Vec::new();
-            let predicate = crate::traits::project::try_normalize_with_depth_to(
+            let predicate = normalize_with_depth_to(
                 &mut self.selcx,
                 obligation.param_env,
                 obligation.cause.clone(),
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 7caaccab63b..cac53796747 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -9,6 +9,7 @@ mod engine;
 pub mod error_reporting;
 mod fulfill;
 pub mod misc;
+pub mod normalize;
 mod object_safety;
 pub mod outlives_bounds;
 pub mod project;
@@ -40,17 +41,15 @@ use rustc_span::Span;
 use std::fmt::Debug;
 use std::ops::ControlFlow;
 
-pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
-
 pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
 pub use self::coherence::{OrphanCheckErr, OverlapResult};
 pub use self::engine::{ObligationCtxt, TraitEngineExt};
 pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
+pub use self::normalize::NormalizeExt;
 pub use self::object_safety::astconv_object_safety_violations;
 pub use self::object_safety::is_vtable_safe_method;
 pub use self::object_safety::object_safety_violations_for_assoc_item;
 pub use self::object_safety::ObjectSafetyViolation;
-pub use self::project::NormalizeExt;
 pub use self::project::{normalize_inherent_projection, normalize_projection_type};
 pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
 pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
@@ -68,6 +67,7 @@ pub use self::util::{
 };
 pub use self::util::{expand_trait_aliases, TraitAliasExpander};
 pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
+pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
 
 pub use rustc_infer::traits::*;
 
@@ -119,9 +119,7 @@ pub fn predicates_for_generics<'tcx>(
 
 /// Determines whether the type `ty` is known to meet `bound` and
 /// returns true if so. Returns false if `ty` either does not meet
-/// `bound` or is not known to meet bound (note that this is
-/// conservative towards *no impl*, which is the opposite of the
-/// `evaluate` methods).
+/// `bound` or is not known to meet bound.
 pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
     infcx: &InferCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -129,50 +127,8 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
     def_id: DefId,
 ) -> bool {
     let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]);
-    pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref)
-}
-
-/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist?
-///
-/// Ping me on zulip if you want to use this method and need help with finding
-/// an appropriate replacement.
-#[instrument(level = "debug", skip(infcx, param_env, pred), ret)]
-fn pred_known_to_hold_modulo_regions<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    pred: impl ToPredicate<'tcx>,
-) -> bool {
-    let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred);
-
-    let result = infcx.evaluate_obligation_no_overflow(&obligation);
-    debug!(?result);
-
-    if result.must_apply_modulo_regions() {
-        true
-    } else if result.may_apply() {
-        // Sometimes obligations are ambiguous because the recursive evaluator
-        // is not smart enough, so we fall back to fulfillment when we're not certain
-        // that an obligation holds or not. Even still, we must make sure that
-        // the we do no inference in the process of checking this obligation.
-        let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
-        infcx.probe(|_| {
-            let ocx = ObligationCtxt::new(infcx);
-            ocx.register_obligation(obligation);
-
-            let errors = ocx.select_all_or_error();
-            match errors.as_slice() {
-                // Only known to hold if we did no inference.
-                [] => infcx.shallow_resolve(goal) == goal,
-
-                errors => {
-                    debug!(?errors);
-                    false
-                }
-            }
-        })
-    } else {
-        false
-    }
+    let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref);
+    infcx.predicate_must_hold_modulo_regions(&obligation)
 }
 
 #[instrument(level = "debug", skip(tcx, elaborated_env))]
@@ -216,7 +172,9 @@ fn do_normalize_predicates<'tcx>(
     // the normalized predicates.
     let errors = infcx.resolve_regions(&outlives_env);
     if !errors.is_empty() {
-        tcx.dcx().span_delayed_bug(
+        // @lcnr: Let's still ICE here for now. I want a test case
+        // for that.
+        tcx.dcx().span_bug(
             span,
             format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"),
         );
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
new file mode 100644
index 00000000000..429e5a5d7a4
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -0,0 +1,419 @@
+//! Deeply normalize types using the old trait solver.
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_infer::infer::at::At;
+use rustc_infer::infer::InferOk;
+use rustc_infer::traits::PredicateObligation;
+use rustc_infer::traits::{FulfillmentError, Normalized, Obligation, TraitEngine};
+use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder};
+use rustc_middle::ty::{TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitableExt};
+
+use super::error_reporting::TypeErrCtxtExt;
+use super::SelectionContext;
+use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
+
+#[extension(pub trait NormalizeExt<'tcx>)]
+impl<'tcx> At<'_, 'tcx> {
+    /// Normalize a value using the `AssocTypeNormalizer`.
+    ///
+    /// This normalization should be used when the type contains inference variables or the
+    /// projection may be fallible.
+    fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
+        if self.infcx.next_trait_solver() {
+            InferOk { value, obligations: Vec::new() }
+        } else {
+            let mut selcx = SelectionContext::new(self.infcx);
+            let Normalized { value, obligations } =
+                normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
+            InferOk { value, obligations }
+        }
+    }
+
+    /// Deeply normalizes `value`, replacing all aliases which can by normalized in
+    /// the current environment. In the new solver this errors in case normalization
+    /// fails or is ambiguous.
+    ///
+    /// In the old solver this simply uses `normalizes` and adds the nested obligations
+    /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the
+    /// same goals in both a temporary and the shared context which negatively impacts
+    /// performance as these don't share caching.
+    ///
+    /// FIXME(-Znext-solver): This has the same behavior as `traits::fully_normalize`
+    /// in the new solver, but because of performance reasons, we currently reuse an
+    /// existing fulfillment context in the old solver. Once we also eagerly prove goals with
+    /// the old solver or have removed the old solver, remove `traits::fully_normalize` and
+    /// rename this function to `At::fully_normalize`.
+    fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+        self,
+        value: T,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+        if self.infcx.next_trait_solver() {
+            crate::solve::deeply_normalize(self, value)
+        } else {
+            let value = self
+                .normalize(value)
+                .into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
+            let errors = fulfill_cx.select_where_possible(self.infcx);
+            let value = self.infcx.resolve_vars_if_possible(value);
+            if errors.is_empty() { Ok(value) } else { Err(errors) }
+        }
+    }
+}
+
+/// As `normalize`, but with a custom depth.
+pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    value: T,
+) -> Normalized<'tcx, T>
+where
+    T: TypeFoldable<TyCtxt<'tcx>>,
+{
+    let mut obligations = Vec::new();
+    let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations);
+    Normalized { value, obligations }
+}
+
+#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
+pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    value: T,
+    obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> T
+where
+    T: TypeFoldable<TyCtxt<'tcx>>,
+{
+    debug!(obligations.len = obligations.len());
+    let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations);
+    let result = ensure_sufficient_stack(|| normalizer.fold(value));
+    debug!(?result, obligations.len = normalizer.obligations.len());
+    debug!(?normalizer.obligations,);
+    result
+}
+
+pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
+    value: &T,
+    reveal: Reveal,
+) -> bool {
+    let mut flags = ty::TypeFlags::HAS_TY_PROJECTION
+        | ty::TypeFlags::HAS_TY_WEAK
+        | ty::TypeFlags::HAS_TY_INHERENT
+        | ty::TypeFlags::HAS_CT_PROJECTION;
+
+    match reveal {
+        Reveal::UserFacing => {}
+        Reveal::All => flags |= ty::TypeFlags::HAS_TY_OPAQUE,
+    }
+
+    value.has_type_flags(flags)
+}
+
+struct AssocTypeNormalizer<'a, 'b, 'tcx> {
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+    depth: usize,
+    universes: Vec<Option<ty::UniverseIndex>>,
+}
+
+impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
+    fn new(
+        selcx: &'a mut SelectionContext<'b, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: ObligationCause<'tcx>,
+        depth: usize,
+        obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+    ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
+        debug_assert!(!selcx.infcx.next_trait_solver());
+        AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] }
+    }
+
+    fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T {
+        let value = self.selcx.infcx.resolve_vars_if_possible(value);
+        debug!(?value);
+
+        assert!(
+            !value.has_escaping_bound_vars(),
+            "Normalizing {value:?} without wrapping in a `Binder`"
+        );
+
+        if !needs_normalization(&value, self.param_env.reveal()) {
+            value
+        } else {
+            value.fold_with(self)
+        }
+    }
+}
+
+impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.selcx.tcx()
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.universes.push(None);
+        let t = t.super_fold_with(self);
+        self.universes.pop();
+        t
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if !needs_normalization(&ty, self.param_env.reveal()) {
+            return ty;
+        }
+
+        let (kind, data) = match *ty.kind() {
+            ty::Alias(kind, alias_ty) => (kind, alias_ty),
+            _ => return ty.super_fold_with(self),
+        };
+
+        // We try to be a little clever here as a performance optimization in
+        // cases where there are nested projections under binders.
+        // For example:
+        // ```
+        // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>)
+        // ```
+        // We normalize the args on the projection before the projecting, but
+        // if we're naive, we'll
+        //   replace bound vars on inner, project inner, replace placeholders on inner,
+        //   replace bound vars on outer, project outer, replace placeholders on outer
+        //
+        // However, if we're a bit more clever, we can replace the bound vars
+        // on the entire type before normalizing nested projections, meaning we
+        //   replace bound vars on outer, project inner,
+        //   project outer, replace placeholders on outer
+        //
+        // This is possible because the inner `'a` will already be a placeholder
+        // when we need to normalize the inner projection
+        //
+        // On the other hand, this does add a bit of complexity, since we only
+        // replace bound vars if the current type is a `Projection` and we need
+        // to make sure we don't forget to fold the args regardless.
+
+        match kind {
+            ty::Opaque => {
+                // Only normalize `impl Trait` outside of type inference, usually in codegen.
+                match self.param_env.reveal() {
+                    Reveal::UserFacing => ty.super_fold_with(self),
+
+                    Reveal::All => {
+                        let recursion_limit = self.interner().recursion_limit();
+                        if !recursion_limit.value_within_limit(self.depth) {
+                            self.selcx.infcx.err_ctxt().report_overflow_error(
+                                &ty,
+                                self.cause.span,
+                                true,
+                                |_| {},
+                            );
+                        }
+
+                        let args = data.args.fold_with(self);
+                        let generic_ty = self.interner().type_of(data.def_id);
+                        let concrete_ty = generic_ty.instantiate(self.interner(), args);
+                        self.depth += 1;
+                        let folded_ty = self.fold_ty(concrete_ty);
+                        self.depth -= 1;
+                        folded_ty
+                    }
+                }
+            }
+
+            ty::Projection if !data.has_escaping_bound_vars() => {
+                // This branch is *mostly* just an optimization: when we don't
+                // have escaping bound vars, we don't need to replace them with
+                // placeholders (see branch below). *Also*, we know that we can
+                // register an obligation to *later* project, since we know
+                // there won't be bound vars there.
+                let data = data.fold_with(self);
+                let normalized_ty = project::normalize_projection_type(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                );
+                debug!(
+                    ?self.depth,
+                    ?ty,
+                    ?normalized_ty,
+                    obligations.len = ?self.obligations.len(),
+                    "AssocTypeNormalizer: normalized type"
+                );
+                normalized_ty.ty().unwrap()
+            }
+
+            ty::Projection => {
+                // If there are escaping bound vars, we temporarily replace the
+                // bound vars with placeholders. Note though, that in the case
+                // that we still can't project for whatever reason (e.g. self
+                // type isn't known enough), we *can't* register an obligation
+                // and return an inference variable (since then that obligation
+                // would have bound vars and that's a can of worms). Instead,
+                // we just give up and fall back to pretending like we never tried!
+                //
+                // Note: this isn't necessarily the final approach here; we may
+                // want to figure out how to register obligations with escaping vars
+                // or handle this some other way.
+
+                let infcx = self.selcx.infcx;
+                let (data, mapped_regions, mapped_types, mapped_consts) =
+                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+                let data = data.fold_with(self);
+                let normalized_ty = project::opt_normalize_projection_type(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                )
+                .ok()
+                .flatten()
+                .map(|term| term.ty().unwrap())
+                .map(|normalized_ty| {
+                    PlaceholderReplacer::replace_placeholders(
+                        infcx,
+                        mapped_regions,
+                        mapped_types,
+                        mapped_consts,
+                        &self.universes,
+                        normalized_ty,
+                    )
+                })
+                .unwrap_or_else(|| ty.super_fold_with(self));
+
+                debug!(
+                    ?self.depth,
+                    ?ty,
+                    ?normalized_ty,
+                    obligations.len = ?self.obligations.len(),
+                    "AssocTypeNormalizer: normalized type"
+                );
+                normalized_ty
+            }
+            ty::Weak => {
+                let recursion_limit = self.interner().recursion_limit();
+                if !recursion_limit.value_within_limit(self.depth) {
+                    self.selcx.infcx.err_ctxt().report_overflow_error(
+                        &ty,
+                        self.cause.span,
+                        false,
+                        |diag| {
+                            diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow);
+                        },
+                    );
+                }
+
+                let infcx = self.selcx.infcx;
+                self.obligations.extend(
+                    infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map(
+                        |(mut predicate, span)| {
+                            if data.has_escaping_bound_vars() {
+                                (predicate, ..) = BoundVarReplacer::replace_bound_vars(
+                                    infcx,
+                                    &mut self.universes,
+                                    predicate,
+                                );
+                            }
+                            let mut cause = self.cause.clone();
+                            cause.map_code(|code| {
+                                ObligationCauseCode::TypeAlias(code, span, data.def_id)
+                            });
+                            Obligation::new(infcx.tcx, cause, self.param_env, predicate)
+                        },
+                    ),
+                );
+                self.depth += 1;
+                let res = infcx
+                    .tcx
+                    .type_of(data.def_id)
+                    .instantiate(infcx.tcx, data.args)
+                    .fold_with(self);
+                self.depth -= 1;
+                res
+            }
+
+            ty::Inherent if !data.has_escaping_bound_vars() => {
+                // This branch is *mostly* just an optimization: when we don't
+                // have escaping bound vars, we don't need to replace them with
+                // placeholders (see branch below). *Also*, we know that we can
+                // register an obligation to *later* project, since we know
+                // there won't be bound vars there.
+
+                let data = data.fold_with(self);
+
+                project::normalize_inherent_projection(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                )
+            }
+
+            ty::Inherent => {
+                let infcx = self.selcx.infcx;
+                let (data, mapped_regions, mapped_types, mapped_consts) =
+                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+                let data = data.fold_with(self);
+                let ty = project::normalize_inherent_projection(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                );
+
+                PlaceholderReplacer::replace_placeholders(
+                    infcx,
+                    mapped_regions,
+                    mapped_types,
+                    mapped_consts,
+                    &self.universes,
+                    ty,
+                )
+            }
+        }
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        let tcx = self.selcx.tcx();
+        if tcx.features().generic_const_exprs
+            || !needs_normalization(&constant, self.param_env.reveal())
+        {
+            constant
+        } else {
+            let constant = constant.super_fold_with(self);
+            debug!(?constant, ?self.param_env);
+            with_replaced_escaping_bound_vars(
+                self.selcx.infcx,
+                &mut self.universes,
+                constant,
+                |constant| constant.normalize(tcx, self.param_env),
+            )
+        }
+    }
+
+    #[inline]
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
+            p.super_fold_with(self)
+        } else {
+            p
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index 6825dd4ac71..c4110df45db 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -62,9 +62,10 @@ fn implied_outlives_bounds<'a, 'tcx>(
     };
 
     let mut constraints = QueryRegionConstraints::default();
+    let span = infcx.tcx.def_span(body_id);
     let Ok(InferOk { value: mut bounds, obligations }) = infcx
         .instantiate_nll_query_response_and_region_obligations(
-            &ObligationCause::dummy(),
+            &ObligationCause::dummy_with_span(span),
             param_env,
             &canonical_var_values,
             canonical_result,
@@ -80,8 +81,6 @@ fn implied_outlives_bounds<'a, 'tcx>(
     bounds.retain(|bound| !bound.has_placeholders());
 
     if !constraints.is_empty() {
-        let span = infcx.tcx.def_span(body_id);
-
         debug!(?constraints);
         if !constraints.member_constraints.is_empty() {
             span_bug!(span, "{:#?}", constraints.member_constraints);
@@ -101,7 +100,7 @@ fn implied_outlives_bounds<'a, 'tcx>(
 
         let errors = ocx.select_all_or_error();
         if !errors.is_empty() {
-            infcx.dcx().span_delayed_bug(
+            infcx.dcx().span_bug(
                 span,
                 "implied_outlives_bounds failed to solve obligations from instantiation",
             );
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 279c0003187..f8de19043e1 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1,5 +1,7 @@
 //! Code for projecting associated types out of trait references.
 
+use std::ops::ControlFlow;
+
 use super::check_args_compatible;
 use super::specialization_graph;
 use super::translate_args;
@@ -18,8 +20,9 @@ use rustc_middle::traits::ImplSourceUserDefinedData;
 
 use crate::errors::InherentProjectionNormalizationOverflow;
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use crate::infer::{BoundRegionConversionTime, InferCtxt, InferOk};
-use crate::traits::error_reporting::TypeErrCtxtExt as _;
+use crate::infer::{BoundRegionConversionTime, InferOk};
+use crate::traits::normalize::normalize_with_depth;
+use crate::traits::normalize::normalize_with_depth_to;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 use crate::traits::select::ProjectionMatchesProjection;
 use rustc_data_structures::sso::SsoHashSet;
@@ -27,21 +30,14 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
 use rustc_hir::lang_items::LangItem;
-use rustc_infer::infer::at::At;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::infer::DefineOpaqueTypes;
-use rustc_infer::traits::FulfillmentError;
-use rustc_infer::traits::ObligationCauseCode;
-use rustc_infer::traits::TraitEngine;
 use rustc_middle::traits::select::OverflowError;
-use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt};
 use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 
-use std::collections::BTreeMap;
-use std::ops::ControlFlow;
-
 pub use rustc_middle::traits::Reveal;
 
 pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
@@ -52,55 +48,6 @@ pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::AliasTy<'tcx>>;
 
 pub(super) struct InProgress;
 
-#[extension(pub trait NormalizeExt<'tcx>)]
-impl<'tcx> At<'_, 'tcx> {
-    /// Normalize a value using the `AssocTypeNormalizer`.
-    ///
-    /// This normalization should be used when the type contains inference variables or the
-    /// projection may be fallible.
-    fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
-        if self.infcx.next_trait_solver() {
-            InferOk { value, obligations: Vec::new() }
-        } else {
-            let mut selcx = SelectionContext::new(self.infcx);
-            let Normalized { value, obligations } =
-                normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
-            InferOk { value, obligations }
-        }
-    }
-
-    /// Deeply normalizes `value`, replacing all aliases which can by normalized in
-    /// the current environment. In the new solver this errors in case normalization
-    /// fails or is ambiguous. This only normalizes opaque types with `Reveal::All`.
-    ///
-    /// In the old solver this simply uses `normalizes` and adds the nested obligations
-    /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the
-    /// same goals in both a temporary and the shared context which negatively impacts
-    /// performance as these don't share caching.
-    ///
-    /// FIXME(-Znext-solver): This has the same behavior as `traits::fully_normalize`
-    /// in the new solver, but because of performance reasons, we currently reuse an
-    /// existing fulfillment context in the old solver. Once we also eagerly prove goals with
-    /// the old solver or have removed the old solver, remove `traits::fully_normalize` and
-    /// rename this function to `At::fully_normalize`.
-    fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
-        self,
-        value: T,
-        fulfill_cx: &mut dyn TraitEngine<'tcx>,
-    ) -> Result<T, Vec<FulfillmentError<'tcx>>> {
-        if self.infcx.next_trait_solver() {
-            crate::solve::deeply_normalize(self, value)
-        } else {
-            let value = self
-                .normalize(value)
-                .into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
-            let errors = fulfill_cx.select_where_possible(self.infcx);
-            let value = self.infcx.resolve_vars_if_possible(value);
-            if errors.is_empty() { Ok(value) } else { Err(errors) }
-        }
-    }
-}
-
 /// When attempting to resolve `<T as TraitRef>::Name` ...
 #[derive(Debug)]
 pub enum ProjectionError<'tcx> {
@@ -338,770 +285,6 @@ fn project_and_unify_type<'cx, 'tcx>(
     }
 }
 
-/// As `normalize`, but with a custom depth.
-pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>(
-    selcx: &'a mut SelectionContext<'b, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: ObligationCause<'tcx>,
-    depth: usize,
-    value: T,
-) -> Normalized<'tcx, T>
-where
-    T: TypeFoldable<TyCtxt<'tcx>>,
-{
-    let mut obligations = Vec::new();
-    let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations);
-    Normalized { value, obligations }
-}
-
-#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
-pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
-    selcx: &'a mut SelectionContext<'b, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: ObligationCause<'tcx>,
-    depth: usize,
-    value: T,
-    obligations: &mut Vec<PredicateObligation<'tcx>>,
-) -> T
-where
-    T: TypeFoldable<TyCtxt<'tcx>>,
-{
-    debug!(obligations.len = obligations.len());
-    let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations);
-    let result = ensure_sufficient_stack(|| normalizer.fold(value));
-    debug!(?result, obligations.len = normalizer.obligations.len());
-    debug!(?normalizer.obligations,);
-    result
-}
-
-#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
-pub(crate) fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>(
-    selcx: &'a mut SelectionContext<'b, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: ObligationCause<'tcx>,
-    depth: usize,
-    value: T,
-    obligations: &mut Vec<PredicateObligation<'tcx>>,
-) -> T
-where
-    T: TypeFoldable<TyCtxt<'tcx>>,
-{
-    debug!(obligations.len = obligations.len());
-    let mut normalizer = AssocTypeNormalizer::new_without_eager_inference_replacement(
-        selcx,
-        param_env,
-        cause,
-        depth,
-        obligations,
-    );
-    let result = ensure_sufficient_stack(|| normalizer.fold(value));
-    debug!(?result, obligations.len = normalizer.obligations.len());
-    debug!(?normalizer.obligations,);
-    result
-}
-
-pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
-    value: &T,
-    reveal: Reveal,
-) -> bool {
-    match reveal {
-        Reveal::UserFacing => value.has_type_flags(
-            ty::TypeFlags::HAS_TY_PROJECTION
-                | ty::TypeFlags::HAS_TY_INHERENT
-                | ty::TypeFlags::HAS_CT_PROJECTION,
-        ),
-        Reveal::All => value.has_type_flags(
-            ty::TypeFlags::HAS_TY_PROJECTION
-                | ty::TypeFlags::HAS_TY_INHERENT
-                | ty::TypeFlags::HAS_TY_OPAQUE
-                | ty::TypeFlags::HAS_CT_PROJECTION,
-        ),
-    }
-}
-
-struct AssocTypeNormalizer<'a, 'b, 'tcx> {
-    selcx: &'a mut SelectionContext<'b, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: ObligationCause<'tcx>,
-    obligations: &'a mut Vec<PredicateObligation<'tcx>>,
-    depth: usize,
-    universes: Vec<Option<ty::UniverseIndex>>,
-    /// If true, when a projection is unable to be completed, an inference
-    /// variable will be created and an obligation registered to project to that
-    /// inference variable. Also, constants will be eagerly evaluated.
-    eager_inference_replacement: bool,
-}
-
-impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
-    fn new(
-        selcx: &'a mut SelectionContext<'b, 'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        cause: ObligationCause<'tcx>,
-        depth: usize,
-        obligations: &'a mut Vec<PredicateObligation<'tcx>>,
-    ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
-        debug_assert!(!selcx.infcx.next_trait_solver());
-        AssocTypeNormalizer {
-            selcx,
-            param_env,
-            cause,
-            obligations,
-            depth,
-            universes: vec![],
-            eager_inference_replacement: true,
-        }
-    }
-
-    fn new_without_eager_inference_replacement(
-        selcx: &'a mut SelectionContext<'b, 'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        cause: ObligationCause<'tcx>,
-        depth: usize,
-        obligations: &'a mut Vec<PredicateObligation<'tcx>>,
-    ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
-        AssocTypeNormalizer {
-            selcx,
-            param_env,
-            cause,
-            obligations,
-            depth,
-            universes: vec![],
-            eager_inference_replacement: false,
-        }
-    }
-
-    fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T {
-        let value = self.selcx.infcx.resolve_vars_if_possible(value);
-        debug!(?value);
-
-        assert!(
-            !value.has_escaping_bound_vars(),
-            "Normalizing {value:?} without wrapping in a `Binder`"
-        );
-
-        if !needs_normalization(&value, self.param_env.reveal()) {
-            value
-        } else {
-            value.fold_with(self)
-        }
-    }
-}
-
-impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.selcx.tcx()
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: ty::Binder<'tcx, T>,
-    ) -> ty::Binder<'tcx, T> {
-        self.universes.push(None);
-        let t = t.super_fold_with(self);
-        self.universes.pop();
-        t
-    }
-
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if !needs_normalization(&ty, self.param_env.reveal()) {
-            return ty;
-        }
-
-        let (kind, data) = match *ty.kind() {
-            ty::Alias(kind, alias_ty) => (kind, alias_ty),
-            _ => return ty.super_fold_with(self),
-        };
-
-        // We try to be a little clever here as a performance optimization in
-        // cases where there are nested projections under binders.
-        // For example:
-        // ```
-        // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>)
-        // ```
-        // We normalize the args on the projection before the projecting, but
-        // if we're naive, we'll
-        //   replace bound vars on inner, project inner, replace placeholders on inner,
-        //   replace bound vars on outer, project outer, replace placeholders on outer
-        //
-        // However, if we're a bit more clever, we can replace the bound vars
-        // on the entire type before normalizing nested projections, meaning we
-        //   replace bound vars on outer, project inner,
-        //   project outer, replace placeholders on outer
-        //
-        // This is possible because the inner `'a` will already be a placeholder
-        // when we need to normalize the inner projection
-        //
-        // On the other hand, this does add a bit of complexity, since we only
-        // replace bound vars if the current type is a `Projection` and we need
-        // to make sure we don't forget to fold the args regardless.
-
-        match kind {
-            ty::Opaque => {
-                // Only normalize `impl Trait` outside of type inference, usually in codegen.
-                match self.param_env.reveal() {
-                    Reveal::UserFacing => ty.super_fold_with(self),
-
-                    Reveal::All => {
-                        let recursion_limit = self.interner().recursion_limit();
-                        if !recursion_limit.value_within_limit(self.depth) {
-                            self.selcx.infcx.err_ctxt().report_overflow_error(
-                                &ty,
-                                self.cause.span,
-                                true,
-                                |_| {},
-                            );
-                        }
-
-                        let args = data.args.fold_with(self);
-                        let generic_ty = self.interner().type_of(data.def_id);
-                        let concrete_ty = generic_ty.instantiate(self.interner(), args);
-                        self.depth += 1;
-                        let folded_ty = self.fold_ty(concrete_ty);
-                        self.depth -= 1;
-                        folded_ty
-                    }
-                }
-            }
-
-            ty::Projection if !data.has_escaping_bound_vars() => {
-                // This branch is *mostly* just an optimization: when we don't
-                // have escaping bound vars, we don't need to replace them with
-                // placeholders (see branch below). *Also*, we know that we can
-                // register an obligation to *later* project, since we know
-                // there won't be bound vars there.
-                let data = data.fold_with(self);
-                let normalized_ty = if self.eager_inference_replacement {
-                    normalize_projection_type(
-                        self.selcx,
-                        self.param_env,
-                        data,
-                        self.cause.clone(),
-                        self.depth,
-                        self.obligations,
-                    )
-                } else {
-                    opt_normalize_projection_type(
-                        self.selcx,
-                        self.param_env,
-                        data,
-                        self.cause.clone(),
-                        self.depth,
-                        self.obligations,
-                    )
-                    .ok()
-                    .flatten()
-                    .unwrap_or_else(|| ty.super_fold_with(self).into())
-                };
-                debug!(
-                    ?self.depth,
-                    ?ty,
-                    ?normalized_ty,
-                    obligations.len = ?self.obligations.len(),
-                    "AssocTypeNormalizer: normalized type"
-                );
-                normalized_ty.ty().unwrap()
-            }
-
-            ty::Projection => {
-                // If there are escaping bound vars, we temporarily replace the
-                // bound vars with placeholders. Note though, that in the case
-                // that we still can't project for whatever reason (e.g. self
-                // type isn't known enough), we *can't* register an obligation
-                // and return an inference variable (since then that obligation
-                // would have bound vars and that's a can of worms). Instead,
-                // we just give up and fall back to pretending like we never tried!
-                //
-                // Note: this isn't necessarily the final approach here; we may
-                // want to figure out how to register obligations with escaping vars
-                // or handle this some other way.
-
-                let infcx = self.selcx.infcx;
-                let (data, mapped_regions, mapped_types, mapped_consts) =
-                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
-                let data = data.fold_with(self);
-                let normalized_ty = opt_normalize_projection_type(
-                    self.selcx,
-                    self.param_env,
-                    data,
-                    self.cause.clone(),
-                    self.depth,
-                    self.obligations,
-                )
-                .ok()
-                .flatten()
-                .map(|term| term.ty().unwrap())
-                .map(|normalized_ty| {
-                    PlaceholderReplacer::replace_placeholders(
-                        infcx,
-                        mapped_regions,
-                        mapped_types,
-                        mapped_consts,
-                        &self.universes,
-                        normalized_ty,
-                    )
-                })
-                .unwrap_or_else(|| ty.super_fold_with(self));
-
-                debug!(
-                    ?self.depth,
-                    ?ty,
-                    ?normalized_ty,
-                    obligations.len = ?self.obligations.len(),
-                    "AssocTypeNormalizer: normalized type"
-                );
-                normalized_ty
-            }
-            ty::Weak => {
-                let recursion_limit = self.interner().recursion_limit();
-                if !recursion_limit.value_within_limit(self.depth) {
-                    self.selcx.infcx.err_ctxt().report_overflow_error(
-                        &ty,
-                        self.cause.span,
-                        false,
-                        |diag| {
-                            diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow);
-                        },
-                    );
-                }
-
-                let infcx = self.selcx.infcx;
-                self.obligations.extend(
-                    infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map(
-                        |(mut predicate, span)| {
-                            if data.has_escaping_bound_vars() {
-                                (predicate, ..) = BoundVarReplacer::replace_bound_vars(
-                                    infcx,
-                                    &mut self.universes,
-                                    predicate,
-                                );
-                            }
-                            let mut cause = self.cause.clone();
-                            cause.map_code(|code| {
-                                ObligationCauseCode::TypeAlias(code, span, data.def_id)
-                            });
-                            Obligation::new(infcx.tcx, cause, self.param_env, predicate)
-                        },
-                    ),
-                );
-                self.depth += 1;
-                let res = infcx
-                    .tcx
-                    .type_of(data.def_id)
-                    .instantiate(infcx.tcx, data.args)
-                    .fold_with(self);
-                self.depth -= 1;
-                res
-            }
-
-            ty::Inherent if !data.has_escaping_bound_vars() => {
-                // This branch is *mostly* just an optimization: when we don't
-                // have escaping bound vars, we don't need to replace them with
-                // placeholders (see branch below). *Also*, we know that we can
-                // register an obligation to *later* project, since we know
-                // there won't be bound vars there.
-
-                let data = data.fold_with(self);
-
-                // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement`
-                // here like `ty::Projection`?
-                normalize_inherent_projection(
-                    self.selcx,
-                    self.param_env,
-                    data,
-                    self.cause.clone(),
-                    self.depth,
-                    self.obligations,
-                )
-            }
-
-            ty::Inherent => {
-                let infcx = self.selcx.infcx;
-                let (data, mapped_regions, mapped_types, mapped_consts) =
-                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
-                let data = data.fold_with(self);
-                let ty = normalize_inherent_projection(
-                    self.selcx,
-                    self.param_env,
-                    data,
-                    self.cause.clone(),
-                    self.depth,
-                    self.obligations,
-                );
-
-                PlaceholderReplacer::replace_placeholders(
-                    infcx,
-                    mapped_regions,
-                    mapped_types,
-                    mapped_consts,
-                    &self.universes,
-                    ty,
-                )
-            }
-        }
-    }
-
-    #[instrument(skip(self), level = "debug")]
-    fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        let tcx = self.selcx.tcx();
-        if tcx.features().generic_const_exprs
-            || !needs_normalization(&constant, self.param_env.reveal())
-        {
-            constant
-        } else {
-            let constant = constant.super_fold_with(self);
-            debug!(?constant, ?self.param_env);
-            with_replaced_escaping_bound_vars(
-                self.selcx.infcx,
-                &mut self.universes,
-                constant,
-                |constant| constant.normalize(tcx, self.param_env),
-            )
-        }
-    }
-
-    #[inline]
-    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
-        if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
-            p.super_fold_with(self)
-        } else {
-            p
-        }
-    }
-}
-
-pub struct BoundVarReplacer<'me, 'tcx> {
-    infcx: &'me InferCtxt<'tcx>,
-    // These three maps track the bound variable that were replaced by placeholders. It might be
-    // nice to remove these since we already have the `kind` in the placeholder; we really just need
-    // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
-    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
-    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-    // The current depth relative to *this* folding, *not* the entire normalization. In other words,
-    // the depth of binders we've passed here.
-    current_index: ty::DebruijnIndex,
-    // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
-    // we don't actually create a universe until we see a bound var we have to replace.
-    universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
-}
-
-/// Executes `f` on `value` after replacing all escaping bound variables with placeholders
-/// and then replaces these placeholders with the original bound variables in the result.
-///
-/// In most places, bound variables should be replaced right when entering a binder, making
-/// this function unnecessary. However, normalization currently does not do that, so we have
-/// to do this lazily.
-///
-/// You should not add any additional uses of this function, at least not without first
-/// discussing it with t-types.
-///
-/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during
-/// normalization as well, at which point this function will be unnecessary and can be removed.
-pub fn with_replaced_escaping_bound_vars<
-    'a,
-    'tcx,
-    T: TypeFoldable<TyCtxt<'tcx>>,
-    R: TypeFoldable<TyCtxt<'tcx>>,
->(
-    infcx: &'a InferCtxt<'tcx>,
-    universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
-    value: T,
-    f: impl FnOnce(T) -> R,
-) -> R {
-    if value.has_escaping_bound_vars() {
-        let (value, mapped_regions, mapped_types, mapped_consts) =
-            BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value);
-        let result = f(value);
-        PlaceholderReplacer::replace_placeholders(
-            infcx,
-            mapped_regions,
-            mapped_types,
-            mapped_consts,
-            universe_indices,
-            result,
-        )
-    } else {
-        f(value)
-    }
-}
-
-impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
-    /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
-    /// use a binding level above `universe_indices.len()`, we fail.
-    pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
-        infcx: &'me InferCtxt<'tcx>,
-        universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
-        value: T,
-    ) -> (
-        T,
-        BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
-        BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-        BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-    ) {
-        let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new();
-        let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new();
-        let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new();
-
-        let mut replacer = BoundVarReplacer {
-            infcx,
-            mapped_regions,
-            mapped_types,
-            mapped_consts,
-            current_index: ty::INNERMOST,
-            universe_indices,
-        };
-
-        let value = value.fold_with(&mut replacer);
-
-        (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
-    }
-
-    fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
-        let infcx = self.infcx;
-        let index =
-            self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1;
-        let universe = self.universe_indices[index].unwrap_or_else(|| {
-            for i in self.universe_indices.iter_mut().take(index + 1) {
-                *i = i.or_else(|| Some(infcx.create_next_universe()))
-            }
-            self.universe_indices[index].unwrap()
-        });
-        universe
-    }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.infcx.tcx
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: ty::Binder<'tcx, T>,
-    ) -> ty::Binder<'tcx, T> {
-        self.current_index.shift_in(1);
-        let t = t.super_fold_with(self);
-        self.current_index.shift_out(1);
-        t
-    }
-
-    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
-        match *r {
-            ty::ReBound(debruijn, _)
-                if debruijn.as_usize()
-                    >= self.current_index.as_usize() + self.universe_indices.len() =>
-            {
-                bug!(
-                    "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}",
-                    self.universe_indices
-                );
-            }
-            ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
-                let universe = self.universe_for(debruijn);
-                let p = ty::PlaceholderRegion { universe, bound: br };
-                self.mapped_regions.insert(p, br);
-                ty::Region::new_placeholder(self.infcx.tcx, p)
-            }
-            _ => r,
-        }
-    }
-
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match *t.kind() {
-            ty::Bound(debruijn, _)
-                if debruijn.as_usize() + 1
-                    > self.current_index.as_usize() + self.universe_indices.len() =>
-            {
-                bug!(
-                    "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}",
-                    self.universe_indices
-                );
-            }
-            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
-                let universe = self.universe_for(debruijn);
-                let p = ty::PlaceholderType { universe, bound: bound_ty };
-                self.mapped_types.insert(p, bound_ty);
-                Ty::new_placeholder(self.infcx.tcx, p)
-            }
-            _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
-            _ => t,
-        }
-    }
-
-    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        match ct.kind() {
-            ty::ConstKind::Bound(debruijn, _)
-                if debruijn.as_usize() + 1
-                    > self.current_index.as_usize() + self.universe_indices.len() =>
-            {
-                bug!(
-                    "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}",
-                    self.universe_indices
-                );
-            }
-            ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
-                let universe = self.universe_for(debruijn);
-                let p = ty::PlaceholderConst { universe, bound: bound_const };
-                self.mapped_consts.insert(p, bound_const);
-                ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty())
-            }
-            _ => ct.super_fold_with(self),
-        }
-    }
-
-    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
-        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
-    }
-}
-
-/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
-pub struct PlaceholderReplacer<'me, 'tcx> {
-    infcx: &'me InferCtxt<'tcx>,
-    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
-    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-    universe_indices: &'me [Option<ty::UniverseIndex>],
-    current_index: ty::DebruijnIndex,
-}
-
-impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
-    pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>(
-        infcx: &'me InferCtxt<'tcx>,
-        mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
-        mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-        mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-        universe_indices: &'me [Option<ty::UniverseIndex>],
-        value: T,
-    ) -> T {
-        let mut replacer = PlaceholderReplacer {
-            infcx,
-            mapped_regions,
-            mapped_types,
-            mapped_consts,
-            universe_indices,
-            current_index: ty::INNERMOST,
-        };
-        value.fold_with(&mut replacer)
-    }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.infcx.tcx
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: ty::Binder<'tcx, T>,
-    ) -> ty::Binder<'tcx, T> {
-        if !t.has_placeholders() && !t.has_infer() {
-            return t;
-        }
-        self.current_index.shift_in(1);
-        let t = t.super_fold_with(self);
-        self.current_index.shift_out(1);
-        t
-    }
-
-    fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
-        let r1 = match *r0 {
-            ty::ReVar(vid) => self
-                .infcx
-                .inner
-                .borrow_mut()
-                .unwrap_region_constraints()
-                .opportunistic_resolve_var(self.infcx.tcx, vid),
-            _ => r0,
-        };
-
-        let r2 = match *r1 {
-            ty::RePlaceholder(p) => {
-                let replace_var = self.mapped_regions.get(&p);
-                match replace_var {
-                    Some(replace_var) => {
-                        let index = self
-                            .universe_indices
-                            .iter()
-                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
-                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
-                        let db = ty::DebruijnIndex::from_usize(
-                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
-                        );
-                        ty::Region::new_bound(self.interner(), db, *replace_var)
-                    }
-                    None => r1,
-                }
-            }
-            _ => r1,
-        };
-
-        debug!(?r0, ?r1, ?r2, "fold_region");
-
-        r2
-    }
-
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let ty = self.infcx.shallow_resolve(ty);
-        match *ty.kind() {
-            ty::Placeholder(p) => {
-                let replace_var = self.mapped_types.get(&p);
-                match replace_var {
-                    Some(replace_var) => {
-                        let index = self
-                            .universe_indices
-                            .iter()
-                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
-                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
-                        let db = ty::DebruijnIndex::from_usize(
-                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
-                        );
-                        Ty::new_bound(self.infcx.tcx, db, *replace_var)
-                    }
-                    None => {
-                        if ty.has_infer() {
-                            ty.super_fold_with(self)
-                        } else {
-                            ty
-                        }
-                    }
-                }
-            }
-
-            _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self),
-            _ => ty,
-        }
-    }
-
-    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        let ct = self.infcx.shallow_resolve(ct);
-        if let ty::ConstKind::Placeholder(p) = ct.kind() {
-            let replace_var = self.mapped_consts.get(&p);
-            match replace_var {
-                Some(replace_var) => {
-                    let index = self
-                        .universe_indices
-                        .iter()
-                        .position(|u| matches!(u, Some(pu) if *pu == p.universe))
-                        .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
-                    let db = ty::DebruijnIndex::from_usize(
-                        self.universe_indices.len() - index + self.current_index.as_usize() - 1,
-                    );
-                    ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty())
-                }
-                None => {
-                    if ct.has_infer() {
-                        ct.super_fold_with(self)
-                    } else {
-                        ct
-                    }
-                }
-            }
-        } else {
-            ct.super_fold_with(self)
-        }
-    }
-}
-
 /// The guts of `normalize`: normalize a specific projection like `<T
 /// as Trait>::Item`. The result is always a type (and possibly
 /// additional obligations). If ambiguity arises, which implies that
@@ -1146,7 +329,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>(
 /// function takes an obligations vector and appends to it directly, which is
 /// slightly uglier but avoids the need for an extra short-lived allocation.
 #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
-fn opt_normalize_projection_type<'a, 'b, 'tcx>(
+pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     projection_ty: ty::AliasTy<'tcx>,
@@ -1250,14 +433,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term);
 
             let mut result = if projected_term.has_projections() {
-                let mut normalizer = AssocTypeNormalizer::new(
+                let normalized_ty = normalize_with_depth_to(
                     selcx,
                     param_env,
                     cause,
                     depth + 1,
+                    projected_term,
                     &mut projected_obligations,
                 );
-                let normalized_ty = normalizer.fold(projected_term);
 
                 Normalized { value: normalized_ty, obligations: projected_obligations }
             } else {
@@ -1462,11 +645,9 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
     match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) {
         Ok(mut ok) => obligations.append(&mut ok.obligations),
         Err(_) => {
-            tcx.dcx().span_delayed_bug(
+            tcx.dcx().span_bug(
                 cause.span,
-                format!(
-                    "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not"
-                ),
+                format!("{self_ty:?} was equal to {impl_ty:?} during selection but now it is not"),
             );
         }
     }
@@ -2011,7 +1192,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                     obligation.cause.span,
                     format!("Cannot project an associated type from `{impl_source:?}`"),
                 );
-                return Err(());
+                return Err(())
             }
         };
 
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 0f6c0abd280..70fd0b7e50b 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -1,12 +1,13 @@
 //! Code for the 'normalization' query. This consists of a wrapper
 //! which folds deeply, invoking the underlying
-//! `normalize_projection_ty` query when it encounters projections.
+//! `normalize_canonicalized_projection_ty` query when it encounters projections.
 
 use crate::infer::at::At;
 use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::{InferCtxt, InferOk};
 use crate::traits::error_reporting::TypeErrCtxtExt;
-use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+use crate::traits::normalize::needs_normalization;
+use crate::traits::{BoundVarReplacer, PlaceholderReplacer};
 use crate::traits::{ObligationCause, PredicateObligation, Reveal};
 use rustc_data_structures::sso::SsoHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -270,9 +271,9 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                 debug!("QueryNormalizer: c_data = {:#?}", c_data);
                 debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
                 let result = match kind {
-                    ty::Projection => tcx.normalize_projection_ty(c_data),
-                    ty::Weak => tcx.normalize_weak_ty(c_data),
-                    ty::Inherent => tcx.normalize_inherent_projection_ty(c_data),
+                    ty::Projection => tcx.normalize_canonicalized_projection_ty(c_data),
+                    ty::Weak => tcx.normalize_canonicalized_weak_ty(c_data),
+                    ty::Inherent => tcx.normalize_canonicalized_inherent_projection_ty(c_data),
                     kind => unreachable!("did not expect {kind:?} due to match arm above"),
                 }?;
                 // We don't expect ambiguity.
@@ -307,10 +308,10 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                 } else {
                     result.normalized_ty
                 };
-                // `tcx.normalize_projection_ty` may normalize to a type that still has
-                // unevaluated consts, so keep normalizing here if that's the case.
-                // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type
-                // and we need to continue folding it to reveal the TAIT behind it.
+                // `tcx.normalize_canonicalized_projection_ty` may normalize to a type that
+                // still has unevaluated consts, so keep normalizing here if that's the case.
+                // Similarly, `tcx.normalize_canonicalized_weak_ty` will only unwrap one layer
+                // of type and we need to continue folding it to reveal the TAIT behind it.
                 if res != ty
                     && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak)
                 {
@@ -335,7 +336,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
 
         let constant = constant.try_super_fold_with(self)?;
         debug!(?constant, ?self.param_env);
-        Ok(crate::traits::project::with_replaced_escaping_bound_vars(
+        Ok(crate::traits::with_replaced_escaping_bound_vars(
             self.infcx,
             &mut self.universes,
             constant,
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index fb09f094d37..12ee778ee05 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -190,10 +190,9 @@ where
                 }
             }
             if !progress {
-                return Err(infcx.dcx().span_delayed_bug(
-                    span,
-                    format!("ambiguity processing {obligations:?} from {self:?}"),
-                ));
+                infcx
+                    .dcx()
+                    .span_bug(span, format!("ambiguity processing {obligations:?} from {self:?}"));
             }
         }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index e4a70c537d2..f76be876948 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -18,7 +18,7 @@ use rustc_middle::ty::{
 };
 use rustc_span::def_id::DefId;
 
-use crate::traits::project::{normalize_with_depth, normalize_with_depth_to};
+use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
 use crate::traits::util::{self, closure_trait_ref_and_return_type};
 use crate::traits::vtable::{
     count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset,
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 5bcf46a96ed..1146f869fc1 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -8,7 +8,6 @@ use self::SelectionCandidate::*;
 use super::coherence::{self, Conflict};
 use super::const_evaluatable;
 use super::project;
-use super::project::normalize_with_depth_to;
 use super::project::ProjectionTyObligation;
 use super::util;
 use super::util::closure_trait_ref_and_return_type;
@@ -22,14 +21,15 @@ use super::{
 use crate::infer::{InferCtxt, InferOk, TypeFreshener};
 use crate::solve::InferCtxtSelectExt;
 use crate::traits::error_reporting::TypeErrCtxtExt;
-use crate::traits::project::try_normalize_with_depth_to;
+use crate::traits::normalize::normalize_with_depth;
+use crate::traits::normalize::normalize_with_depth_to;
 use crate::traits::project::ProjectAndUnifyResult;
 use crate::traits::project::ProjectionCacheKeyExt;
 use crate::traits::ProjectionCacheKey;
 use crate::traits::Unimplemented;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::Diagnostic;
+use rustc_errors::{DiagnosticBuilder, EmissionGuarantee};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::BoundRegionConversionTime;
@@ -70,7 +70,10 @@ pub enum IntercrateAmbiguityCause<'tcx> {
 impl<'tcx> IntercrateAmbiguityCause<'tcx> {
     /// Emits notes when the overlap is caused by complex intercrate ambiguities.
     /// See #23980 for details.
-    pub fn add_intercrate_ambiguity_hint(&self, err: &mut Diagnostic) {
+    pub fn add_intercrate_ambiguity_hint<G: EmissionGuarantee>(
+        &self,
+        err: &mut DiagnosticBuilder<'_, G>,
+    ) {
         err.note(self.intercrate_ambiguity_hint());
     }
 
@@ -1070,7 +1073,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 && fresh_trait_pred.is_global()
             {
                 let mut nested_obligations = Vec::new();
-                let predicate = try_normalize_with_depth_to(
+                let predicate = normalize_with_depth_to(
                     this,
                     param_env,
                     obligation.cause.clone(),
@@ -1662,7 +1665,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| {
-            project::normalize_with_depth(
+            normalize_with_depth(
                 self,
                 obligation.param_env,
                 obligation.cause.clone(),
@@ -1718,7 +1721,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         );
         let infer_projection = if potentially_unnormalized_candidates {
             ensure_sufficient_stack(|| {
-                project::normalize_with_depth_to(
+                normalize_with_depth_to(
                     self,
                     obligation.param_env,
                     obligation.cause.clone(),
@@ -2383,7 +2386,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 let placeholder_ty = self.infcx.enter_forall_and_leak_universe(ty);
                 let Normalized { value: normalized_ty, mut obligations } =
                     ensure_sufficient_stack(|| {
-                        project::normalize_with_depth(
+                        normalize_with_depth(
                             self,
                             param_env,
                             cause.clone(),
@@ -2480,7 +2483,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
 
         let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } =
             ensure_sufficient_stack(|| {
-                project::normalize_with_depth(
+                normalize_with_depth(
                     self,
                     obligation.param_env,
                     obligation.cause.clone(),
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index e1a49ecf1b6..56bc2f2cf25 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -20,7 +20,7 @@ use crate::traits::{
     self, coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt,
 };
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{codes::*, DelayDm, Diagnostic};
+use rustc_errors::{codes::*, DelayDm, DiagnosticBuilder, EmissionGuarantee};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt};
 use rustc_middle::ty::{GenericArgs, GenericArgsRef};
@@ -395,11 +395,11 @@ fn report_conflicting_impls<'tcx>(
     // Work to be done after we've built the DiagnosticBuilder. We have to define it
     // now because the lint emit methods don't return back the DiagnosticBuilder
     // that's passed in.
-    fn decorate<'tcx>(
+    fn decorate<'tcx, G: EmissionGuarantee>(
         tcx: TyCtxt<'tcx>,
         overlap: &OverlapError<'tcx>,
         impl_span: Span,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
     ) {
         if (overlap.trait_ref, overlap.self_ty).references_error() {
             err.downgrade_to_delayed_bug();
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index af172eb0713..d66c4004ef5 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -1,11 +1,14 @@
+use std::collections::BTreeMap;
+
 use super::NormalizeExt;
 use super::{ObligationCause, PredicateObligation, SelectionContext};
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::Diagnostic;
+use rustc_errors::DiagnosticBuilder;
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::InferOk;
+use rustc_infer::infer::{InferCtxt, InferOk};
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_span::Span;
 use smallvec::SmallVec;
 
@@ -43,7 +46,7 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> {
     /// trait aliases.
     pub fn label_with_exp_info(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         top_label: &'static str,
         use_desc: &str,
     ) {
@@ -382,3 +385,336 @@ pub fn check_args_compatible<'tcx>(
     let args = &args[0..generics.count().min(args.len())];
     check_args_compatible_inner(tcx, generics, args)
 }
+
+/// Executes `f` on `value` after replacing all escaping bound variables with placeholders
+/// and then replaces these placeholders with the original bound variables in the result.
+///
+/// In most places, bound variables should be replaced right when entering a binder, making
+/// this function unnecessary. However, normalization currently does not do that, so we have
+/// to do this lazily.
+///
+/// You should not add any additional uses of this function, at least not without first
+/// discussing it with t-types.
+///
+/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during
+/// normalization as well, at which point this function will be unnecessary and can be removed.
+pub fn with_replaced_escaping_bound_vars<
+    'a,
+    'tcx,
+    T: TypeFoldable<TyCtxt<'tcx>>,
+    R: TypeFoldable<TyCtxt<'tcx>>,
+>(
+    infcx: &'a InferCtxt<'tcx>,
+    universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
+    value: T,
+    f: impl FnOnce(T) -> R,
+) -> R {
+    if value.has_escaping_bound_vars() {
+        let (value, mapped_regions, mapped_types, mapped_consts) =
+            BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value);
+        let result = f(value);
+        PlaceholderReplacer::replace_placeholders(
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            universe_indices,
+            result,
+        )
+    } else {
+        f(value)
+    }
+}
+
+pub struct BoundVarReplacer<'me, 'tcx> {
+    infcx: &'me InferCtxt<'tcx>,
+    // These three maps track the bound variable that were replaced by placeholders. It might be
+    // nice to remove these since we already have the `kind` in the placeholder; we really just need
+    // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
+    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    // The current depth relative to *this* folding, *not* the entire normalization. In other words,
+    // the depth of binders we've passed here.
+    current_index: ty::DebruijnIndex,
+    // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
+    // we don't actually create a universe until we see a bound var we have to replace.
+    universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+}
+
+impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
+    /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
+    /// use a binding level above `universe_indices.len()`, we fail.
+    pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
+        infcx: &'me InferCtxt<'tcx>,
+        universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+        value: T,
+    ) -> (
+        T,
+        BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+        BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+        BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    ) {
+        let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new();
+        let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new();
+        let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new();
+
+        let mut replacer = BoundVarReplacer {
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            current_index: ty::INNERMOST,
+            universe_indices,
+        };
+
+        let value = value.fold_with(&mut replacer);
+
+        (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
+    }
+
+    fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
+        let infcx = self.infcx;
+        let index =
+            self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1;
+        let universe = self.universe_indices[index].unwrap_or_else(|| {
+            for i in self.universe_indices.iter_mut().take(index + 1) {
+                *i = i.or_else(|| Some(infcx.create_next_universe()))
+            }
+            self.universe_indices[index].unwrap()
+        });
+        universe
+    }
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReBound(debruijn, _)
+                if debruijn.as_usize()
+                    >= self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!(
+                    "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderRegion { universe, bound: br };
+                self.mapped_regions.insert(p, br);
+                ty::Region::new_placeholder(self.infcx.tcx, p)
+            }
+            _ => r,
+        }
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match *t.kind() {
+            ty::Bound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!(
+                    "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderType { universe, bound: bound_ty };
+                self.mapped_types.insert(p, bound_ty);
+                Ty::new_placeholder(self.infcx.tcx, p)
+            }
+            _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
+            _ => t,
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        match ct.kind() {
+            ty::ConstKind::Bound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!(
+                    "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderConst { universe, bound: bound_const };
+                self.mapped_consts.insert(p, bound_const);
+                ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty())
+            }
+            _ => ct.super_fold_with(self),
+        }
+    }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
+}
+
+/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
+pub struct PlaceholderReplacer<'me, 'tcx> {
+    infcx: &'me InferCtxt<'tcx>,
+    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    universe_indices: &'me [Option<ty::UniverseIndex>],
+    current_index: ty::DebruijnIndex,
+}
+
+impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
+    pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>(
+        infcx: &'me InferCtxt<'tcx>,
+        mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+        mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+        mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+        universe_indices: &'me [Option<ty::UniverseIndex>],
+        value: T,
+    ) -> T {
+        let mut replacer = PlaceholderReplacer {
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            universe_indices,
+            current_index: ty::INNERMOST,
+        };
+        value.fold_with(&mut replacer)
+    }
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        if !t.has_placeholders() && !t.has_infer() {
+            return t;
+        }
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        let r1 = match *r0 {
+            ty::ReVar(vid) => self
+                .infcx
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
+                .opportunistic_resolve_var(self.infcx.tcx, vid),
+            _ => r0,
+        };
+
+        let r2 = match *r1 {
+            ty::RePlaceholder(p) => {
+                let replace_var = self.mapped_regions.get(&p);
+                match replace_var {
+                    Some(replace_var) => {
+                        let index = self
+                            .universe_indices
+                            .iter()
+                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                        let db = ty::DebruijnIndex::from_usize(
+                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                        );
+                        ty::Region::new_bound(self.interner(), db, *replace_var)
+                    }
+                    None => r1,
+                }
+            }
+            _ => r1,
+        };
+
+        debug!(?r0, ?r1, ?r2, "fold_region");
+
+        r2
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let ty = self.infcx.shallow_resolve(ty);
+        match *ty.kind() {
+            ty::Placeholder(p) => {
+                let replace_var = self.mapped_types.get(&p);
+                match replace_var {
+                    Some(replace_var) => {
+                        let index = self
+                            .universe_indices
+                            .iter()
+                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                        let db = ty::DebruijnIndex::from_usize(
+                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                        );
+                        Ty::new_bound(self.infcx.tcx, db, *replace_var)
+                    }
+                    None => {
+                        if ty.has_infer() {
+                            ty.super_fold_with(self)
+                        } else {
+                            ty
+                        }
+                    }
+                }
+            }
+
+            _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self),
+            _ => ty,
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        let ct = self.infcx.shallow_resolve(ct);
+        if let ty::ConstKind::Placeholder(p) = ct.kind() {
+            let replace_var = self.mapped_consts.get(&p);
+            match replace_var {
+                Some(replace_var) => {
+                    let index = self
+                        .universe_indices
+                        .iter()
+                        .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                        .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                    let db = ty::DebruijnIndex::from_usize(
+                        self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                    );
+                    ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty())
+                }
+                None => {
+                    if ct.has_infer() {
+                        ct.super_fold_with(self)
+                    } else {
+                        ct
+                    }
+                }
+            }
+        } else {
+            ct.super_fold_with(self)
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index c727ef53d55..15059bc6613 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -313,7 +313,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
             // Don't normalize the whole obligation, the param env is either
             // already normalized, or we're currently normalizing the
             // param_env. Either way we should only normalize the predicate.
-            let normalized_predicate = traits::project::normalize_with_depth_to(
+            let normalized_predicate = traits::normalize::normalize_with_depth_to(
                 &mut selcx,
                 param_env,
                 cause.clone(),