about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection/src')
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs7
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs191
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs235
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs17
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs183
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs19
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs29
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs11
-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.rs188
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs157
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs427
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs200
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs165
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs104
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs2
22 files changed, 1281 insertions, 830 deletions
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 7894f8dd98f..20cd573f46e 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, SubdiagnosticMessage,
+    EmissionGuarantee, IntoDiagnostic, Level, SubdiagnosticMessageOp,
 };
 use rustc_macros::Diagnostic;
 use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty};
@@ -102,10 +102,7 @@ pub enum AdjustSignatureBorrow {
 }
 
 impl AddToDiagnostic for AdjustSignatureBorrow {
-    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
-    where
-        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
-    {
+    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
         match self {
             AdjustSignatureBorrow::Borrow { to_borrow } => {
                 diag.arg("len", to_borrow.len());
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 5ec45212bc7..00a2adccf64 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -14,6 +14,8 @@
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
 #![allow(internal_features)]
+#![allow(rustc::diagnostic_outside_of_impl)]
+#![allow(rustc::untranslatable_diagnostic)]
 #![feature(associated_type_bounds)]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
@@ -22,7 +24,7 @@
 #![feature(option_take_if)]
 #![feature(never_type)]
 #![feature(type_alias_impl_trait)]
-#![feature(min_specialization)]
+#![cfg_attr(bootstrap, feature(min_specialization))]
 #![recursion_limit = "512"] // For rustdoc
 
 #[macro_use]
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 7052fd776b0..81c0cfea85a 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -8,7 +8,7 @@ use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::Reveal;
 use rustc_middle::traits::solve::inspect::ProbeKind;
 use rustc_middle::traits::solve::{
-    CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult,
+    CandidateSource, CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult,
 };
 use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
@@ -276,25 +276,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         &mut self,
         goal: Goal<'tcx, G>,
     ) -> Vec<Candidate<'tcx>> {
-        let dummy_candidate = |this: &mut EvalCtxt<'_, 'tcx>, certainty| {
-            let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
-            let result = this.evaluate_added_goals_and_make_canonical_response(certainty).unwrap();
-            let mut dummy_probe = this.inspect.new_probe();
-            dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) });
-            this.inspect.finish_probe(dummy_probe);
-            vec![Candidate { source, result }]
-        };
-
         let Some(normalized_self_ty) =
             self.try_normalize_ty(goal.param_env, goal.predicate.self_ty())
         else {
             debug!("overflow while evaluating self type");
-            return dummy_candidate(self, Certainty::OVERFLOW);
+            return self.forced_ambiguity(MaybeCause::Overflow);
         };
 
         if normalized_self_ty.is_ty_var() {
             debug!("self type has been normalized to infer");
-            return dummy_candidate(self, Certainty::AMBIGUOUS);
+            return self.forced_ambiguity(MaybeCause::Ambiguity);
         }
 
         let goal =
@@ -315,11 +306,26 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
         self.assemble_param_env_candidates(goal, &mut candidates);
 
-        self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
+        match self.solver_mode() {
+            SolverMode::Normal => self.discard_impls_shadowed_by_env(goal, &mut candidates),
+            SolverMode::Coherence => {
+                self.assemble_coherence_unknowable_candidates(goal, &mut candidates)
+            }
+        }
 
         candidates
     }
 
+    fn forced_ambiguity(&mut self, cause: MaybeCause) -> Vec<Candidate<'tcx>> {
+        let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
+        let certainty = Certainty::Maybe(cause);
+        let result = self.evaluate_added_goals_and_make_canonical_response(certainty).unwrap();
+        let mut dummy_probe = self.inspect.new_probe();
+        dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) });
+        self.inspect.finish_probe(dummy_probe);
+        vec![Candidate { source, result }]
+    }
+
     #[instrument(level = "debug", skip_all)]
     fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>(
         &mut self,
@@ -542,7 +548,27 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         goal: Goal<'tcx, G>,
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
-        let alias_ty = match goal.predicate.self_ty().kind() {
+        let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
+            ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates);
+        });
+    }
+
+    /// For some deeply nested `<T>::A::B::C::D` rigid associated type,
+    /// we should explore the item bounds for all levels, since the
+    /// `associated_type_bounds` feature means that a parent associated
+    /// type may carry bounds for a nested associated type.
+    ///
+    /// If we have a projection, check that its self type is a rigid projection.
+    /// If so, continue searching by recursively calling after normalization.
+    // FIXME: This may recurse infinitely, but I can't seem to trigger it without
+    // hitting another overflow error something. Add a depth parameter needed later.
+    fn assemble_alias_bound_candidates_recur<G: GoalKind<'tcx>>(
+        &mut self,
+        self_ty: Ty<'tcx>,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let (kind, alias_ty) = match *self_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(_)
@@ -567,13 +593,27 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             | ty::Param(_)
             | ty::Placeholder(..)
             | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
-            | ty::Alias(ty::Inherent, _)
-            | ty::Alias(ty::Weak, _)
             | ty::Error(_) => return,
-            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
-            | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
-            // Excluding IATs and type aliases here as they don't have meaningful item bounds.
-            ty::Alias(ty::Projection | ty::Opaque, alias_ty) => alias_ty,
+            ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) | ty::Bound(..) => {
+                bug!("unexpected self type for `{goal:?}`")
+            }
+
+            ty::Infer(ty::TyVar(_)) => {
+                // If we hit infer when normalizing the self type of an alias,
+                // then bail with ambiguity. We should never encounter this on
+                // the *first* iteration of this recursive function.
+                if let Ok(result) =
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+                {
+                    candidates.push(Candidate { source: CandidateSource::AliasBound, result });
+                }
+                return;
+            }
+
+            ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty),
+            ty::Alias(ty::Inherent | ty::Weak, _) => {
+                unreachable!("Weak and Inherent aliases should have been normalized away already")
+            }
         };
 
         for assumption in
@@ -581,9 +621,28 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         {
             match G::consider_alias_bound_candidate(self, goal, assumption) {
                 Ok(result) => {
-                    candidates.push(Candidate { source: CandidateSource::AliasBound, result })
+                    candidates.push(Candidate { source: CandidateSource::AliasBound, result });
+                }
+                Err(NoSolution) => {}
+            }
+        }
+
+        if kind != ty::Projection {
+            return;
+        }
+
+        match self.try_normalize_ty(goal.param_env, alias_ty.self_ty()) {
+            // Recurse on the self type of the projection.
+            Some(next_self_ty) => {
+                self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates);
+            }
+            // Bail if we overflow when normalizing, adding an ambiguous candidate.
+            None => {
+                if let Ok(result) =
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
+                {
+                    candidates.push(Candidate { source: CandidateSource::AliasBound, result });
                 }
-                Err(NoSolution) => (),
             }
         }
     }
@@ -779,6 +838,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
+    /// In coherence we have to not only care about all impls we know about, but
+    /// also consider impls which may get added in a downstream or sibling crate
+    /// or which an upstream impl may add in a minor release.
+    ///
+    /// To do so we add an ambiguous candidate in case such an unknown impl could
+    /// apply to the current goal.
     #[instrument(level = "debug", skip_all)]
     fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>(
         &mut self,
@@ -786,11 +851,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
         let tcx = self.tcx();
-        match self.solver_mode() {
-            SolverMode::Normal => return,
-            SolverMode::Coherence => {}
-        };
-
         let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
             let trait_ref = goal.predicate.trait_ref(tcx);
             #[derive(Debug)]
@@ -820,46 +880,65 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
+    /// If there's a where-bound for the current goal, do not use any impl candidates
+    /// to prove the current goal. Most importantly, if there is a where-bound which does
+    /// not specify any associated types, we do not allow normalizing the associated type
+    /// by using an impl, even if it would apply.
+    ///
+    ///  <https://github.com/rust-lang/trait-system-refactor-initiative/issues/76>
+    // FIXME(@lcnr): The current structure here makes me unhappy and feels ugly. idk how
+    // to improve this however. However, this should make it fairly straightforward to refine
+    // the filtering going forward, so it seems alright-ish for now.
+    fn discard_impls_shadowed_by_env<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> =
+            goal.with(tcx, goal.predicate.trait_ref(tcx));
+        let mut trait_candidates_from_env = Vec::new();
+        self.assemble_param_env_candidates(trait_goal, &mut trait_candidates_from_env);
+        self.assemble_alias_bound_candidates(trait_goal, &mut trait_candidates_from_env);
+        if !trait_candidates_from_env.is_empty() {
+            let trait_env_result = self.merge_candidates(trait_candidates_from_env);
+            match trait_env_result.unwrap().value.certainty {
+                // If proving the trait goal succeeds by using the env,
+                // we freely drop all impl candidates.
+                //
+                // FIXME(@lcnr): It feels like this could easily hide
+                // a forced ambiguity candidate added earlier.
+                // This feels dangerous.
+                Certainty::Yes => {
+                    candidates.retain(|c| match c.source {
+                        CandidateSource::Impl(_) | CandidateSource::BuiltinImpl(_) => false,
+                        CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true,
+                    });
+                }
+                // If it is still ambiguous we instead just force the whole goal
+                // to be ambig and wait for inference constraints. See
+                // tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs
+                Certainty::Maybe(cause) => {
+                    *candidates = self.forced_ambiguity(cause);
+                }
+            }
+        }
+    }
+
     /// If there are multiple ways to prove a trait or projection goal, we have
     /// to somehow try to merge the candidates into one. If that fails, we return
     /// ambiguity.
     #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn merge_candidates(
         &mut self,
-        mut candidates: Vec<Candidate<'tcx>>,
+        candidates: Vec<Candidate<'tcx>>,
     ) -> QueryResult<'tcx> {
         // First try merging all candidates. This is complete and fully sound.
         let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>();
         if let Some(result) = self.try_merge_responses(&responses) {
             return Ok(result);
+        } else {
+            self.flounder(&responses)
         }
-
-        // We then check whether we should prioritize `ParamEnv` candidates.
-        //
-        // Doing so is incomplete and would therefore be unsound during coherence.
-        match self.solver_mode() {
-            SolverMode::Coherence => (),
-            // Prioritize `ParamEnv` candidates only if they do not guide inference.
-            //
-            // This is still incomplete as we may add incorrect region bounds.
-            SolverMode::Normal => {
-                let param_env_responses = candidates
-                    .iter()
-                    .filter(|c| {
-                        matches!(
-                            c.source,
-                            CandidateSource::ParamEnv(_) | CandidateSource::AliasBound
-                        )
-                    })
-                    .map(|c| c.result)
-                    .collect::<Vec<_>>();
-                if let Some(result) = self.try_merge_responses(&param_env_responses) {
-                    // We strongly prefer alias and param-env bounds here, even if they affect inference.
-                    // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/11.
-                    return Ok(result);
-                }
-            }
-        }
-        self.flounder(&responses)
     }
 }
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..819b070cf8b 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,48 @@ 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::CoroutineClosure(_, args) => {
+            Ok(vec![ty::Binder::dummy(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 +118,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 +152,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 +165,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 +196,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 +207,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 +221,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()),
     }
@@ -318,34 +323,27 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
     self_ty: Ty<'tcx>,
     goal_kind: ty::ClosureKind,
     env_region: ty::Region<'tcx>,
-) -> Result<
-    (ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Option<ty::Predicate<'tcx>>),
-    NoSolution,
-> {
+) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec<ty::Predicate<'tcx>>), NoSolution>
+{
     match *self_ty.kind() {
         ty::CoroutineClosure(def_id, args) => {
             let args = args.as_coroutine_closure();
             let kind_ty = args.kind_ty();
-
-            if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
+            let sig = args.coroutine_closure_sig().skip_binder();
+            let mut nested = vec![];
+            let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
                 if !closure_kind.extends(goal_kind) {
                     return Err(NoSolution);
                 }
-                Ok((
-                    args.coroutine_closure_sig().map_bound(|sig| {
-                        let coroutine_ty = sig.to_coroutine_given_kind_and_upvars(
-                            tcx,
-                            args.parent_args(),
-                            tcx.coroutine_for_closure(def_id),
-                            goal_kind,
-                            env_region,
-                            args.tupled_upvars_ty(),
-                            args.coroutine_captures_by_ref_ty(),
-                        );
-                        (sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
-                    }),
-                    None,
-                ))
+                sig.to_coroutine_given_kind_and_upvars(
+                    tcx,
+                    args.parent_args(),
+                    tcx.coroutine_for_closure(def_id),
+                    goal_kind,
+                    env_region,
+                    args.tupled_upvars_ty(),
+                    args.coroutine_captures_by_ref_ty(),
+                )
             } else {
                 let async_fn_kind_trait_def_id =
                     tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
@@ -362,42 +360,117 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
                 // the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
                 // will project to the right upvars for the generator, appending the inputs and
                 // coroutine upvars respecting the closure kind.
-                Ok((
-                    args.coroutine_closure_sig().map_bound(|sig| {
-                        let tupled_upvars_ty = Ty::new_projection(
-                            tcx,
-                            upvars_projection_def_id,
-                            [
-                                ty::GenericArg::from(kind_ty),
-                                Ty::from_closure_kind(tcx, goal_kind).into(),
-                                env_region.into(),
-                                sig.tupled_inputs_ty.into(),
-                                args.tupled_upvars_ty().into(),
-                                args.coroutine_captures_by_ref_ty().into(),
-                            ],
-                        );
-                        let coroutine_ty = sig.to_coroutine(
-                            tcx,
-                            args.parent_args(),
-                            Ty::from_closure_kind(tcx, goal_kind),
-                            tcx.coroutine_for_closure(def_id),
-                            tupled_upvars_ty,
-                        );
-                        (sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
-                    }),
-                    Some(
-                        ty::TraitRef::new(
-                            tcx,
-                            async_fn_kind_trait_def_id,
-                            [kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
-                        )
-                        .to_predicate(tcx),
-                    ),
-                ))
-            }
+                nested.push(
+                    ty::TraitRef::new(
+                        tcx,
+                        async_fn_kind_trait_def_id,
+                        [kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
+                    )
+                    .to_predicate(tcx),
+                );
+                let tupled_upvars_ty = Ty::new_projection(
+                    tcx,
+                    upvars_projection_def_id,
+                    [
+                        ty::GenericArg::from(kind_ty),
+                        Ty::from_closure_kind(tcx, goal_kind).into(),
+                        env_region.into(),
+                        sig.tupled_inputs_ty.into(),
+                        args.tupled_upvars_ty().into(),
+                        args.coroutine_captures_by_ref_ty().into(),
+                    ],
+                );
+                sig.to_coroutine(
+                    tcx,
+                    args.parent_args(),
+                    Ty::from_closure_kind(tcx, goal_kind),
+                    tcx.coroutine_for_closure(def_id),
+                    tupled_upvars_ty,
+                )
+            };
+
+            Ok((
+                args.coroutine_closure_sig().rebind((
+                    sig.tupled_inputs_ty,
+                    sig.return_ty,
+                    coroutine_ty,
+                )),
+                nested,
+            ))
+        }
+
+        ty::FnDef(..) | ty::FnPtr(..) => {
+            let bound_sig = self_ty.fn_sig(tcx);
+            let sig = bound_sig.skip_binder();
+            let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
+            // `FnDef` and `FnPtr` only implement `AsyncFn*` when their
+            // return type implements `Future`.
+            let nested = vec![
+                bound_sig
+                    .rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
+                    .to_predicate(tcx),
+            ];
+            let future_output_def_id = tcx
+                .associated_items(future_trait_def_id)
+                .filter_by_name_unhygienic(sym::Output)
+                .next()
+                .unwrap()
+                .def_id;
+            let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
+            Ok((
+                bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)),
+                nested,
+            ))
         }
+        ty::Closure(_, args) => {
+            let args = args.as_closure();
+            let bound_sig = args.sig();
+            let sig = bound_sig.skip_binder();
+            let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
+            // `Closure`s only implement `AsyncFn*` when their return type
+            // implements `Future`.
+            let mut nested = vec![
+                bound_sig
+                    .rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
+                    .to_predicate(tcx),
+            ];
+
+            // Additionally, we need to check that the closure kind
+            // is still compatible.
+            let kind_ty = args.kind_ty();
+            if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
+                if !closure_kind.extends(goal_kind) {
+                    return Err(NoSolution);
+                }
+            } else {
+                let async_fn_kind_trait_def_id =
+                    tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
+                // When we don't know the closure kind (and therefore also the closure's upvars,
+                // which are computed at the same time), we must delay the computation of the
+                // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
+                // goal functions similarly to the old `ClosureKind` predicate, and ensures that
+                // the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
+                // will project to the right upvars for the generator, appending the inputs and
+                // coroutine upvars respecting the closure kind.
+                nested.push(
+                    ty::TraitRef::new(
+                        tcx,
+                        async_fn_kind_trait_def_id,
+                        [kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
+                    )
+                    .to_predicate(tcx),
+                );
+            }
 
-        ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) => Err(NoSolution),
+            let future_output_def_id = tcx
+                .associated_items(future_trait_def_id)
+                .filter_by_name_unhygienic(sym::Output)
+                .next()
+                .unwrap()
+                .def_id;
+            let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
+            Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested))
+        }
 
         ty::Bool
         | ty::Char
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..803379af005 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,11 @@ 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);
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            })
         }
     }
 
@@ -801,13 +802,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..69b69930432 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,108 @@ 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);
+        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/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index f08622816ec..63555a305d8 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -140,7 +140,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
                                     )
                                 }
                                 ty::PredicateKind::Subtype(pred) => {
-                                    let (a, b) = infcx.instantiate_binder_with_placeholders(
+                                    let (a, b) = infcx.enter_forall_and_leak_universe(
                                         goal.predicate.kind().rebind((pred.a, pred.b)),
                                     );
                                     let expected_found = ExpectedFound::new(true, a, b);
@@ -150,7 +150,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
                                     )
                                 }
                                 ty::PredicateKind::Coerce(pred) => {
-                                    let (a, b) = infcx.instantiate_binder_with_placeholders(
+                                    let (a, b) = infcx.enter_forall_and_leak_universe(
                                         goal.predicate.kind().rebind((pred.a, pred.b)),
                                     );
                                     let expected_found = ExpectedFound::new(false, a, b);
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 6984f0ba694..a7330136fe7 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -124,25 +124,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         }
     }
 
-    #[instrument(level = "debug", skip(self))]
-    fn compute_closure_kind_goal(
-        &mut self,
-        goal: Goal<'tcx, (DefId, ty::GenericArgsRef<'tcx>, ty::ClosureKind)>,
-    ) -> QueryResult<'tcx> {
-        let (_, args, expected_kind) = goal.predicate;
-        let found_kind = args.as_closure().kind_ty().to_opt_closure_kind();
-
-        let Some(found_kind) = found_kind else {
-            return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
-        };
-        if found_kind.extends(expected_kind) {
-            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        } else {
-            Err(NoSolution)
-        }
-    }
-
-    #[instrument(level = "debug", skip(self))]
     fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
         if self.tcx().check_is_object_safe(trait_def_id) {
             self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
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 47ba549022d..d177109c420 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -491,6 +491,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
         let tcx = ecx.tcx();
+        let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
+        assert_eq!(metadata_def_id, goal.predicate.def_id());
         ecx.probe_misc_candidate("builtin pointee").enter(|ecx| {
             let metadata_ty = match goal.predicate.self_ty().kind() {
                 ty::Bool
@@ -522,7 +524,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                 }
 
                 ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
-                    // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
+                    // This is the "fallback impl" for type parameters, unnormalizable projections
+                    // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`.
+                    // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
+                    // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
                     let sized_predicate = ty::TraitRef::from_lang_item(
                         tcx,
                         LangItem::Sized,
@@ -536,30 +541,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
                 ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
                     None => tcx.types.unit,
-                    Some(field_def) => {
-                        let self_ty = field_def.ty(tcx, args);
-                        // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
-                        ecx.add_goal(
-                            GoalSource::Misc,
-                            goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)),
-                        );
-                        return ecx
-                            .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+                    Some(tail_def) => {
+                        let tail_ty = tail_def.ty(tcx, args);
+                        Ty::new_projection(tcx, metadata_def_id, [tail_ty])
                     }
                 },
                 ty::Adt(_, _) => tcx.types.unit,
 
                 ty::Tuple(elements) => match elements.last() {
                     None => tcx.types.unit,
-                    Some(&self_ty) => {
-                        // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
-                        ecx.add_goal(
-                            GoalSource::Misc,
-                            goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)),
-                        );
-                        return ecx
-                            .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
-                    }
+                    Some(&tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]),
                 },
 
                 ty::Infer(
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index fd09a6b671d..044832224e5 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -1049,14 +1049,21 @@ 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| {
+                        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..f68200b6f4d 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -19,7 +19,7 @@ use rustc_errors::{
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{Map, Visitor};
+use rustc_hir::intravisit::Visitor;
 use rustc_hir::is_range_literal;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node};
@@ -774,7 +774,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         if steps > 0 {
                             // Don't care about `&mut` because `DerefMut` is used less
                             // often and user will not expect that an autoderef happens.
-                            if let Some(hir::Node::Expr(hir::Expr {
+                            if let hir::Node::Expr(hir::Expr {
                                 kind:
                                     hir::ExprKind::AddrOf(
                                         hir::BorrowKind::Ref,
@@ -782,7 +782,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                         expr,
                                     ),
                                 ..
-                            })) = self.tcx.opt_hir_node(*arg_hir_id)
+                            }) = self.tcx.hir_node(*arg_hir_id)
                             {
                                 let derefs = "*".repeat(steps);
                                 err.span_suggestion_verbose(
@@ -1199,7 +1199,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         let Res::Local(hir_id) = path.res else {
             return;
         };
-        let Some(hir::Node::Pat(pat)) = self.tcx.opt_hir_node(hir_id) else {
+        let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else {
             return;
         };
         let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) =
@@ -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
@@ -1786,7 +1789,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
             if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
                 && let Res::Local(hir_id) = path.res
-                && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(hir_id)
+                && let hir::Node::Pat(binding) = self.tcx.hir_node(hir_id)
                 && let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id)
                 && let None = local.ty
                 && let Some(binding_expr) = local.init
@@ -2198,7 +2201,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         let ty::FnPtr(found) = found.kind() else {
             return;
         };
-        let Some(Node::Expr(arg)) = self.tcx.opt_hir_node(*arg_hir_id) else {
+        let Node::Expr(arg) = self.tcx.hir_node(*arg_hir_id) else {
             return;
         };
         let hir::ExprKind::Path(path) = arg.kind else {
@@ -3186,8 +3189,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
             ObligationCauseCode::VariableType(hir_id) => {
                 let parent_node = tcx.hir().parent_id(hir_id);
-                match tcx.opt_hir_node(parent_node) {
-                    Some(Node::Local(hir::Local { ty: Some(ty), .. })) => {
+                match tcx.hir_node(parent_node) {
+                    Node::Local(hir::Local { ty: Some(ty), .. }) => {
                         err.span_suggestion_verbose(
                             ty.span.shrink_to_lo(),
                             "consider borrowing here",
@@ -3196,10 +3199,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         );
                         err.note("all local variables must have a statically known size");
                     }
-                    Some(Node::Local(hir::Local {
+                    Node::Local(hir::Local {
                         init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }),
                         ..
-                    })) => {
+                    }) => {
                         // When encountering an assignment of an unsized trait, like
                         // `let x = ""[..];`, provide a suggestion to borrow the initializer in
                         // order to use have a slice instead.
@@ -3211,7 +3214,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         );
                         err.note("all local variables must have a statically known size");
                     }
-                    Some(Node::Param(param)) => {
+                    Node::Param(param) => {
                         err.span_suggestion_verbose(
                             param.ty_span.shrink_to_lo(),
                             "function arguments must have a statically known size, borrowed types \
@@ -3233,7 +3236,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 let borrowed_msg = "function arguments must have a statically known size, borrowed \
                                     types always have a known size";
                 if let Some(hir_id) = hir_id
-                    && let Some(hir::Node::Param(param)) = self.tcx.hir().find(hir_id)
+                    && let hir::Node::Param(param) = self.tcx.hir_node(hir_id)
                     && let Some(item) = self.tcx.hir().find_parent(hir_id)
                     && let Some(decl) = item.fn_decl()
                     && let Some(t) = decl.inputs.iter().find(|t| param.ty_span.contains(t.span))
@@ -3247,7 +3250,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     //                                 param._ty_span
                     ty = Some(t);
                 } else if let Some(hir_id) = hir_id
-                    && let Some(hir::Node::Ty(t)) = self.tcx.hir().find(hir_id)
+                    && let hir::Node::Ty(t) = self.tcx.hir_node(hir_id)
                 {
                     ty = Some(t);
                 }
@@ -3958,7 +3961,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         call_hir_id: HirId,
     ) {
         let tcx = self.tcx;
-        if let Some(Node::Expr(expr)) = tcx.opt_hir_node(arg_hir_id)
+        if let Node::Expr(expr) = tcx.hir_node(arg_hir_id)
             && let Some(typeck_results) = &self.typeck_results
         {
             if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
@@ -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()
@@ -4074,9 +4078,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
             if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
                 && let hir::Path { res: Res::Local(hir_id), .. } = path
-                && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id)
+                && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id)
                 && let parent_hir_id = self.tcx.hir().parent_id(binding.hir_id)
-                && let Some(hir::Node::Local(local)) = self.tcx.opt_hir_node(parent_hir_id)
+                && let hir::Node::Local(local) = self.tcx.hir_node(parent_hir_id)
                 && let Some(binding_expr) = local.init
             {
                 // If the expression we're calling on is a binding, we want to point at the
@@ -4087,17 +4091,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 self.point_at_chain(expr, typeck_results, type_diffs, param_env, err);
             }
         }
-        let call_node = tcx.opt_hir_node(call_hir_id);
-        if let Some(Node::Expr(hir::Expr {
-            kind: hir::ExprKind::MethodCall(path, rcvr, ..), ..
-        })) = call_node
+        let call_node = tcx.hir_node(call_hir_id);
+        if let Node::Expr(hir::Expr { kind: hir::ExprKind::MethodCall(path, rcvr, ..), .. }) =
+            call_node
         {
             if Some(rcvr.span) == err.span.primary_span() {
                 err.replace_span_with(path.ident.span, true);
             }
         }
 
-        if let Some(Node::Expr(expr)) = tcx.opt_hir_node(call_hir_id) {
+        if let Node::Expr(expr) = tcx.hir_node(call_hir_id) {
             if let hir::ExprKind::Call(hir::Expr { span, .. }, _)
             | hir::ExprKind::MethodCall(
                 hir::PathSegment { ident: Ident { span, .. }, .. },
@@ -4334,7 +4337,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
             if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
                 && let hir::Path { res: Res::Local(hir_id), .. } = path
-                && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id)
+                && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id)
                 && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id)
             {
                 // We've reached the root of the method call chain...
@@ -4615,14 +4618,15 @@ 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);
+                    ocx.register_obligation(Obligation::new(
+                        self.tcx,
+                        ObligationCause::dummy(),
+                        param_env,
+                        pred,
+                    ));
+                });
                 if !ocx.select_where_possible().is_empty() {
                     // encountered errors.
                     return;
@@ -4769,13 +4773,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 1ac0f172ef4..07e4fef9dd4 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
@@ -993,13 +993,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     ) -> Result<(), ErrorGuaranteed> {
         if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
             obligation.cause.code()
-            && let Some(Node::Expr(arg)) = self.tcx.opt_hir_node(*arg_hir_id)
+            && let Node::Expr(arg) = self.tcx.hir_node(*arg_hir_id)
             && let arg = arg.peel_borrows()
             && let hir::ExprKind::Path(hir::QPath::Resolved(
                 None,
                 hir::Path { res: hir::def::Res::Local(hir_id), .. },
             )) = arg.kind
-            && let Some(Node::Pat(pat)) = self.tcx.opt_hir_node(*hir_id)
+            && let Node::Pat(pat) = self.tcx.hir_node(*hir_id)
             && let Some((preds, guar)) = self.reported_trait_errors.borrow().get(&pat.span)
             && preds.contains(&obligation.predicate)
         {
@@ -1037,10 +1037,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
         }
         let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id);
-        let body_id = match self.tcx.opt_hir_node(hir_id) {
-            Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => {
-                body_id
-            }
+        let body_id = match self.tcx.hir_node(hir_id) {
+            hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id,
             _ => return false,
         };
         let mut v = V { search_span: span, found: None };
@@ -1163,7 +1161,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
             if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
                 && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path
-                && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id)
+                && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id)
                 && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id)
             {
                 // We've reached the root of the method call chain...
@@ -1307,12 +1305,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 +2016,78 @@ 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)
+                    {
+                        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;
             }
@@ -2371,6 +2378,12 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     return e;
                 }
 
+                if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) {
+                    // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
+                    // other `Foo` impls are incoherent.
+                    return guar;
+                }
+
                 // This is kind of a hack: it frequently happens that some earlier
                 // error prevents types from being fully inferred, and then we get
                 // a bunch of uninteresting errors saying something like "<generic
@@ -2666,6 +2679,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 if let Some(e) = self.tainted_by_errors() {
                     return e;
                 }
+
+                if let Err(guar) =
+                    self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_ty.def_id))
+                {
+                    // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
+                    // other `Foo` impls are incoherent.
+                    return guar;
+                }
                 let subst = data
                     .projection_ty
                     .args
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 0337672b302..5ef7a202a12 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -358,8 +358,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                 | ty::PredicateKind::Coerce(_)
                 | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
                 | ty::PredicateKind::ConstEquate(..) => {
-                    let pred =
-                        ty::Binder::dummy(infcx.instantiate_binder_with_placeholders(binder));
+                    let pred = ty::Binder::dummy(infcx.enter_forall_and_leak_universe(binder));
                     ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)]))
                 }
                 ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 955c81eee6b..054402acb5c 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -40,6 +40,7 @@ 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;
 
@@ -250,8 +251,7 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
     let infcx = selcx.infcx;
     let r = infcx.commit_if_ok(|_snapshot| {
         let old_universe = infcx.universe();
-        let placeholder_predicate =
-            infcx.instantiate_binder_with_placeholders(obligation.predicate);
+        let placeholder_predicate = infcx.enter_forall_and_leak_universe(obligation.predicate);
         let new_universe = infcx.universe();
 
         let placeholder_obligation = obligation.with(infcx.tcx, placeholder_predicate);
@@ -1615,32 +1615,44 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
     candidate_set: &mut ProjectionCandidateSet<'tcx>,
 ) {
     debug!("assemble_candidates_from_trait_def(..)");
+    let mut ambiguous = false;
+    selcx.for_each_item_bound(
+        obligation.predicate.self_ty(),
+        |selcx, clause, _| {
+            let Some(clause) = clause.as_projection_clause() else {
+                return ControlFlow::Continue(());
+            };
 
-    let tcx = selcx.tcx();
-    // Check whether the self-type is itself a projection.
-    // If so, extract what we know from the trait and try to come up with a good answer.
-    let bounds = match *obligation.predicate.self_ty().kind() {
-        // Excluding IATs and type aliases here as they don't have meaningful item bounds.
-        ty::Alias(ty::Projection | ty::Opaque, ref data) => {
-            tcx.item_bounds(data.def_id).instantiate(tcx, data.args)
-        }
-        ty::Infer(ty::TyVar(_)) => {
-            // If the self-type is an inference variable, then it MAY wind up
-            // being a projected type, so induce an ambiguity.
-            candidate_set.mark_ambiguous();
-            return;
-        }
-        _ => return,
-    };
+            let is_match =
+                selcx.infcx.probe(|_| selcx.match_projection_projections(obligation, clause, true));
 
-    assemble_candidates_from_predicates(
-        selcx,
-        obligation,
-        candidate_set,
-        ProjectionCandidate::TraitDef,
-        bounds.iter(),
-        true,
+            match is_match {
+                ProjectionMatchesProjection::Yes => {
+                    candidate_set.push_candidate(ProjectionCandidate::TraitDef(clause));
+
+                    if !obligation.predicate.has_non_region_infer() {
+                        // HACK: Pick the first trait def candidate for a fully
+                        // inferred predicate. This is to allow duplicates that
+                        // differ only in normalization.
+                        return ControlFlow::Break(());
+                    }
+                }
+                ProjectionMatchesProjection::Ambiguous => {
+                    candidate_set.mark_ambiguous();
+                }
+                ProjectionMatchesProjection::No => {}
+            }
+
+            ControlFlow::Continue(())
+        },
+        // `ProjectionCandidateSet` is borrowed in the above closure,
+        // so just mark ambiguous outside of the closure.
+        || ambiguous = true,
     );
+
+    if ambiguous {
+        candidate_set.mark_ambiguous();
+    }
 }
 
 /// In the case of a trait object like
@@ -1936,10 +1948,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         // Integers and floats are always Sized, and so have unit type metadata.
                         | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
 
-                        // type parameters, opaques, and unnormalized projections have pointer
-                        // metadata if they're known (e.g. by the param_env) to be sized
+                        // We normalize from `Wrapper<Tail>::Metadata` to `Tail::Metadata` if able.
+                        // Otherwise, type parameters, opaques, and unnormalized projections have
+                        // unit metadata if they're known (e.g. by the param_env) to be sized.
                         ty::Param(_) | ty::Alias(..)
-                            if selcx.infcx.predicate_must_hold_modulo_regions(
+                            if self_ty != tail || selcx.infcx.predicate_must_hold_modulo_regions(
                                 &obligation.with(
                                     selcx.tcx(),
                                     ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]),
@@ -2074,7 +2087,9 @@ fn confirm_select_candidate<'cx, 'tcx>(
             } else if lang_items.async_iterator_trait() == Some(trait_def_id) {
                 confirm_async_iterator_candidate(selcx, obligation, data)
             } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() {
-                if obligation.predicate.self_ty().is_closure() {
+                if obligation.predicate.self_ty().is_closure()
+                    || obligation.predicate.self_ty().is_coroutine_closure()
+                {
                     confirm_closure_candidate(selcx, obligation, data)
                 } else {
                     confirm_fn_pointer_candidate(selcx, obligation, data)
@@ -2313,7 +2328,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
         assert_eq!(metadata_def_id, item_def_id);
 
         let mut obligations = Vec::new();
-        let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| {
+        let normalize = |ty| {
             normalize_with_depth_to(
                 selcx,
                 obligation.param_env,
@@ -2322,16 +2337,27 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
                 ty,
                 &mut obligations,
             )
+        };
+        let metadata_ty = self_ty.ptr_metadata_ty_or_tail(tcx, normalize).unwrap_or_else(|tail| {
+            if tail == self_ty {
+                // This is the "fallback impl" for type parameters, unnormalizable projections
+                // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`.
+                // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
+                // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
+                let sized_predicate = ty::TraitRef::from_lang_item(
+                    tcx,
+                    LangItem::Sized,
+                    obligation.cause.span(),
+                    [self_ty],
+                );
+                obligations.push(obligation.with(tcx, sized_predicate));
+                tcx.types.unit
+            } else {
+                // We know that `self_ty` has the same metadata as `tail`. This allows us
+                // to prove predicates like `Wrapper<Tail>::Metadata == Tail::Metadata`.
+                Ty::new_projection(tcx, metadata_def_id, [tail])
+            }
         });
-        if check_is_sized {
-            let sized_predicate = ty::TraitRef::from_lang_item(
-                tcx,
-                LangItem::Sized,
-                obligation.cause.span(),
-                [self_ty],
-            );
-            obligations.push(obligation.with(tcx, sized_predicate));
-        }
         (metadata_ty.into(), obligations)
     } else {
         bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate);
@@ -2386,11 +2412,75 @@ fn confirm_closure_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     nested: Vec<PredicateObligation<'tcx>>,
 ) -> Progress<'tcx> {
+    let tcx = selcx.tcx();
     let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
-    let ty::Closure(_, args) = self_ty.kind() else {
-        unreachable!("expected closure self type for closure candidate, found {self_ty}")
+    let closure_sig = match *self_ty.kind() {
+        ty::Closure(_, args) => args.as_closure().sig(),
+
+        // Construct a "normal" `FnOnce` signature for coroutine-closure. This is
+        // basically duplicated with the `AsyncFnOnce::CallOnce` confirmation, but
+        // I didn't see a good way to unify those.
+        ty::CoroutineClosure(def_id, args) => {
+            let args = args.as_coroutine_closure();
+            let kind_ty = args.kind_ty();
+            args.coroutine_closure_sig().map_bound(|sig| {
+                // If we know the kind and upvars, use that directly.
+                // Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay
+                // the projection, like the `AsyncFn*` traits do.
+                let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() {
+                    sig.to_coroutine_given_kind_and_upvars(
+                        tcx,
+                        args.parent_args(),
+                        tcx.coroutine_for_closure(def_id),
+                        ty::ClosureKind::FnOnce,
+                        tcx.lifetimes.re_static,
+                        args.tupled_upvars_ty(),
+                        args.coroutine_captures_by_ref_ty(),
+                    )
+                } else {
+                    let async_fn_kind_trait_def_id =
+                        tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
+                    let upvars_projection_def_id = tcx
+                        .associated_items(async_fn_kind_trait_def_id)
+                        .filter_by_name_unhygienic(sym::Upvars)
+                        .next()
+                        .unwrap()
+                        .def_id;
+                    let tupled_upvars_ty = Ty::new_projection(
+                        tcx,
+                        upvars_projection_def_id,
+                        [
+                            ty::GenericArg::from(kind_ty),
+                            Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(),
+                            tcx.lifetimes.re_static.into(),
+                            sig.tupled_inputs_ty.into(),
+                            args.tupled_upvars_ty().into(),
+                            args.coroutine_captures_by_ref_ty().into(),
+                        ],
+                    );
+                    sig.to_coroutine(
+                        tcx,
+                        args.parent_args(),
+                        Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce),
+                        tcx.coroutine_for_closure(def_id),
+                        tupled_upvars_ty,
+                    )
+                };
+                tcx.mk_fn_sig(
+                    [sig.tupled_inputs_ty],
+                    output_ty,
+                    sig.c_variadic,
+                    sig.unsafety,
+                    sig.abi,
+                )
+            })
+        }
+
+        _ => {
+            unreachable!("expected closure self type for closure candidate, found {self_ty}");
+        }
     };
-    let closure_sig = args.as_closure().sig();
+
     let Normalized { value: closure_sig, obligations } = normalize_with_depth(
         selcx,
         obligation.param_env,
@@ -2446,126 +2536,171 @@ fn confirm_callable_candidate<'cx, 'tcx>(
 fn confirm_async_closure_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    mut nested: Vec<PredicateObligation<'tcx>>,
+    nested: Vec<PredicateObligation<'tcx>>,
 ) -> Progress<'tcx> {
+    let tcx = selcx.tcx();
     let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
-    let ty::CoroutineClosure(def_id, args) = *self_ty.kind() else {
-        unreachable!(
-            "expected coroutine-closure self type for coroutine-closure candidate, found {self_ty}"
-        )
-    };
-    let args = args.as_coroutine_closure();
-    let kind_ty = args.kind_ty();
 
-    let tcx = selcx.tcx();
     let goal_kind =
         tcx.async_fn_trait_kind_from_def_id(obligation.predicate.trait_def_id(tcx)).unwrap();
-
-    let async_fn_kind_helper_trait_def_id =
-        tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
-    nested.push(obligation.with(
-        tcx,
-        ty::TraitRef::new(
-            tcx,
-            async_fn_kind_helper_trait_def_id,
-            [kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
-        ),
-    ));
-
     let env_region = match goal_kind {
         ty::ClosureKind::Fn | ty::ClosureKind::FnMut => obligation.predicate.args.region_at(2),
         ty::ClosureKind::FnOnce => tcx.lifetimes.re_static,
     };
-
-    let upvars_projection_def_id = tcx
-        .associated_items(async_fn_kind_helper_trait_def_id)
-        .filter_by_name_unhygienic(sym::Upvars)
-        .next()
-        .unwrap()
-        .def_id;
-
-    // FIXME(async_closures): Confirmation is kind of a mess here. Ideally,
-    // we'd short-circuit when we know that the goal_kind >= closure_kind, and not
-    // register a nested predicate or create a new projection ty here. But I'm too
-    // lazy to make this more efficient atm, and we can always tweak it later,
-    // since all this does is make the solver do more work.
-    //
-    // The code duplication due to the different length args is kind of weird, too.
-    //
-    // See the logic in `structural_traits` in the new solver to understand a bit
-    // more clearly how this *should* look.
-    let poly_cache_entry = args.coroutine_closure_sig().map_bound(|sig| {
-        let (projection_ty, term) = match tcx.item_name(obligation.predicate.def_id) {
-            sym::CallOnceFuture => {
-                let tupled_upvars_ty = Ty::new_projection(
+    let item_name = tcx.item_name(obligation.predicate.def_id);
+
+    let poly_cache_entry = match *self_ty.kind() {
+        ty::CoroutineClosure(def_id, args) => {
+            let args = args.as_coroutine_closure();
+            let kind_ty = args.kind_ty();
+            let sig = args.coroutine_closure_sig().skip_binder();
+
+            let term = match item_name {
+                sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => {
+                    if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
+                        if !closure_kind.extends(goal_kind) {
+                            bug!("we should not be confirming if the closure kind is not met");
+                        }
+                        sig.to_coroutine_given_kind_and_upvars(
+                            tcx,
+                            args.parent_args(),
+                            tcx.coroutine_for_closure(def_id),
+                            goal_kind,
+                            env_region,
+                            args.tupled_upvars_ty(),
+                            args.coroutine_captures_by_ref_ty(),
+                        )
+                    } else {
+                        let async_fn_kind_trait_def_id =
+                            tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
+                        let upvars_projection_def_id = tcx
+                            .associated_items(async_fn_kind_trait_def_id)
+                            .filter_by_name_unhygienic(sym::Upvars)
+                            .next()
+                            .unwrap()
+                            .def_id;
+                        // When we don't know the closure kind (and therefore also the closure's upvars,
+                        // which are computed at the same time), we must delay the computation of the
+                        // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
+                        // goal functions similarly to the old `ClosureKind` predicate, and ensures that
+                        // the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
+                        // will project to the right upvars for the generator, appending the inputs and
+                        // coroutine upvars respecting the closure kind.
+                        // N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`.
+                        let tupled_upvars_ty = Ty::new_projection(
+                            tcx,
+                            upvars_projection_def_id,
+                            [
+                                ty::GenericArg::from(kind_ty),
+                                Ty::from_closure_kind(tcx, goal_kind).into(),
+                                env_region.into(),
+                                sig.tupled_inputs_ty.into(),
+                                args.tupled_upvars_ty().into(),
+                                args.coroutine_captures_by_ref_ty().into(),
+                            ],
+                        );
+                        sig.to_coroutine(
+                            tcx,
+                            args.parent_args(),
+                            Ty::from_closure_kind(tcx, goal_kind),
+                            tcx.coroutine_for_closure(def_id),
+                            tupled_upvars_ty,
+                        )
+                    }
+                }
+                sym::Output => sig.return_ty,
+                name => bug!("no such associated type: {name}"),
+            };
+            let projection_ty = match item_name {
+                sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
                     tcx,
-                    upvars_projection_def_id,
-                    [
-                        ty::GenericArg::from(kind_ty),
-                        Ty::from_closure_kind(tcx, goal_kind).into(),
-                        env_region.into(),
-                        sig.tupled_inputs_ty.into(),
-                        args.tupled_upvars_ty().into(),
-                        args.coroutine_captures_by_ref_ty().into(),
-                    ],
-                );
-                let coroutine_ty = sig.to_coroutine(
+                    obligation.predicate.def_id,
+                    [self_ty, sig.tupled_inputs_ty],
+                ),
+                sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
                     tcx,
-                    args.parent_args(),
-                    Ty::from_closure_kind(tcx, goal_kind),
-                    tcx.coroutine_for_closure(def_id),
-                    tupled_upvars_ty,
-                );
-                (
-                    ty::AliasTy::new(
-                        tcx,
-                        obligation.predicate.def_id,
-                        [self_ty, sig.tupled_inputs_ty],
-                    ),
-                    coroutine_ty.into(),
-                )
-            }
-            sym::CallMutFuture | sym::CallFuture => {
-                let tupled_upvars_ty = Ty::new_projection(
+                    obligation.predicate.def_id,
+                    [ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()],
+                ),
+                name => bug!("no such associated type: {name}"),
+            };
+
+            args.coroutine_closure_sig()
+                .rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
+        }
+        ty::FnDef(..) | ty::FnPtr(..) => {
+            let bound_sig = self_ty.fn_sig(tcx);
+            let sig = bound_sig.skip_binder();
+
+            let term = match item_name {
+                sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
+                sym::Output => {
+                    let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
+                    let future_output_def_id = tcx
+                        .associated_items(future_trait_def_id)
+                        .filter_by_name_unhygienic(sym::Output)
+                        .next()
+                        .unwrap()
+                        .def_id;
+                    Ty::new_projection(tcx, future_output_def_id, [sig.output()])
+                }
+                name => bug!("no such associated type: {name}"),
+            };
+            let projection_ty = match item_name {
+                sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
                     tcx,
-                    upvars_projection_def_id,
+                    obligation.predicate.def_id,
+                    [self_ty, Ty::new_tup(tcx, sig.inputs())],
+                ),
+                sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
+                    tcx,
+                    obligation.predicate.def_id,
                     [
-                        ty::GenericArg::from(kind_ty),
-                        Ty::from_closure_kind(tcx, goal_kind).into(),
+                        ty::GenericArg::from(self_ty),
+                        Ty::new_tup(tcx, sig.inputs()).into(),
                         env_region.into(),
-                        sig.tupled_inputs_ty.into(),
-                        args.tupled_upvars_ty().into(),
-                        args.coroutine_captures_by_ref_ty().into(),
                     ],
-                );
-                let coroutine_ty = sig.to_coroutine(
+                ),
+                name => bug!("no such associated type: {name}"),
+            };
+
+            bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
+        }
+        ty::Closure(_, args) => {
+            let args = args.as_closure();
+            let bound_sig = args.sig();
+            let sig = bound_sig.skip_binder();
+
+            let term = match item_name {
+                sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
+                sym::Output => {
+                    let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
+                    let future_output_def_id = tcx
+                        .associated_items(future_trait_def_id)
+                        .filter_by_name_unhygienic(sym::Output)
+                        .next()
+                        .unwrap()
+                        .def_id;
+                    Ty::new_projection(tcx, future_output_def_id, [sig.output()])
+                }
+                name => bug!("no such associated type: {name}"),
+            };
+            let projection_ty = match item_name {
+                sym::CallOnceFuture | sym::Output => {
+                    ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]])
+                }
+                sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
                     tcx,
-                    args.parent_args(),
-                    Ty::from_closure_kind(tcx, goal_kind),
-                    tcx.coroutine_for_closure(def_id),
-                    tupled_upvars_ty,
-                );
-                (
-                    ty::AliasTy::new(
-                        tcx,
-                        obligation.predicate.def_id,
-                        [
-                            ty::GenericArg::from(self_ty),
-                            sig.tupled_inputs_ty.into(),
-                            env_region.into(),
-                        ],
-                    ),
-                    coroutine_ty.into(),
-                )
-            }
-            sym::Output => (
-                ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.tupled_inputs_ty]),
-                sig.return_ty.into(),
-            ),
-            name => bug!("no such associated type: {name}"),
-        };
-        ty::ProjectionPredicate { projection_ty, term }
-    });
+                    obligation.predicate.def_id,
+                    [ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()],
+                ),
+                name => bug!("no such associated type: {name}"),
+            };
+
+            bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
+        }
+        _ => bug!("expected callable type for AsyncFn candidate"),
+    };
 
     confirm_param_env_candidate(selcx, obligation, poly_cache_entry, true)
         .with_addl_obligations(nested)
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index 4c0c57377e0..a050b30317a 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -41,7 +41,28 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
     /// not entirely accurate if inference variables are involved.
     ///
     /// This version may conservatively fail when outlives obligations
-    /// are required.
+    /// are required. Therefore, this version should only be used for
+    /// optimizations or diagnostics and be treated as if it can always
+    /// return `false`.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # #![allow(dead_code)]
+    /// trait Trait {}
+    ///
+    /// fn check<T: Trait>() {}
+    ///
+    /// fn foo<T: 'static>()
+    /// where
+    ///     &'static T: Trait,
+    /// {
+    ///     // Evaluating `&'?0 T: Trait` adds a `'?0: 'static` outlives obligation,
+    ///     // which means that `predicate_must_hold_considering_regions` will return
+    ///     // `false`.
+    ///     check::<&'_ T>();
+    /// }
+    /// ```
     fn predicate_must_hold_considering_regions(
         &self,
         obligation: &PredicateObligation<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 2258e796103..f9a292c2bd7 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -6,13 +6,16 @@
 //!
 //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
 
+use std::ops::ControlFlow;
+
 use hir::def_id::DefId;
 use hir::LangItem;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_infer::traits::ObligationCause;
 use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
-use rustc_middle::ty::{self, Ty, TypeVisitableExt};
+use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt};
 
 use crate::traits;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
@@ -158,11 +161,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             _ => return,
         }
 
-        let result = self
-            .infcx
-            .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
+        self.infcx.probe(|_| {
+            let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
+            let placeholder_trait_predicate =
+                self.infcx.enter_forall_and_leak_universe(poly_trait_predicate);
+            debug!(?placeholder_trait_predicate);
+
+            // The bounds returned by `item_bounds` may contain duplicates after
+            // normalization, so try to deduplicate when possible to avoid
+            // unnecessary ambiguity.
+            let mut distinct_normalized_bounds = FxHashSet::default();
+            self.for_each_item_bound::<!>(
+                placeholder_trait_predicate.self_ty(),
+                |selcx, bound, idx| {
+                    let Some(bound) = bound.as_trait_clause() else {
+                        return ControlFlow::Continue(());
+                    };
+                    if bound.polarity() != placeholder_trait_predicate.polarity {
+                        return ControlFlow::Continue(());
+                    }
+
+                    selcx.infcx.probe(|_| {
+                        match selcx.match_normalize_trait_ref(
+                            obligation,
+                            bound.to_poly_trait_ref(),
+                            placeholder_trait_predicate.trait_ref,
+                        ) {
+                            Ok(None) => {
+                                candidates.vec.push(ProjectionCandidate(idx));
+                            }
+                            Ok(Some(normalized_trait))
+                                if distinct_normalized_bounds.insert(normalized_trait) =>
+                            {
+                                candidates.vec.push(ProjectionCandidate(idx));
+                            }
+                            _ => {}
+                        }
+                    });
 
-        candidates.vec.extend(result.into_iter().map(|idx| ProjectionCandidate(idx)));
+                    ControlFlow::Continue(())
+                },
+                // On ambiguity.
+                || candidates.ambiguous = true,
+            );
+        });
     }
 
     /// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller
@@ -332,6 +374,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     }
                 }
             }
+            ty::CoroutineClosure(def_id, args) => {
+                let is_const = self.tcx().is_const_fn_raw(def_id);
+                match self.infcx.closure_kind(self_ty) {
+                    Some(closure_kind) => {
+                        let no_borrows = match self
+                            .infcx
+                            .shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty())
+                            .kind()
+                        {
+                            ty::Tuple(tys) => tys.is_empty(),
+                            ty::Error(_) => false,
+                            _ => bug!("tuple_fields called on non-tuple"),
+                        };
+                        // A coroutine-closure implements `FnOnce` *always*, since it may
+                        // always be called once. It additionally implements `Fn`/`FnMut`
+                        // only if it has no upvars (therefore no borrows from the closure
+                        // that would need to be represented with a lifetime) and if the
+                        // closure kind permits it.
+                        // FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
+                        // if it takes all of its upvars by copy, and none by ref. This would
+                        // require us to record a bit more information during upvar analysis.
+                        if no_borrows && closure_kind.extends(kind) {
+                            candidates.vec.push(ClosureCandidate { is_const });
+                        } else if kind == ty::ClosureKind::FnOnce {
+                            candidates.vec.push(ClosureCandidate { is_const });
+                        }
+                    }
+                    None => {
+                        if kind == ty::ClosureKind::FnOnce {
+                            candidates.vec.push(ClosureCandidate { is_const });
+                        } else {
+                            // This stays ambiguous until kind+upvars are determined.
+                            candidates.ambiguous = true;
+                        }
+                    }
+                }
+            }
             ty::Infer(ty::TyVar(_)) => {
                 debug!("assemble_unboxed_closure_candidates: ambiguous self-type");
                 candidates.ambiguous = true;
@@ -361,8 +440,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 }
                 candidates.vec.push(AsyncClosureCandidate);
             }
-            ty::Infer(ty::TyVar(_)) => {
-                candidates.ambiguous = true;
+            // Closures and fn pointers implement `AsyncFn*` if their return types
+            // implement `Future`, which is checked later.
+            ty::Closure(_, args) => {
+                if let Some(closure_kind) = args.as_closure().kind_ty().to_opt_closure_kind()
+                    && !closure_kind.extends(goal_kind)
+                {
+                    return;
+                }
+                candidates.vec.push(AsyncClosureCandidate);
+            }
+            ty::FnDef(..) | ty::FnPtr(..) => {
+                candidates.vec.push(AsyncClosureCandidate);
             }
             _ => {}
         }
@@ -728,64 +817,63 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         self.infcx.probe(|_snapshot| {
             let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
-            let placeholder_trait_predicate =
-                self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate);
-
-            let self_ty = placeholder_trait_predicate.self_ty();
-            let principal_trait_ref = match self_ty.kind() {
-                ty::Dynamic(data, ..) => {
-                    if data.auto_traits().any(|did| did == obligation.predicate.def_id()) {
-                        debug!(
-                            "assemble_candidates_from_object_ty: matched builtin bound, \
+            self.infcx.enter_forall(poly_trait_predicate, |placeholder_trait_predicate| {
+                let self_ty = placeholder_trait_predicate.self_ty();
+                let principal_trait_ref = match self_ty.kind() {
+                    ty::Dynamic(data, ..) => {
+                        if data.auto_traits().any(|did| did == obligation.predicate.def_id()) {
+                            debug!(
+                                "assemble_candidates_from_object_ty: matched builtin bound, \
                              pushing candidate"
-                        );
-                        candidates.vec.push(BuiltinObjectCandidate);
-                        return;
-                    }
+                            );
+                            candidates.vec.push(BuiltinObjectCandidate);
+                            return;
+                        }
 
-                    if let Some(principal) = data.principal() {
-                        if !self.infcx.tcx.features().object_safe_for_dispatch {
-                            principal.with_self_ty(self.tcx(), self_ty)
-                        } else if self.tcx().check_is_object_safe(principal.def_id()) {
-                            principal.with_self_ty(self.tcx(), self_ty)
+                        if let Some(principal) = data.principal() {
+                            if !self.infcx.tcx.features().object_safe_for_dispatch {
+                                principal.with_self_ty(self.tcx(), self_ty)
+                            } else if self.tcx().check_is_object_safe(principal.def_id()) {
+                                principal.with_self_ty(self.tcx(), self_ty)
+                            } else {
+                                return;
+                            }
                         } else {
+                            // Only auto trait bounds exist.
                             return;
                         }
-                    } else {
-                        // Only auto trait bounds exist.
+                    }
+                    ty::Infer(ty::TyVar(_)) => {
+                        debug!("assemble_candidates_from_object_ty: ambiguous");
+                        candidates.ambiguous = true; // could wind up being an object type
                         return;
                     }
-                }
-                ty::Infer(ty::TyVar(_)) => {
-                    debug!("assemble_candidates_from_object_ty: ambiguous");
-                    candidates.ambiguous = true; // could wind up being an object type
-                    return;
-                }
-                _ => return,
-            };
-
-            debug!(?principal_trait_ref, "assemble_candidates_from_object_ty");
-
-            // Count only those upcast versions that match the trait-ref
-            // we are looking for. Specifically, do not only check for the
-            // correct trait, but also the correct type parameters.
-            // For example, we may be trying to upcast `Foo` to `Bar<i32>`,
-            // but `Foo` is declared as `trait Foo: Bar<u32>`.
-            let candidate_supertraits = util::supertraits(self.tcx(), principal_trait_ref)
-                .enumerate()
-                .filter(|&(_, upcast_trait_ref)| {
-                    self.infcx.probe(|_| {
-                        self.match_normalize_trait_ref(
-                            obligation,
-                            upcast_trait_ref,
-                            placeholder_trait_predicate.trait_ref,
-                        )
-                        .is_ok()
+                    _ => return,
+                };
+
+                debug!(?principal_trait_ref, "assemble_candidates_from_object_ty");
+
+                // Count only those upcast versions that match the trait-ref
+                // we are looking for. Specifically, do not only check for the
+                // correct trait, but also the correct type parameters.
+                // For example, we may be trying to upcast `Foo` to `Bar<i32>`,
+                // but `Foo` is declared as `trait Foo: Bar<u32>`.
+                let candidate_supertraits = util::supertraits(self.tcx(), principal_trait_ref)
+                    .enumerate()
+                    .filter(|&(_, upcast_trait_ref)| {
+                        self.infcx.probe(|_| {
+                            self.match_normalize_trait_ref(
+                                obligation,
+                                upcast_trait_ref,
+                                placeholder_trait_predicate.trait_ref,
+                            )
+                            .is_ok()
+                        })
                     })
-                })
-                .map(|(idx, _)| ObjectCandidate(idx));
+                    .map(|(idx, _)| ObjectCandidate(idx));
 
-            candidates.vec.extend(candidate_supertraits);
+                candidates.vec.extend(candidate_supertraits);
+            })
         })
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index c9d06b0f675..6ca24933979 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -159,23 +159,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let trait_predicate = self.infcx.shallow_resolve(obligation.predicate);
         let placeholder_trait_predicate =
-            self.infcx.instantiate_binder_with_placeholders(trait_predicate).trait_ref;
+            self.infcx.enter_forall_and_leak_universe(trait_predicate).trait_ref;
         let placeholder_self_ty = placeholder_trait_predicate.self_ty();
         let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate);
-        let (def_id, args) = match *placeholder_self_ty.kind() {
-            // Excluding IATs and type aliases here as they don't have meaningful item bounds.
-            ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
-                (def_id, args)
-            }
-            _ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty),
-        };
 
-        let candidate_predicate =
-            tcx.item_bounds(def_id).map_bound(|i| i[idx]).instantiate(tcx, args);
+        let candidate_predicate = self
+            .for_each_item_bound(
+                placeholder_self_ty,
+                |_, clause, clause_idx| {
+                    if clause_idx == idx {
+                        ControlFlow::Break(clause)
+                    } else {
+                        ControlFlow::Continue(())
+                    }
+                },
+                || unreachable!(),
+            )
+            .break_value()
+            .expect("expected to index into clause that exists");
         let candidate = candidate_predicate
             .as_trait_clause()
             .expect("projection candidate is not a trait predicate")
             .map_bound(|t| t.trait_ref);
+
         let mut obligations = Vec::new();
         let candidate = normalize_with_depth_to(
             self,
@@ -194,8 +200,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 .map_err(|_| Unimplemented)
         })?);
 
-        if let ty::Alias(ty::Projection, ..) = placeholder_self_ty.kind() {
-            let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
+        // FIXME(compiler-errors): I don't think this is needed.
+        if let ty::Alias(ty::Projection, alias_ty) = placeholder_self_ty.kind() {
+            let predicates = tcx.predicates_of(alias_ty.def_id).instantiate_own(tcx, alias_ty.args);
             for (predicate, _) in predicates {
                 let normalized = normalize_with_depth_to(
                     self,
@@ -402,7 +409,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             let cause = obligation.derived_cause(BuiltinDerivedObligation);
 
             let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
-            let trait_ref = self.infcx.instantiate_binder_with_placeholders(poly_trait_ref);
+            let trait_ref = self.infcx.enter_forall_and_leak_universe(poly_trait_ref);
             let trait_obligations: Vec<PredicateObligation<'_>> = self.impl_or_trait_obligations(
                 &cause,
                 obligation.recursion_depth + 1,
@@ -493,7 +500,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let tcx = self.tcx();
         debug!(?obligation, ?index, "confirm_object_candidate");
 
-        let trait_predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
+        let trait_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty());
         let obligation_trait_ref = ty::Binder::dummy(trait_predicate.trait_ref);
         let ty::Dynamic(data, ..) = *self_ty.kind() else {
@@ -691,7 +698,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let cause = obligation.derived_cause(BuiltinDerivedObligation);
 
         // Confirm the `type Output: Sized;` bound that is present on `FnOnce`
-        let output_ty = self.infcx.instantiate_binder_with_placeholders(sig.output());
+        let output_ty = self.infcx.enter_forall_and_leak_universe(sig.output());
         let output_ty = normalize_with_depth_to(
             self,
             obligation.param_env,
@@ -712,7 +719,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     ) -> Vec<PredicateObligation<'tcx>> {
         debug!(?obligation, "confirm_trait_alias_candidate");
 
-        let predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
+        let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let trait_ref = predicate.trait_ref;
         let trait_def_id = trait_ref.def_id;
         let args = trait_ref.args;
@@ -865,17 +872,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // touch bound regions, they just capture the in-scope
         // type/region parameters.
         let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
-        let ty::Closure(closure_def_id, args) = *self_ty.kind() else {
-            bug!("closure candidate for non-closure {:?}", obligation);
+        let trait_ref = match *self_ty.kind() {
+            ty::Closure(_, args) => {
+                self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_)
+            }
+            ty::CoroutineClosure(_, args) => {
+                args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
+                    ty::TraitRef::new(
+                        self.tcx(),
+                        obligation.predicate.def_id(),
+                        [self_ty, sig.tupled_inputs_ty],
+                    )
+                })
+            }
+            _ => {
+                bug!("closure candidate for non-closure {:?}", obligation);
+            }
         };
 
-        let trait_ref =
-            self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_);
-        let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
-
-        debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
-
-        Ok(nested)
+        self.confirm_poly_trait_refs(obligation, trait_ref)
     }
 
     #[instrument(skip(self), level = "debug")]
@@ -883,40 +898,86 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
-        // Okay to skip binder because the args on closure types never
-        // touch bound regions, they just capture the in-scope
-        // type/region parameters.
+        let tcx = self.tcx();
         let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
-        let ty::CoroutineClosure(closure_def_id, args) = *self_ty.kind() else {
-            bug!("async closure candidate for non-coroutine-closure {:?}", obligation);
-        };
 
-        let trait_ref = args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
-            ty::TraitRef::new(
-                self.tcx(),
-                obligation.predicate.def_id(),
-                [self_ty, sig.tupled_inputs_ty],
-            )
-        });
+        let mut nested = vec![];
+        let (trait_ref, kind_ty) = match *self_ty.kind() {
+            ty::CoroutineClosure(_, args) => {
+                let args = args.as_coroutine_closure();
+                let trait_ref = args.coroutine_closure_sig().map_bound(|sig| {
+                    ty::TraitRef::new(
+                        self.tcx(),
+                        obligation.predicate.def_id(),
+                        [self_ty, sig.tupled_inputs_ty],
+                    )
+                });
+                (trait_ref, args.kind_ty())
+            }
+            ty::FnDef(..) | ty::FnPtr(..) => {
+                let sig = self_ty.fn_sig(tcx);
+                let trait_ref = sig.map_bound(|sig| {
+                    ty::TraitRef::new(
+                        self.tcx(),
+                        obligation.predicate.def_id(),
+                        [self_ty, Ty::new_tup(tcx, sig.inputs())],
+                    )
+                });
+                // We must additionally check that the return type impls `Future`.
+                let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
+                nested.push(obligation.with(
+                    tcx,
+                    sig.map_bound(|sig| {
+                        ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
+                    }),
+                ));
+                (trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn))
+            }
+            ty::Closure(_, args) => {
+                let sig = args.as_closure().sig();
+                let trait_ref = sig.map_bound(|sig| {
+                    ty::TraitRef::new(
+                        self.tcx(),
+                        obligation.predicate.def_id(),
+                        [self_ty, sig.inputs()[0]],
+                    )
+                });
+                // We must additionally check that the return type impls `Future`.
+                let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
+                nested.push(obligation.with(
+                    tcx,
+                    sig.map_bound(|sig| {
+                        ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
+                    }),
+                ));
+                (trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn))
+            }
+            _ => bug!("expected callable type for AsyncFn candidate"),
+        };
 
-        let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
+        nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?);
 
         let goal_kind =
             self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();
-        nested.push(obligation.with(
-            self.tcx(),
-            ty::TraitRef::from_lang_item(
+
+        // If we have not yet determiend the `ClosureKind` of the closure or coroutine-closure,
+        // then additionally register an `AsyncFnKindHelper` goal which will fail if the kind
+        // is constrained to an insufficient type later on.
+        if let Some(closure_kind) = self.infcx.shallow_resolve(kind_ty).to_opt_closure_kind() {
+            if !closure_kind.extends(goal_kind) {
+                return Err(SelectionError::Unimplemented);
+            }
+        } else {
+            nested.push(obligation.with(
                 self.tcx(),
-                LangItem::AsyncFnKindHelper,
-                obligation.cause.span,
-                [
-                    args.as_coroutine_closure().kind_ty(),
-                    Ty::from_closure_kind(self.tcx(), goal_kind),
-                ],
-            ),
-        ));
-
-        debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
+                ty::TraitRef::from_lang_item(
+                    self.tcx(),
+                    LangItem::AsyncFnKindHelper,
+                    obligation.cause.span,
+                    [kind_ty, Ty::from_closure_kind(self.tcx(), goal_kind)],
+                ),
+            ));
+        }
 
         Ok(nested)
     }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 7f41c73b72f..ac6cfcdeb59 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -52,6 +52,7 @@ use std::cell::{Cell, RefCell};
 use std::cmp;
 use std::fmt::{self, Display};
 use std::iter;
+use std::ops::ControlFlow;
 
 pub use rustc_middle::traits::select::*;
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -483,7 +484,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // Instead, we select the right impl now but report "`Bar` does
         // not implement `Clone`".
         if candidates.len() == 1 {
-            return self.filter_reservation_impls(candidates.pop().unwrap(), stack.obligation);
+            return self.filter_reservation_impls(candidates.pop().unwrap());
         }
 
         // Winnow, but record the exact outcome of evaluation, which
@@ -557,7 +558,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         // Just one candidate left.
-        self.filter_reservation_impls(candidates.pop().unwrap().candidate, stack.obligation)
+        self.filter_reservation_impls(candidates.pop().unwrap().candidate)
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -1436,7 +1437,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn filter_reservation_impls(
         &mut self,
         candidate: SelectionCandidate<'tcx>,
-        obligation: &PolyTraitObligation<'tcx>,
     ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
         let tcx = self.tcx();
         // Treat reservation impls as ambiguity.
@@ -1593,71 +1593,41 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         self.infcx.selection_cache.insert((param_env, pred), dep_node, candidate);
     }
 
-    /// Matches a predicate against the bounds of its self type.
-    ///
-    /// Given an obligation like `<T as Foo>::Bar: Baz` where the self type is
-    /// a projection, look at the bounds of `T::Bar`, see if we can find a
-    /// `Baz` bound. We return indexes into the list returned by
-    /// `tcx.item_bounds` for any applicable bounds.
-    #[instrument(level = "debug", skip(self), ret)]
-    fn match_projection_obligation_against_definition_bounds(
+    /// Looks at the item bounds of the projection or opaque type.
+    /// If this is a nested rigid projection, such as
+    /// `<<T as Tr1>::Assoc as Tr2>::Assoc`, consider the item bounds
+    /// on both `Tr1::Assoc` and `Tr2::Assoc`, since we may encounter
+    /// relative bounds on both via the `associated_type_bounds` feature.
+    pub(super) fn for_each_item_bound<T>(
         &mut self,
-        obligation: &PolyTraitObligation<'tcx>,
-    ) -> smallvec::SmallVec<[usize; 2]> {
-        let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
-        let placeholder_trait_predicate =
-            self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate);
-        debug!(?placeholder_trait_predicate);
-
-        let tcx = self.infcx.tcx;
-        let (def_id, args) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
-            ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
-                (def_id, args)
-            }
-            _ => {
-                span_bug!(
-                    obligation.cause.span,
-                    "match_projection_obligation_against_definition_bounds() called \
-                     but self-ty is not a projection: {:?}",
-                    placeholder_trait_predicate.trait_ref.self_ty()
-                );
-            }
-        };
-        let bounds = tcx.item_bounds(def_id).instantiate(tcx, args);
+        mut self_ty: Ty<'tcx>,
+        mut for_each: impl FnMut(&mut Self, ty::Clause<'tcx>, usize) -> ControlFlow<T, ()>,
+        on_ambiguity: impl FnOnce(),
+    ) -> ControlFlow<T, ()> {
+        let mut idx = 0;
+        loop {
+            let (kind, alias_ty) = match *self_ty.kind() {
+                ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty),
+                ty::Infer(ty::TyVar(_)) => {
+                    on_ambiguity();
+                    return ControlFlow::Continue(());
+                }
+                _ => return ControlFlow::Continue(()),
+            };
 
-        // The bounds returned by `item_bounds` may contain duplicates after
-        // normalization, so try to deduplicate when possible to avoid
-        // unnecessary ambiguity.
-        let mut distinct_normalized_bounds = FxHashSet::default();
+            for bound in
+                self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args)
+            {
+                for_each(self, bound, idx)?;
+                idx += 1;
+            }
 
-        bounds
-            .iter()
-            .enumerate()
-            .filter_map(|(idx, bound)| {
-                let bound_predicate = bound.kind();
-                if let ty::ClauseKind::Trait(pred) = bound_predicate.skip_binder() {
-                    let bound = bound_predicate.rebind(pred.trait_ref);
-                    if self.infcx.probe(|_| {
-                        match self.match_normalize_trait_ref(
-                            obligation,
-                            bound,
-                            placeholder_trait_predicate.trait_ref,
-                        ) {
-                            Ok(None) => true,
-                            Ok(Some(normalized_trait))
-                                if distinct_normalized_bounds.insert(normalized_trait) =>
-                            {
-                                true
-                            }
-                            _ => false,
-                        }
-                    }) {
-                        return Some(idx);
-                    }
-                }
-                None
-            })
-            .collect()
+            if kind == ty::Projection {
+                self_ty = alias_ty.self_ty();
+            } else {
+                return ControlFlow::Continue(());
+            }
+        }
     }
 
     /// Equates the trait in `obligation` with trait bound. If the two traits
@@ -2386,7 +2356,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             .flat_map(|ty| {
                 let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(*ty); // <----/
 
-                let placeholder_ty = self.infcx.instantiate_binder_with_placeholders(ty);
+                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(
@@ -2472,7 +2442,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result<Normalized<'tcx, GenericArgsRef<'tcx>>, ()> {
         let placeholder_obligation =
-            self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
+            self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref;
 
         let impl_args = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id);
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index de08e7d2f03..8e0fa79c977 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -446,7 +446,7 @@ fn report_conflicting_impls<'tcx>(
     match used_to_be_allowed {
         None => {
             let reported = if overlap.with_impl.is_local()
-                || tcx.orphan_check_impl(impl_def_id).is_ok()
+                || tcx.ensure().orphan_check_impl(impl_def_id).is_ok()
             {
                 let mut err = tcx.dcx().struct_span_err(impl_span, msg);
                 err.code(E0119);