about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs21
-rw-r--r--compiler/rustc_infer/src/infer/relate/higher_ranked.rs42
-rw-r--r--compiler/rustc_infer/src/infer/relate/nll.rs28
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs181
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs53
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs18
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs184
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs87
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs53
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs146
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs132
-rw-r--r--compiler/rustc_type_ir/src/region_kind.rs2
15 files changed, 520 insertions, 442 deletions
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 2d8b47a9cc0..82690b9acbe 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1032,10 +1032,10 @@ impl<'tcx> InferCtxt<'tcx> {
             _ => {}
         }
 
-        let ty::SubtypePredicate { a_is_expected, a, b } =
-            self.instantiate_binder_with_placeholders(predicate);
-
-        Ok(self.at(cause, param_env).sub_exp(DefineOpaqueTypes::No, a_is_expected, a, b))
+        // FIXME(tree_universes): leaking universes
+        self.enter_forall(predicate, |ty::SubtypePredicate { a_is_expected, a, b }| {
+            Ok(self.at(cause, param_env).sub_exp(DefineOpaqueTypes::No, a_is_expected, a, b))
+        })
     }
 
     pub fn region_outlives_predicate(
@@ -1043,10 +1043,13 @@ impl<'tcx> InferCtxt<'tcx> {
         cause: &traits::ObligationCause<'tcx>,
         predicate: ty::PolyRegionOutlivesPredicate<'tcx>,
     ) {
-        let ty::OutlivesPredicate(r_a, r_b) = self.instantiate_binder_with_placeholders(predicate);
-        let origin =
-            SubregionOrigin::from_obligation_cause(cause, || RelateRegionParamBound(cause.span));
-        self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
+        // FIXME(tree_universes): leaking universes
+        self.enter_forall(predicate, |ty::OutlivesPredicate(r_a, r_b)| {
+            let origin = SubregionOrigin::from_obligation_cause(cause, || {
+                RelateRegionParamBound(cause.span)
+            });
+            self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
+        })
     }
 
     /// Number of type variables created so far.
@@ -1455,7 +1458,7 @@ impl<'tcx> InferCtxt<'tcx> {
     // Use this method if you'd like to find some substitution of the binder's
     // variables (e.g. during a method call). If there isn't a [`BoundRegionConversionTime`]
     // that corresponds to your use case, consider whether or not you should
-    // use [`InferCtxt::instantiate_binder_with_placeholders`] instead.
+    // use [`InferCtxt::enter_forall`] instead.
     pub fn instantiate_binder_with_fresh_vars<T>(
         &self,
         span: Span,
diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
index 440df8c8936..cff544ed154 100644
--- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
+++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
@@ -38,24 +38,24 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> {
         // First, we instantiate each bound region in the supertype with a
         // fresh placeholder region. Note that this automatically creates
         // a new universe if needed.
-        let sup_prime = self.infcx.instantiate_binder_with_placeholders(sup);
+        self.infcx.enter_forall(sup, |sup_prime| {
+            // Next, we instantiate each bound region in the subtype
+            // with a fresh region variable. These region variables --
+            // but no other preexisting region variables -- can name
+            // the placeholders.
+            let sub_prime =
+                self.infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, sub);
+            debug!("a_prime={:?}", sub_prime);
+            debug!("b_prime={:?}", sup_prime);
 
-        // Next, we instantiate each bound region in the subtype
-        // with a fresh region variable. These region variables --
-        // but no other preexisting region variables -- can name
-        // the placeholders.
-        let sub_prime = self.infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, sub);
-
-        debug!("a_prime={:?}", sub_prime);
-        debug!("b_prime={:?}", sup_prime);
-
-        // Compare types now that bound regions have been replaced.
-        let result = self.sub(sub_is_expected).relate(sub_prime, sup_prime)?;
-
-        debug!("OK result={result:?}");
-        // NOTE: returning the result here would be dangerous as it contains
-        // placeholders which **must not** be named afterwards.
-        Ok(())
+            // Compare types now that bound regions have been replaced.
+            // FIXME(tree_universes): leaked dead universes
+            let result = self.sub(sub_is_expected).relate(sub_prime, sup_prime);
+            if result.is_ok() {
+                debug!("OK result={result:?}");
+            }
+            result.map(|_| ())
+        })
     }
 }
 
@@ -106,6 +106,14 @@ impl<'tcx> InferCtxt<'tcx> {
         self.tcx.replace_bound_vars_uncached(binder, delegate)
     }
 
+    pub fn enter_forall<T, U>(&self, forall: ty::Binder<'tcx, T>, f: impl FnOnce(T) -> U) -> U
+    where
+        T: TypeFoldable<TyCtxt<'tcx>> + Copy,
+    {
+        let value = self.instantiate_binder_with_placeholders(forall);
+        f(value)
+    }
+
     /// See [RegionConstraintCollector::leak_check][1]. We only check placeholder
     /// leaking into `outer_universe`, i.e. placeholders which cannot be named by that
     /// universe.
diff --git a/compiler/rustc_infer/src/infer/relate/nll.rs b/compiler/rustc_infer/src/infer/relate/nll.rs
index 8b80646a386..7e93ab995db 100644
--- a/compiler/rustc_infer/src/infer/relate/nll.rs
+++ b/compiler/rustc_infer/src/infer/relate/nll.rs
@@ -309,6 +309,18 @@ where
         replaced
     }
 
+    fn enter_forall<T, U>(
+        &mut self,
+        binder: ty::Binder<'tcx, T>,
+        f: impl FnOnce(&mut Self, T) -> U,
+    ) -> U
+    where
+        T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy,
+    {
+        let value = self.instantiate_binder_with_placeholders(binder);
+        f(self, value)
+    }
+
     #[instrument(skip(self), level = "debug")]
     fn instantiate_binder_with_existentials<T>(&mut self, binder: ty::Binder<'tcx, T>) -> T
     where
@@ -630,10 +642,10 @@ where
 
             // Note: the order here is important. Create the placeholders first, otherwise
             // we assign the wrong universe to the existential!
-            let b_replaced = self.instantiate_binder_with_placeholders(b);
-            let a_replaced = self.instantiate_binder_with_existentials(a);
-
-            self.relate(a_replaced, b_replaced)?;
+            self.enter_forall(b, |this, b| {
+                let a = this.instantiate_binder_with_existentials(a);
+                this.relate(a, b)
+            })?;
 
             self.ambient_variance = variance;
         }
@@ -650,10 +662,10 @@ where
             let variance =
                 std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant);
 
-            let a_replaced = self.instantiate_binder_with_placeholders(a);
-            let b_replaced = self.instantiate_binder_with_existentials(b);
-
-            self.relate(a_replaced, b_replaced)?;
+            self.enter_forall(a, |this, a| {
+                let b = this.instantiate_binder_with_existentials(b);
+                this.relate(a, b)
+            })?;
 
             self.ambient_variance = variance;
         }
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index bed5d3c80c0..cf825be7a55 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -77,101 +77,104 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
         for (pred, pred_span) in
             cx.tcx.explicit_item_bounds(def_id).instantiate_identity_iter_copied()
         {
-            let predicate = infcx.instantiate_binder_with_placeholders(pred.kind());
-            let ty::ClauseKind::Projection(proj) = predicate else {
-                continue;
-            };
-            // Only check types, since those are the only things that may
-            // have opaques in them anyways.
-            let Some(proj_term) = proj.term.ty() else { continue };
+            infcx.enter_forall(pred.kind(), |predicate| {
+                let ty::ClauseKind::Projection(proj) = predicate else {
+                    return;
+                };
+                // Only check types, since those are the only things that may
+                // have opaques in them anyways.
+                let Some(proj_term) = proj.term.ty() else { return };
 
-            // HACK: `impl Trait<Assoc = impl Trait2>` from an RPIT is "ok"...
-            if let ty::Alias(ty::Opaque, opaque_ty) = *proj_term.kind()
-                && cx.tcx.parent(opaque_ty.def_id) == def_id
-                && matches!(
-                    opaque.origin,
-                    hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_)
-                )
-            {
-                continue;
-            }
+                // HACK: `impl Trait<Assoc = impl Trait2>` from an RPIT is "ok"...
+                if let ty::Alias(ty::Opaque, opaque_ty) = *proj_term.kind()
+                    && cx.tcx.parent(opaque_ty.def_id) == def_id
+                    && matches!(
+                        opaque.origin,
+                        hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_)
+                    )
+                {
+                    return;
+                }
 
-            // HACK: `async fn() -> Self` in traits is "ok"...
-            // This is not really that great, but it's similar to why the `-> Self`
-            // return type is well-formed in traits even when `Self` isn't sized.
-            if let ty::Param(param_ty) = *proj_term.kind()
-                && param_ty.name == kw::SelfUpper
-                && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(_))
-                && opaque.in_trait
-            {
-                continue;
-            }
+                // HACK: `async fn() -> Self` in traits is "ok"...
+                // This is not really that great, but it's similar to why the `-> Self`
+                // return type is well-formed in traits even when `Self` isn't sized.
+                if let ty::Param(param_ty) = *proj_term.kind()
+                    && param_ty.name == kw::SelfUpper
+                    && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(_))
+                    && opaque.in_trait
+                {
+                    return;
+                }
 
-            let proj_ty =
-                Ty::new_projection(cx.tcx, proj.projection_ty.def_id, proj.projection_ty.args);
-            // For every instance of the projection type in the bounds,
-            // replace them with the term we're assigning to the associated
-            // type in our opaque type.
-            let proj_replacer = &mut BottomUpFolder {
-                tcx: cx.tcx,
-                ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
-                lt_op: |lt| lt,
-                ct_op: |ct| ct,
-            };
-            // For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
-            // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
-            // with `impl Send: OtherTrait`.
-            for (assoc_pred, assoc_pred_span) in cx
-                .tcx
-                .explicit_item_bounds(proj.projection_ty.def_id)
-                .iter_instantiated_copied(cx.tcx, proj.projection_ty.args)
-            {
-                let assoc_pred = assoc_pred.fold_with(proj_replacer);
-                let Ok(assoc_pred) = traits::fully_normalize(
-                    infcx,
-                    traits::ObligationCause::dummy(),
-                    cx.param_env,
-                    assoc_pred,
-                ) else {
-                    continue;
+                let proj_ty =
+                    Ty::new_projection(cx.tcx, proj.projection_ty.def_id, proj.projection_ty.args);
+                // For every instance of the projection type in the bounds,
+                // replace them with the term we're assigning to the associated
+                // type in our opaque type.
+                let proj_replacer = &mut BottomUpFolder {
+                    tcx: cx.tcx,
+                    ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
+                    lt_op: |lt| lt,
+                    ct_op: |ct| ct,
                 };
-                // If that predicate doesn't hold modulo regions (but passed during type-check),
-                // then we must've taken advantage of the hack in `project_and_unify_types` where
-                // we replace opaques with inference vars. Emit a warning!
-                if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new(
-                    cx.tcx,
-                    traits::ObligationCause::dummy(),
-                    cx.param_env,
-                    assoc_pred,
-                )) {
-                    // If it's a trait bound and an opaque that doesn't satisfy it,
-                    // then we can emit a suggestion to add the bound.
-                    let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
-                        (
-                            ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }),
-                            ty::ClauseKind::Trait(trait_pred),
-                        ) => Some(AddBound {
-                            suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
-                            trait_ref: trait_pred.print_modifiers_and_trait_path(),
-                        }),
-                        _ => None,
+                // For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
+                // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
+                // with `impl Send: OtherTrait`.
+                for (assoc_pred, assoc_pred_span) in cx
+                    .tcx
+                    .explicit_item_bounds(proj.projection_ty.def_id)
+                    .iter_instantiated_copied(cx.tcx, proj.projection_ty.args)
+                {
+                    let assoc_pred = assoc_pred.fold_with(proj_replacer);
+                    let Ok(assoc_pred) = traits::fully_normalize(
+                        infcx,
+                        traits::ObligationCause::dummy(),
+                        cx.param_env,
+                        assoc_pred,
+                    ) else {
+                        continue;
                     };
-                    cx.emit_span_lint(
-                        OPAQUE_HIDDEN_INFERRED_BOUND,
-                        pred_span,
-                        OpaqueHiddenInferredBoundLint {
-                            ty: Ty::new_opaque(
-                                cx.tcx,
-                                def_id,
-                                ty::GenericArgs::identity_for_item(cx.tcx, def_id),
-                            ),
-                            proj_ty: proj_term,
-                            assoc_pred_span,
-                            add_bound,
-                        },
-                    );
+
+                    // If that predicate doesn't hold modulo regions (but passed during type-check),
+                    // then we must've taken advantage of the hack in `project_and_unify_types` where
+                    // we replace opaques with inference vars. Emit a warning!
+                    if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new(
+                        cx.tcx,
+                        traits::ObligationCause::dummy(),
+                        cx.param_env,
+                        assoc_pred,
+                    )) {
+                        // If it's a trait bound and an opaque that doesn't satisfy it,
+                        // then we can emit a suggestion to add the bound.
+                        let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
+                            (
+                                ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }),
+                                ty::ClauseKind::Trait(trait_pred),
+                            ) => Some(AddBound {
+                                suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
+                                trait_ref: trait_pred.print_modifiers_and_trait_path(),
+                            }),
+                            _ => None,
+                        };
+
+                        cx.emit_span_lint(
+                            OPAQUE_HIDDEN_INFERRED_BOUND,
+                            pred_span,
+                            OpaqueHiddenInferredBoundLint {
+                                ty: Ty::new_opaque(
+                                    cx.tcx,
+                                    def_id,
+                                    ty::GenericArgs::identity_for_item(cx.tcx, def_id),
+                                ),
+                                proj_ty: proj_term,
+                                assoc_pred_span,
+                                add_bound,
+                            },
+                        );
+                    }
                 }
-            }
+            });
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 927924452f9..8c734fca9b6 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1052,6 +1052,7 @@ impl<'tcx, T> Binder<'tcx, T> {
     where
         T: TypeVisitable<TyCtxt<'tcx>>,
     {
+        // `self.value` is equivalent to `self.skip_binder()`
         if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) }
     }
 
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index d02578c4846..daa4977a1e0 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -20,7 +20,7 @@ use crate::solve::EvalCtxt;
 pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
     ecx: &EvalCtxt<'_, 'tcx>,
     ty: Ty<'tcx>,
-) -> Result<Vec<Ty<'tcx>>, NoSolution> {
+) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
     let tcx = ecx.tcx();
     match *ty.kind() {
         ty::Uint(_)
@@ -34,7 +34,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
         | ty::Char => Ok(vec![]),
 
         // Treat `str` like it's defined as `struct str([u8]);`
-        ty::Str => Ok(vec![Ty::new_slice(tcx, tcx.types.u8)]),
+        ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, tcx.types.u8))]),
 
         ty::Dynamic(..)
         | ty::Param(..)
@@ -47,46 +47,46 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
         }
 
         ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => {
-            Ok(vec![element_ty])
+            Ok(vec![ty::Binder::dummy(element_ty)])
         }
 
-        ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]),
+        ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![ty::Binder::dummy(element_ty)]),
 
         ty::Tuple(tys) => {
             // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
-            Ok(tys.iter().collect())
+            Ok(tys.iter().map(ty::Binder::dummy).collect())
         }
 
-        ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]),
+        ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
 
         ty::CoroutineClosure(_, args) => Ok(vec![args.as_coroutine_closure().tupled_upvars_ty()]),
 
         ty::Coroutine(_, args) => {
             let coroutine_args = args.as_coroutine();
-            Ok(vec![coroutine_args.tupled_upvars_ty(), coroutine_args.witness()])
+            Ok(vec![
+                ty::Binder::dummy(coroutine_args.tupled_upvars_ty()),
+                ty::Binder::dummy(coroutine_args.witness()),
+            ])
         }
 
         ty::CoroutineWitness(def_id, args) => Ok(ecx
             .tcx()
             .coroutine_hidden_types(def_id)
-            .map(|bty| {
-                ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars(
-                    tcx,
-                    bty.instantiate(tcx, args),
-                ))
-            })
+            .map(|bty| replace_erased_lifetimes_with_bound_vars(tcx, bty.instantiate(tcx, args)))
             .collect()),
 
         // For `PhantomData<T>`, we pass `T`.
-        ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![args.type_at(0)]),
+        ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]),
 
-        ty::Adt(def, args) => Ok(def.all_fields().map(|f| f.ty(tcx, args)).collect()),
+        ty::Adt(def, args) => {
+            Ok(def.all_fields().map(|f| ty::Binder::dummy(f.ty(tcx, args))).collect())
+        }
 
         ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
             // We can resolve the `impl Trait` to its concrete type,
             // which enforces a DAG between the functions requiring
             // the auto trait bounds in question.
-            Ok(vec![tcx.type_of(def_id).instantiate(tcx, args)])
+            Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, args))])
         }
     }
 }
@@ -116,7 +116,7 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>(
 pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
     ecx: &EvalCtxt<'_, 'tcx>,
     ty: Ty<'tcx>,
-) -> Result<Vec<Ty<'tcx>>, NoSolution> {
+) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
     match *ty.kind() {
         ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
         | ty::Uint(_)
@@ -150,11 +150,11 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
             bug!("unexpected type `{ty}`")
         }
 
-        ty::Tuple(tys) => Ok(tys.to_vec()),
+        ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
 
         ty::Adt(def, args) => {
             let sized_crit = def.sized_constraint(ecx.tcx());
-            Ok(sized_crit.iter_instantiated(ecx.tcx(), args).collect())
+            Ok(sized_crit.iter_instantiated(ecx.tcx(), args).map(ty::Binder::dummy).collect())
         }
     }
 }
@@ -163,7 +163,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
 pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
     ecx: &EvalCtxt<'_, 'tcx>,
     ty: Ty<'tcx>,
-) -> Result<Vec<Ty<'tcx>>, NoSolution> {
+) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
     match *ty.kind() {
         ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]),
 
@@ -194,9 +194,9 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
             bug!("unexpected type `{ty}`")
         }
 
-        ty::Tuple(tys) => Ok(tys.to_vec()),
+        ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
 
-        ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]),
+        ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
 
         ty::CoroutineClosure(..) => Err(NoSolution),
 
@@ -205,7 +205,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
             Movability::Movable => {
                 if ecx.tcx().features().coroutine_clone {
                     let coroutine = args.as_coroutine();
-                    Ok(vec![coroutine.tupled_upvars_ty(), coroutine.witness()])
+                    Ok(vec![
+                        ty::Binder::dummy(coroutine.tupled_upvars_ty()),
+                        ty::Binder::dummy(coroutine.witness()),
+                    ])
                 } else {
                     Err(NoSolution)
                 }
@@ -216,10 +219,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
             .tcx()
             .coroutine_hidden_types(def_id)
             .map(|bty| {
-                ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars(
+                replace_erased_lifetimes_with_bound_vars(
                     ecx.tcx(),
                     bty.instantiate(ecx.tcx(), args),
-                ))
+                )
             })
             .collect()),
     }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
index 76c50a11102..a1c55091c26 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -477,10 +477,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                 }
             }
         } else {
-            let kind = self.infcx.instantiate_binder_with_placeholders(kind);
-            let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
-            self.add_goal(GoalSource::Misc, goal);
-            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            self.infcx.enter_forall(kind, |kind| {
+                let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
+                self.add_goal(GoalSource::Misc, goal);
+                // FIXME(tree_universes): leaking universes
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            })
         }
     }
 
@@ -801,13 +803,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         )
     }
 
-    pub(super) fn instantiate_binder_with_placeholders<T: TypeFoldable<TyCtxt<'tcx>> + Copy>(
+    pub(super) fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>(
         &self,
         value: ty::Binder<'tcx, T>,
-    ) -> T {
-        self.infcx.instantiate_binder_with_placeholders(value)
+        f: impl FnOnce(T) -> U,
+    ) -> U {
+        self.infcx.enter_forall(value, f)
     }
-
     pub(super) fn resolve_vars_if_possible<T>(&self, value: T) -> T
     where
         T: TypeFoldable<TyCtxt<'tcx>>,
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
index 315df06be41..2706b5c8e0d 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -31,103 +31,109 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
     ) -> SelectionResult<'tcx, Selection<'tcx>> {
         assert!(self.next_trait_solver());
 
-        let trait_goal = Goal::new(
-            self.tcx,
-            obligation.param_env,
-            self.instantiate_binder_with_placeholders(obligation.predicate),
-        );
-
-        let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::Never, |ecx| {
-            let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
-            let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
-            let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
-
-            // pseudo-winnow
-            if candidates.len() == 0 {
-                return Err(SelectionError::Unimplemented);
-            } else if candidates.len() > 1 {
-                let mut i = 0;
-                while i < candidates.len() {
-                    let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
-                        candidate_should_be_dropped_in_favor_of(
-                            ecx.tcx(),
-                            &candidates[i],
-                            &candidates[j],
-                        )
-                    });
-                    if should_drop_i {
-                        candidates.swap_remove(i);
-                    } else {
-                        i += 1;
-                        if i > 1 {
-                            return Ok(None);
+        // FIXME(tree_universes): leaking universes?
+        self.enter_forall(obligation.predicate, |pred| {
+            let trait_goal = Goal::new(self.tcx, obligation.param_env, pred);
+
+            let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::Never, |ecx| {
+                let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
+                let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
+                let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
+
+                // pseudo-winnow
+                if candidates.len() == 0 {
+                    return Err(SelectionError::Unimplemented);
+                } else if candidates.len() > 1 {
+                    let mut i = 0;
+                    while i < candidates.len() {
+                        let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
+                            candidate_should_be_dropped_in_favor_of(
+                                ecx.tcx(),
+                                &candidates[i],
+                                &candidates[j],
+                            )
+                        });
+                        if should_drop_i {
+                            candidates.swap_remove(i);
+                        } else {
+                            i += 1;
+                            if i > 1 {
+                                return Ok(None);
+                            }
                         }
                     }
                 }
-            }
 
-            let candidate = candidates.pop().unwrap();
-            let (certainty, nested_goals) = ecx
-                .instantiate_and_apply_query_response(
-                    trait_goal.param_env,
-                    orig_values,
-                    candidate.result,
-                )
-                .map_err(|_| SelectionError::Unimplemented)?;
-
-            Ok(Some((candidate, certainty, nested_goals)))
-        });
-
-        let (candidate, certainty, nested_goals) = match result {
-            Ok(Some((candidate, certainty, nested_goals))) => (candidate, certainty, nested_goals),
-            Ok(None) => return Ok(None),
-            Err(e) => return Err(e),
-        };
-
-        let nested_obligations: Vec<_> = nested_goals
-            .into_iter()
-            .map(|goal| {
-                Obligation::new(self.tcx, ObligationCause::dummy(), goal.param_env, goal.predicate)
-            })
-            .collect();
-
-        let goal = self.resolve_vars_if_possible(trait_goal);
-        match (certainty, candidate.source) {
-            // Rematching the implementation will instantiate the same nested goals that
-            // would have caused the ambiguity, so we can still make progress here regardless.
-            (_, CandidateSource::Impl(def_id)) => {
-                rematch_impl(self, goal, def_id, nested_obligations)
-            }
+                let candidate = candidates.pop().unwrap();
+                let (certainty, nested_goals) = ecx
+                    .instantiate_and_apply_query_response(
+                        trait_goal.param_env,
+                        orig_values,
+                        candidate.result,
+                    )
+                    .map_err(|_| SelectionError::Unimplemented)?;
+
+                Ok(Some((candidate, certainty, nested_goals)))
+            });
+
+            let (candidate, certainty, nested_goals) = match result {
+                Ok(Some((candidate, certainty, nested_goals))) => {
+                    (candidate, certainty, nested_goals)
+                }
+                Ok(None) => return Ok(None),
+                Err(e) => return Err(e),
+            };
+
+            let nested_obligations: Vec<_> = nested_goals
+                .into_iter()
+                .map(|goal| {
+                    Obligation::new(
+                        self.tcx,
+                        ObligationCause::dummy(),
+                        goal.param_env,
+                        goal.predicate,
+                    )
+                })
+                .collect();
+
+            let goal = self.resolve_vars_if_possible(trait_goal);
+            match (certainty, candidate.source) {
+                // Rematching the implementation will instantiate the same nested goals that
+                // would have caused the ambiguity, so we can still make progress here regardless.
+                (_, CandidateSource::Impl(def_id)) => {
+                    rematch_impl(self, goal, def_id, nested_obligations)
+                }
 
-            // If an unsize goal is ambiguous, then we can manually rematch it to make
-            // selection progress for coercion during HIR typeck. If it is *not* ambiguous,
-            // but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals,
-            // and we need to rematch those to detect tuple unsizing and trait upcasting.
-            // FIXME: This will be wrong if we have param-env or where-clause bounds
-            // with the unsize goal -- we may need to mark those with different impl
-            // sources.
-            (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
-            | (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc))
-                if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
-            {
-                rematch_unsize(self, goal, nested_obligations, src, certainty)
-            }
+                // If an unsize goal is ambiguous, then we can manually rematch it to make
+                // selection progress for coercion during HIR typeck. If it is *not* ambiguous,
+                // but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals,
+                // and we need to rematch those to detect tuple unsizing and trait upcasting.
+                // FIXME: This will be wrong if we have param-env or where-clause bounds
+                // with the unsize goal -- we may need to mark those with different impl
+                // sources.
+                (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
+                | (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc))
+                    if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
+                {
+                    rematch_unsize(self, goal, nested_obligations, src, certainty)
+                }
 
-            // Technically some builtin impls have nested obligations, but if
-            // `Certainty::Yes`, then they should've all been verified and don't
-            // need re-checking.
-            (Certainty::Yes, CandidateSource::BuiltinImpl(src)) => {
-                Ok(Some(ImplSource::Builtin(src, nested_obligations)))
-            }
+                // Technically some builtin impls have nested obligations, but if
+                // `Certainty::Yes`, then they should've all been verified and don't
+                // need re-checking.
+                (Certainty::Yes, CandidateSource::BuiltinImpl(src)) => {
+                    Ok(Some(ImplSource::Builtin(src, nested_obligations)))
+                }
 
-            // It's fine not to do anything to rematch these, since there are no
-            // nested obligations.
-            (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
-                Ok(Some(ImplSource::Param(nested_obligations)))
-            }
+                // It's fine not to do anything to rematch these, since there are no
+                // nested obligations.
+                (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
+                    Ok(Some(ImplSource::Param(nested_obligations)))
+                }
 
-            (Certainty::Maybe(_), _) => Ok(None),
-        }
+                (Certainty::Maybe(_), _) => Ok(None),
+            }
+        })
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index fd09a6b671d..53307da1d9b 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -1049,14 +1049,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     fn probe_and_evaluate_goal_for_constituent_tys(
         &mut self,
         goal: Goal<'tcx, TraitPredicate<'tcx>>,
-        constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
+        constituent_tys: impl Fn(
+            &EvalCtxt<'_, 'tcx>,
+            Ty<'tcx>,
+        ) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution>,
     ) -> QueryResult<'tcx> {
         self.probe_misc_candidate("constituent tys").enter(|ecx| {
             ecx.add_goals(
                 GoalSource::ImplWhereBound,
                 constituent_tys(ecx, goal.predicate.self_ty())?
                     .into_iter()
-                    .map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty)))
+                    .map(|ty| {
+                        // FIXME(tree_universes): leaking universes
+                        ecx.enter_forall(ty, |ty| {
+                            goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty))
+                        })
+                    })
                     .collect::<Vec<_>>(),
             );
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 4b20de26219..cbe9a238b1c 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -971,7 +971,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
 
         let Goal { param_env, predicate } = goal.goal();
 
-        // For bound predicates we simply call `infcx.instantiate_binder_with_placeholders`
+        // For bound predicates we simply call `infcx.enter_forall`
         // and then prove the resulting predicate as a nested goal.
         let trait_ref = match predicate.kind().no_bound_vars() {
             Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
index b246e476bed..c6f5af08013 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
@@ -21,51 +21,62 @@ pub fn recompute_applicable_impls<'tcx>(
 
     let impl_may_apply = |impl_def_id| {
         let ocx = ObligationCtxt::new(infcx);
-        let placeholder_obligation =
-            infcx.instantiate_binder_with_placeholders(obligation.predicate);
-        let obligation_trait_ref =
-            ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
-
-        let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
-        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
-        let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
-
-        if let Err(_) =
-            ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
-        {
-            return false;
-        }
+        infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
+            let obligation_trait_ref = ocx.normalize(
+                &ObligationCause::dummy(),
+                param_env,
+                placeholder_obligation.trait_ref,
+            );
+
+            let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
+            let impl_trait_ref =
+                tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
+            let impl_trait_ref =
+                ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
+
+            if let Err(_) =
+                ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
+            {
+                return false;
+            }
 
-        let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args);
-        ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
-            Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
-        }));
+            let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args);
+            ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
+                Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
+            }));
 
-        ocx.select_where_possible().is_empty()
+            ocx.select_where_possible().is_empty()
+        })
     };
 
     let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
         let ocx = ObligationCtxt::new(infcx);
-        let placeholder_obligation =
-            infcx.instantiate_binder_with_placeholders(obligation.predicate);
-        let obligation_trait_ref =
-            ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
-
-        let param_env_predicate = infcx.instantiate_binder_with_fresh_vars(
-            DUMMY_SP,
-            BoundRegionConversionTime::HigherRankedType,
-            poly_trait_predicate,
-        );
-        let param_env_trait_ref =
-            ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
-
-        if let Err(_) =
-            ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, param_env_trait_ref)
-        {
-            return false;
-        }
+        infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
+            let obligation_trait_ref = ocx.normalize(
+                &ObligationCause::dummy(),
+                param_env,
+                placeholder_obligation.trait_ref,
+            );
+
+            let param_env_predicate = infcx.instantiate_binder_with_fresh_vars(
+                DUMMY_SP,
+                BoundRegionConversionTime::HigherRankedType,
+                poly_trait_predicate,
+            );
+            let param_env_trait_ref =
+                ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
+
+            if let Err(_) = ocx.eq(
+                &ObligationCause::dummy(),
+                param_env,
+                obligation_trait_ref,
+                param_env_trait_ref,
+            ) {
+                return false;
+            }
 
-        ocx.select_where_possible().is_empty()
+            ocx.select_where_possible().is_empty()
+        })
     };
 
     let mut ambiguities = Vec::new();
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 972da180a33..ceff7187646 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -64,39 +64,44 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     ) -> Option<(DefId, GenericArgsRef<'tcx>)> {
         let tcx = self.tcx;
         let param_env = obligation.param_env;
-        let trait_ref = self.instantiate_binder_with_placeholders(trait_ref);
-        let trait_self_ty = trait_ref.self_ty();
+        self.enter_forall(trait_ref, |trait_ref| {
+            let trait_self_ty = trait_ref.self_ty();
 
-        let mut self_match_impls = vec![];
-        let mut fuzzy_match_impls = vec![];
+            let mut self_match_impls = vec![];
+            let mut fuzzy_match_impls = vec![];
 
-        self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
-            let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
-            let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
+            self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
+                let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
+                let impl_trait_ref =
+                    tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
 
-            let impl_self_ty = impl_trait_ref.self_ty();
+                let impl_self_ty = impl_trait_ref.self_ty();
 
-            if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
-                self_match_impls.push((def_id, impl_args));
+                if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
+                    self_match_impls.push((def_id, impl_args));
 
-                if iter::zip(trait_ref.args.types().skip(1), impl_trait_ref.args.types().skip(1))
+                    if iter::zip(
+                        trait_ref.args.types().skip(1),
+                        impl_trait_ref.args.types().skip(1),
+                    )
                     .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
-                {
-                    fuzzy_match_impls.push((def_id, impl_args));
+                    {
+                        fuzzy_match_impls.push((def_id, impl_args));
+                    }
                 }
-            }
-        });
+            });
 
-        let impl_def_id_and_args = if self_match_impls.len() == 1 {
-            self_match_impls[0]
-        } else if fuzzy_match_impls.len() == 1 {
-            fuzzy_match_impls[0]
-        } else {
-            return None;
-        };
+            let impl_def_id_and_args = if self_match_impls.len() == 1 {
+                self_match_impls[0]
+            } else if fuzzy_match_impls.len() == 1 {
+                fuzzy_match_impls[0]
+            } else {
+                return None;
+            };
 
-        tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
-            .then_some(impl_def_id_and_args)
+            tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
+                .then_some(impl_def_id_and_args)
+        })
     }
 
     /// Used to set on_unimplemented's `ItemContext`
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 243f583aed8..046b2718dba 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1248,52 +1248,55 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
-        let ty = self.instantiate_binder_with_placeholders(self_ty);
-        let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else {
-            return false;
-        };
-        let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
-        let ty::Param(param) = inner_ty.kind() else { return false };
-        let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
-            obligation.cause.code()
-        else {
-            return false;
-        };
-        let arg_node = self.tcx.hir_node(*arg_hir_id);
-        let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else { return false };
+        self.enter_forall(self_ty, |ty: Ty<'_>| {
+            let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else {
+                return false;
+            };
+            let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
+            let ty::Param(param) = inner_ty.kind() else { return false };
+            let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
+                obligation.cause.code()
+            else {
+                return false;
+            };
+            let arg_node = self.tcx.hir_node(*arg_hir_id);
+            let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else {
+                return false;
+            };
 
-        let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None);
-        let has_clone = |ty| {
-            self.type_implements_trait(clone_trait, [ty], obligation.param_env)
-                .must_apply_modulo_regions()
-        };
+            let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None);
+            let has_clone = |ty| {
+                self.type_implements_trait(clone_trait, [ty], obligation.param_env)
+                    .must_apply_modulo_regions()
+            };
 
-        let new_obligation = self.mk_trait_obligation_with_new_self_ty(
-            obligation.param_env,
-            trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),
-        );
+            let new_obligation = self.mk_trait_obligation_with_new_self_ty(
+                obligation.param_env,
+                trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),
+            );
 
-        if self.predicate_may_hold(&new_obligation) && has_clone(ty) {
-            if !has_clone(param.to_ty(self.tcx)) {
-                suggest_constraining_type_param(
-                    self.tcx,
-                    generics,
-                    err,
-                    param.name.as_str(),
-                    "Clone",
-                    Some(clone_trait),
-                    None,
+            if self.predicate_may_hold(&new_obligation) && has_clone(ty) {
+                if !has_clone(param.to_ty(self.tcx)) {
+                    suggest_constraining_type_param(
+                        self.tcx,
+                        generics,
+                        err,
+                        param.name.as_str(),
+                        "Clone",
+                        Some(clone_trait),
+                        None,
+                    );
+                }
+                err.span_suggestion_verbose(
+                    obligation.cause.span.shrink_to_hi(),
+                    "consider using clone here",
+                    ".clone()".to_string(),
+                    Applicability::MaybeIncorrect,
                 );
+                return true;
             }
-            err.span_suggestion_verbose(
-                obligation.cause.span.shrink_to_hi(),
-                "consider using clone here",
-                ".clone()",
-                Applicability::MaybeIncorrect,
-            );
-            return true;
-        }
-        false
+            false
+        })
     }
 
     /// Extracts information about a callable type for diagnostics. This is a
@@ -4038,26 +4041,27 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 if let Some(where_pred) = where_pred.as_trait_clause()
                     && let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
                 {
-                    let where_pred = self.instantiate_binder_with_placeholders(where_pred);
-                    let failed_pred = self.instantiate_binder_with_fresh_vars(
-                        expr.span,
-                        BoundRegionConversionTime::FnCall,
-                        failed_pred,
-                    );
+                    self.enter_forall(where_pred, |where_pred| {
+                        let failed_pred = self.instantiate_binder_with_fresh_vars(
+                            expr.span,
+                            BoundRegionConversionTime::FnCall,
+                            failed_pred,
+                        );
 
-                    let zipped = iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args);
-                    for (expected, actual) in zipped {
-                        self.probe(|_| {
-                            match self.at(&ObligationCause::misc(expr.span, body_id), param_env).eq(
-                                DefineOpaqueTypes::No,
-                                expected,
-                                actual,
-                            ) {
-                                Ok(_) => (), // We ignore nested obligations here for now.
-                                Err(err) => type_diffs.push(err),
-                            }
-                        })
-                    }
+                        let zipped =
+                            iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args);
+                        for (expected, actual) in zipped {
+                            self.probe(|_| {
+                                match self
+                                    .at(&ObligationCause::misc(expr.span, body_id), param_env)
+                                    .eq(DefineOpaqueTypes::No, expected, actual)
+                                {
+                                    Ok(_) => (), // We ignore nested obligations here for now.
+                                    Err(err) => type_diffs.push(err),
+                                }
+                            })
+                        }
+                    })
                 } else if let Some(where_pred) = where_pred.as_projection_clause()
                     && let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
                     && let Some(found) = failed_pred.skip_binder().term.ty()
@@ -4615,14 +4619,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         {
             self.probe(|_| {
                 let ocx = ObligationCtxt::new(self);
-                let pred = self.instantiate_binder_with_placeholders(pred);
-                let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
-                ocx.register_obligation(Obligation::new(
-                    self.tcx,
-                    ObligationCause::dummy(),
-                    param_env,
-                    pred,
-                ));
+                self.enter_forall(pred, |pred| {
+                    let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
+                    // FIXME(tree_universes): universe leakage
+                    ocx.register_obligation(Obligation::new(
+                        self.tcx,
+                        ObligationCause::dummy(),
+                        param_env,
+                        pred,
+                    ));
+                });
                 if !ocx.select_where_possible().is_empty() {
                     // encountered errors.
                     return;
@@ -4769,13 +4775,13 @@ fn hint_missing_borrow<'tcx>(
     }
 
     let found_args = match found.kind() {
-        ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(),
+        ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()),
         kind => {
             span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind)
         }
     };
     let expected_args = match expected.kind() {
-        ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(),
+        ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()),
         kind => {
             span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind)
         }
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 4ef490758a3..0f253981930 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
@@ -1307,12 +1307,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         let mut pred = obligation.predicate.to_opt_poly_trait_pred();
         while let Some((next_code, next_pred)) = code.parent() {
             if let Some(pred) = pred {
-                let pred = self.instantiate_binder_with_placeholders(pred);
-                diag.note(format!(
-                    "`{}` must implement `{}`, but it does not",
-                    pred.self_ty(),
-                    pred.print_modifiers_and_trait_path()
-                ));
+                self.enter_forall(pred, |pred| {
+                    diag.note(format!(
+                        "`{}` must implement `{}`, but it does not",
+                        pred.self_ty(),
+                        pred.print_modifiers_and_trait_path()
+                    ));
+                })
             }
             code = next_code;
             pred = next_pred;
@@ -2017,70 +2018,79 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         if let [single] = &impl_candidates {
             if self.probe(|_| {
                 let ocx = ObligationCtxt::new(self);
-                let obligation_trait_ref = self.instantiate_binder_with_placeholders(trait_ref);
-                let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id);
-                let impl_trait_ref = ocx.normalize(
-                    &ObligationCause::dummy(),
-                    param_env,
-                    ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args),
-                );
 
-                ocx.register_obligations(
-                    self.tcx
-                        .predicates_of(single.impl_def_id)
-                        .instantiate(self.tcx, impl_args)
-                        .into_iter()
-                        .map(|(clause, _)| {
-                            Obligation::new(self.tcx, ObligationCause::dummy(), param_env, clause)
-                        }),
-                );
-                if !ocx.select_where_possible().is_empty() {
-                    return false;
-                }
+                self.enter_forall(trait_ref, |obligation_trait_ref| {
+                    let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id);
+                    let impl_trait_ref = ocx.normalize(
+                        &ObligationCause::dummy(),
+                        param_env,
+                        ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args),
+                    );
 
-                let mut terrs = vec![];
-                for (obligation_arg, impl_arg) in
-                    std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args)
-                {
-                    if let Err(terr) =
-                        ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg)
-                    {
-                        terrs.push(terr);
-                    }
+                    ocx.register_obligations(
+                        self.tcx
+                            .predicates_of(single.impl_def_id)
+                            .instantiate(self.tcx, impl_args)
+                            .into_iter()
+                            .map(|(clause, _)| {
+                                Obligation::new(
+                                    self.tcx,
+                                    ObligationCause::dummy(),
+                                    param_env,
+                                    clause,
+                                )
+                            }),
+                    );
                     if !ocx.select_where_possible().is_empty() {
                         return false;
                     }
-                }
 
-                // Literally nothing unified, just give up.
-                if terrs.len() == impl_trait_ref.args.len() {
-                    return false;
-                }
+                    let mut terrs = vec![];
+                    for (obligation_arg, impl_arg) in
+                        std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args)
+                    {
+                        // FIXME(tree_universes): universe leakage
+                        if let Err(terr) =
+                            ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg)
+                        {
+                            terrs.push(terr);
+                        }
+                        if !ocx.select_where_possible().is_empty() {
+                            return false;
+                        }
+                    }
 
-                let cand =
-                    self.resolve_vars_if_possible(impl_trait_ref).fold_with(&mut BottomUpFolder {
-                        tcx: self.tcx,
-                        ty_op: |ty| ty,
-                        lt_op: |lt| lt,
-                        ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()),
-                    });
-                err.highlighted_help(vec![
-                    StringPart::normal(format!("the trait `{}` ", cand.print_trait_sugared())),
-                    StringPart::highlighted("is"),
-                    StringPart::normal(" implemented for `"),
-                    StringPart::highlighted(cand.self_ty().to_string()),
-                    StringPart::normal("`"),
-                ]);
+                    // Literally nothing unified, just give up.
+                    if terrs.len() == impl_trait_ref.args.len() {
+                        return false;
+                    }
 
-                if let [TypeError::Sorts(exp_found)] = &terrs[..] {
-                    let exp_found = self.resolve_vars_if_possible(*exp_found);
-                    err.help(format!(
-                        "for that trait implementation, expected `{}`, found `{}`",
-                        exp_found.expected, exp_found.found
-                    ));
-                }
+                    let cand = self.resolve_vars_if_possible(impl_trait_ref).fold_with(
+                        &mut BottomUpFolder {
+                            tcx: self.tcx,
+                            ty_op: |ty| ty,
+                            lt_op: |lt| lt,
+                            ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()),
+                        },
+                    );
+                    err.highlighted_help(vec![
+                        StringPart::normal(format!("the trait `{}` ", cand.print_trait_sugared())),
+                        StringPart::highlighted("is"),
+                        StringPart::normal(" implemented for `"),
+                        StringPart::highlighted(cand.self_ty().to_string()),
+                        StringPart::normal("`"),
+                    ]);
+
+                    if let [TypeError::Sorts(exp_found)] = &terrs[..] {
+                        let exp_found = self.resolve_vars_if_possible(*exp_found);
+                        err.help(format!(
+                            "for that trait implementation, expected `{}`, found `{}`",
+                            exp_found.expected, exp_found.found
+                        ));
+                    }
 
-                true
+                    true
+                })
             }) {
                 return true;
             }
diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs
index 3b5e41e8d85..5e0f15a0d77 100644
--- a/compiler/rustc_type_ir/src/region_kind.rs
+++ b/compiler/rustc_type_ir/src/region_kind.rs
@@ -164,7 +164,7 @@ pub enum RegionKind<I: Interner> {
     /// Should not exist outside of type inference.
     ///
     /// Used when instantiating a `forall` binder via
-    /// `infcx.instantiate_binder_with_placeholders`.
+    /// `infcx.enter_forall` and `infcx.instantiate_binder_with_placeholders`.
     RePlaceholder(I::PlaceholderRegion),
 
     /// Erased region, used by trait selection, in MIR and during codegen.