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.rs169
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs198
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs45
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs152
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs951
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs424
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs381
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs45
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs1003
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs108
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs398
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs192
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs236
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/build.rs556
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs370
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs272
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs56
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs719
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs106
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs35
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs38
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph.rs360
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs1021
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs841
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs1148
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs231
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs272
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs101
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs274
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs183
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs910
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs5020
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs3597
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs826
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs224
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs549
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs930
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs132
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs2626
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs340
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs129
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/method_autoderef.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/mod.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs371
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs143
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs127
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs33
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs232
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs208
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs78
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs51
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs55
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs30
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs1107
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs1370
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs3116
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs514
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs431
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs174
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_normalize.rs60
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs384
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs408
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs977
64 files changed, 36061 insertions, 0 deletions
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
new file mode 100644
index 00000000000..bea6fbd6ac5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -0,0 +1,169 @@
+use crate::fluent_generated as fluent;
+use rustc_errors::{
+    AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, EmissionGuarantee,
+    IntoDiagnostic, Level, SubdiagnosticMessage,
+};
+use rustc_macros::Diagnostic;
+use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty};
+use rustc_span::{Span, Symbol};
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_dump_vtable_entries)]
+pub struct DumpVTableEntries<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub trait_ref: PolyTraitRef<'a>,
+    pub entries: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_unable_to_construct_constant_value)]
+pub struct UnableToConstructConstantValue<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub unevaluated: ty::UnevaluatedConst<'a>,
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_empty_on_clause_in_rustc_on_unimplemented, code = "E0232")]
+pub struct EmptyOnClauseInOnUnimplemented {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_invalid_on_clause_in_rustc_on_unimplemented, code = "E0232")]
+pub struct InvalidOnClauseInOnUnimplemented {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_no_value_in_rustc_on_unimplemented, code = "E0232")]
+#[note]
+pub struct NoValueInOnUnimplemented {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+pub struct NegativePositiveConflict<'tcx> {
+    pub impl_span: Span,
+    pub trait_desc: ty::TraitRef<'tcx>,
+    pub self_ty: Option<Ty<'tcx>>,
+    pub negative_impl_span: Result<Span, Symbol>,
+    pub positive_impl_span: Result<Span, Symbol>,
+}
+
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for NegativePositiveConflict<'_> {
+    #[track_caller]
+    fn into_diagnostic(
+        self,
+        dcx: &DiagCtxt,
+        level: Level,
+    ) -> rustc_errors::DiagnosticBuilder<'_, G> {
+        let mut diag =
+            DiagnosticBuilder::new(dcx, level, fluent::trait_selection_negative_positive_conflict);
+        diag.set_arg("trait_desc", self.trait_desc.print_only_trait_path().to_string());
+        diag.set_arg(
+            "self_desc",
+            self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string()),
+        );
+        diag.set_span(self.impl_span);
+        diag.code(rustc_errors::error_code!(E0751));
+        match self.negative_impl_span {
+            Ok(span) => {
+                diag.span_label(span, fluent::trait_selection_negative_implementation_here);
+            }
+            Err(cname) => {
+                diag.note(fluent::trait_selection_negative_implementation_in_crate);
+                diag.set_arg("negative_impl_cname", cname.to_string());
+            }
+        }
+        match self.positive_impl_span {
+            Ok(span) => {
+                diag.span_label(span, fluent::trait_selection_positive_implementation_here);
+            }
+            Err(cname) => {
+                diag.note(fluent::trait_selection_positive_implementation_in_crate);
+                diag.set_arg("positive_impl_cname", cname.to_string());
+            }
+        }
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_inherent_projection_normalization_overflow)]
+pub struct InherentProjectionNormalizationOverflow {
+    #[primary_span]
+    pub span: Span,
+    pub ty: String,
+}
+
+pub enum AdjustSignatureBorrow {
+    Borrow { to_borrow: Vec<(Span, String)> },
+    RemoveBorrow { remove_borrow: Vec<(Span, String)> },
+}
+
+impl AddToDiagnostic for AdjustSignatureBorrow {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
+        match self {
+            AdjustSignatureBorrow::Borrow { to_borrow } => {
+                diag.set_arg("len", to_borrow.len());
+                diag.multipart_suggestion_verbose(
+                    fluent::trait_selection_adjust_signature_borrow,
+                    to_borrow,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => {
+                diag.set_arg("len", remove_borrow.len());
+                diag.multipart_suggestion_verbose(
+                    fluent::trait_selection_adjust_signature_remove_borrow,
+                    remove_borrow,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_closure_kind_mismatch, code = "E0525")]
+pub struct ClosureKindMismatch {
+    #[primary_span]
+    #[label]
+    pub closure_span: Span,
+    pub expected: ClosureKind,
+    pub found: ClosureKind,
+    #[label(trait_selection_closure_kind_requirement)]
+    pub cause_span: Span,
+
+    #[subdiagnostic]
+    pub fn_once_label: Option<ClosureFnOnceLabel>,
+
+    #[subdiagnostic]
+    pub fn_mut_label: Option<ClosureFnMutLabel>,
+}
+
+#[derive(Subdiagnostic)]
+#[label(trait_selection_closure_fn_once_label)]
+pub struct ClosureFnOnceLabel {
+    #[primary_span]
+    pub span: Span,
+    pub place: String,
+}
+
+#[derive(Subdiagnostic)]
+#[label(trait_selection_closure_fn_mut_label)]
+pub struct ClosureFnMutLabel {
+    #[primary_span]
+    pub span: Span,
+    pub place: String,
+}
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
new file mode 100644
index 00000000000..251f0628a71
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -0,0 +1,198 @@
+use crate::solve::FulfillmentCtxt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use crate::traits::{self, DefiningAnchor, ObligationCtxt};
+
+use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
+use rustc_infer::traits::{TraitEngine, TraitEngineExt};
+use rustc_middle::arena::ArenaAllocatable;
+use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse};
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
+use rustc_middle::ty::{GenericArg, ToPredicate};
+use rustc_span::DUMMY_SP;
+
+use std::fmt::Debug;
+
+pub use rustc_infer::infer::*;
+
+pub trait InferCtxtExt<'tcx> {
+    fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool;
+
+    fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool;
+
+    /// Check whether a `ty` implements given trait(trait_def_id).
+    /// The inputs are:
+    ///
+    /// - the def-id of the trait
+    /// - the type parameters of the trait, including the self-type
+    /// - the parameter environment
+    ///
+    /// Invokes `evaluate_obligation`, so in the event that evaluating
+    /// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent (or EvaluatedToAmbigStackDependent)
+    /// will be returned.
+    fn type_implements_trait(
+        &self,
+        trait_def_id: DefId,
+        params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> traits::EvaluationResult;
+
+    fn could_impl_trait(
+        &self,
+        trait_def_id: DefId,
+        ty: Ty<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<Vec<traits::FulfillmentError<'tcx>>>;
+}
+
+impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
+    fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+        let ty = self.resolve_vars_if_possible(ty);
+
+        if !(param_env, ty).has_infer() {
+            return ty.is_copy_modulo_regions(self.tcx, param_env);
+        }
+
+        let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None);
+
+        // This can get called from typeck (by euv), and `moves_by_default`
+        // rightly refuses to work with inference variables, but
+        // moves_by_default has a cache, which we want to use in other
+        // cases.
+        traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id)
+    }
+
+    fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+        let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
+        traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item)
+    }
+
+    #[instrument(level = "debug", skip(self, params), ret)]
+    fn type_implements_trait(
+        &self,
+        trait_def_id: DefId,
+        params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> traits::EvaluationResult {
+        let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, params);
+
+        let obligation = traits::Obligation {
+            cause: traits::ObligationCause::dummy(),
+            param_env,
+            recursion_depth: 0,
+            predicate: ty::Binder::dummy(trait_ref).to_predicate(self.tcx),
+        };
+        self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr)
+    }
+
+    fn could_impl_trait(
+        &self,
+        trait_def_id: DefId,
+        ty: Ty<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<Vec<traits::FulfillmentError<'tcx>>> {
+        self.probe(|_snapshot| {
+            if let ty::Adt(def, args) = ty.kind()
+                && let Some((impl_def_id, _)) = self
+                    .tcx
+                    .all_impls(trait_def_id)
+                    .filter_map(|impl_def_id| {
+                        self.tcx.impl_trait_ref(impl_def_id).map(|r| (impl_def_id, r))
+                    })
+                    .map(|(impl_def_id, imp)| (impl_def_id, imp.skip_binder()))
+                    .find(|(_, imp)| match imp.self_ty().peel_refs().kind() {
+                        ty::Adt(i_def, _) if i_def.did() == def.did() => true,
+                        _ => false,
+                    })
+            {
+                let mut fulfill_cx = FulfillmentCtxt::new(self);
+                // We get all obligations from the impl to talk about specific
+                // trait bounds.
+                let obligations = self
+                    .tcx
+                    .predicates_of(impl_def_id)
+                    .instantiate(self.tcx, args)
+                    .into_iter()
+                    .map(|(clause, span)| {
+                        traits::Obligation::new(
+                            self.tcx,
+                            traits::ObligationCause::dummy_with_span(span),
+                            param_env,
+                            clause,
+                        )
+                    })
+                    .collect::<Vec<_>>();
+                fulfill_cx.register_predicate_obligations(self, obligations);
+                let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty]);
+                let obligation = traits::Obligation::new(
+                    self.tcx,
+                    traits::ObligationCause::dummy(),
+                    param_env,
+                    trait_ref,
+                );
+                fulfill_cx.register_predicate_obligation(self, obligation);
+                let mut errors = fulfill_cx.select_all_or_error(self);
+                // We remove the last predicate failure, which corresponds to
+                // the top-level obligation, because most of the type we only
+                // care about the other ones, *except* when it is the only one.
+                // This seems to only be relevant for arbitrary self-types.
+                // Look at `tests/ui/moves/move-fn-self-receiver.rs`.
+                if errors.len() > 1 {
+                    errors.truncate(errors.len() - 1);
+                }
+                Some(errors)
+            } else {
+                None
+            }
+        })
+    }
+}
+
+pub trait InferCtxtBuilderExt<'tcx> {
+    fn enter_canonical_trait_query<K, R>(
+        self,
+        canonical_key: &Canonical<'tcx, K>,
+        operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>,
+    ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution>
+    where
+        K: TypeFoldable<TyCtxt<'tcx>>,
+        R: Debug + TypeFoldable<TyCtxt<'tcx>>,
+        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>;
+}
+
+impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
+    /// The "main method" for a canonicalized trait query. Given the
+    /// canonical key `canonical_key`, this method will create a new
+    /// inference context, instantiate the key, and run your operation
+    /// `op`. The operation should yield up a result (of type `R`) as
+    /// well as a set of trait obligations that must be fully
+    /// satisfied. These obligations will be processed and the
+    /// canonical result created.
+    ///
+    /// Returns `NoSolution` in the event of any error.
+    ///
+    /// (It might be mildly nicer to implement this on `TyCtxt`, and
+    /// not `InferCtxtBuilder`, but that is a bit tricky right now.
+    /// In part because we would need a `for<'tcx>` sort of
+    /// bound for the closure and in part because it is convenient to
+    /// have `'tcx` be free on this function so that we can talk about
+    /// `K: TypeFoldable<TyCtxt<'tcx>>`.)
+    fn enter_canonical_trait_query<K, R>(
+        self,
+        canonical_key: &Canonical<'tcx, K>,
+        operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>,
+    ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution>
+    where
+        K: TypeFoldable<TyCtxt<'tcx>>,
+        R: Debug + TypeFoldable<TyCtxt<'tcx>>,
+        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
+    {
+        let (infcx, key, canonical_inference_vars) = self
+            .with_opaque_type_inference(DefiningAnchor::Bubble)
+            .build_with_canonical(DUMMY_SP, canonical_key);
+        let ocx = ObligationCtxt::new(&infcx);
+        let value = operation(&ocx, key)?;
+        ocx.make_canonicalized_query_response(canonical_inference_vars, value)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
new file mode 100644
index 00000000000..de2577cca49
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -0,0 +1,45 @@
+//! This crate defines the trait resolution method.
+//!
+//! - **Traits.** Trait resolution is implemented in the `traits` module.
+//!
+//! For more information about how rustc works, see the [rustc-dev-guide].
+//!
+//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
+//!
+//! # Note
+//!
+//! This API is completely unstable and subject to change.
+
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![doc(rust_logo)]
+#![feature(rustdoc_internals)]
+#![allow(internal_features)]
+#![feature(associated_type_bounds)]
+#![feature(box_patterns)]
+#![feature(control_flow_enum)]
+#![feature(extract_if)]
+#![feature(let_chains)]
+#![feature(if_let_guard)]
+#![feature(never_type)]
+#![feature(type_alias_impl_trait)]
+#![feature(min_specialization)]
+#![recursion_limit = "512"] // For rustdoc
+
+#[macro_use]
+extern crate rustc_macros;
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[macro_use]
+extern crate rustc_data_structures;
+#[macro_use]
+extern crate tracing;
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate smallvec;
+
+pub mod errors;
+pub mod infer;
+pub mod solve;
+pub mod traits;
+
+rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
new file mode 100644
index 00000000000..626569fb40f
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -0,0 +1,152 @@
+//! Implements the `AliasRelate` goal, which is used when unifying aliases.
+//! Doing this via a separate goal is called "deferred alias relation" and part
+//! of our more general approach to "lazy normalization".
+//!
+//! This goal, e.g. `A alias-relate B`, may be satisfied by one of three branches:
+//! * normalizes-to: If `A` is a projection, we can prove the equivalent
+//!   projection predicate with B as the right-hand side of the projection.
+//!   This goal is computed in both directions, if both are aliases.
+//! * subst-relate: Equate `A` and `B` by their substs, if they're both
+//!   aliases with the same def-id.
+//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both
+//!   may apply, then we can compute the "intersection" of both normalizes-to by
+//!   performing them together. This is used specifically to resolve ambiguities.
+use super::{EvalCtxt, GoalSource};
+use rustc_infer::infer::DefineOpaqueTypes;
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    #[instrument(level = "debug", skip(self), ret)]
+    pub(super) fn compute_alias_relate_goal(
+        &mut self,
+        goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
+    ) -> QueryResult<'tcx> {
+        let tcx = self.tcx();
+        let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
+
+        let Some(lhs) = self.try_normalize_term(param_env, lhs)? else {
+            return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
+        };
+
+        let Some(rhs) = self.try_normalize_term(param_env, rhs)? else {
+            return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
+        };
+
+        let variance = match direction {
+            ty::AliasRelationDirection::Equate => ty::Variance::Invariant,
+            ty::AliasRelationDirection::Subtype => ty::Variance::Covariant,
+        };
+
+        match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
+            (None, None) => {
+                self.relate(param_env, lhs, variance, rhs)?;
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            }
+
+            (Some(alias), None) => {
+                if rhs.is_infer() {
+                    self.relate(param_env, lhs, variance, rhs)?;
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                } else if alias.is_opaque(tcx) {
+                    self.define_opaque(param_env, alias, rhs)
+                } else {
+                    Err(NoSolution)
+                }
+            }
+            (None, Some(alias)) => {
+                if lhs.is_infer() {
+                    self.relate(param_env, lhs, variance, rhs)?;
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                } else if alias.is_opaque(tcx) {
+                    self.define_opaque(param_env, alias, lhs)
+                } else {
+                    Err(NoSolution)
+                }
+            }
+
+            (Some(alias_lhs), Some(alias_rhs)) => {
+                self.relate_rigid_alias_or_opaque(param_env, alias_lhs, variance, alias_rhs)
+            }
+        }
+    }
+
+    /// Normalize the `term` to equate it later. This does not define opaque types.
+    #[instrument(level = "debug", skip(self, param_env), ret)]
+    fn try_normalize_term(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        term: ty::Term<'tcx>,
+    ) -> Result<Option<ty::Term<'tcx>>, NoSolution> {
+        match term.unpack() {
+            ty::TermKind::Ty(ty) => {
+                // We do no define opaque types here but instead do so in `relate_rigid_alias_or_opaque`.
+                Ok(self
+                    .try_normalize_ty_recur(param_env, DefineOpaqueTypes::No, 0, ty)
+                    .map(Into::into))
+            }
+            ty::TermKind::Const(_) => {
+                if let Some(alias) = term.to_alias_ty(self.tcx()) {
+                    let term = self.next_term_infer_of_kind(term);
+                    self.add_goal(
+                        GoalSource::Misc,
+                        Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }),
+                    );
+                    self.try_evaluate_added_goals()?;
+                    Ok(Some(self.resolve_vars_if_possible(term)))
+                } else {
+                    Ok(Some(term))
+                }
+            }
+        }
+    }
+
+    fn define_opaque(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        opaque: ty::AliasTy<'tcx>,
+        term: ty::Term<'tcx>,
+    ) -> QueryResult<'tcx> {
+        self.add_goal(
+            GoalSource::Misc,
+            Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }),
+        );
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn relate_rigid_alias_or_opaque(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        lhs: ty::AliasTy<'tcx>,
+        variance: ty::Variance,
+        rhs: ty::AliasTy<'tcx>,
+    ) -> QueryResult<'tcx> {
+        let tcx = self.tcx();
+        let mut candidates = vec![];
+        if lhs.is_opaque(tcx) {
+            candidates.extend(
+                self.probe_misc_candidate("define-lhs-opaque")
+                    .enter(|ecx| ecx.define_opaque(param_env, lhs, rhs.to_ty(tcx).into())),
+            );
+        }
+
+        if rhs.is_opaque(tcx) {
+            candidates.extend(
+                self.probe_misc_candidate("define-rhs-opaque")
+                    .enter(|ecx| ecx.define_opaque(param_env, rhs, lhs.to_ty(tcx).into())),
+            );
+        }
+
+        candidates.extend(self.probe_misc_candidate("args-relate").enter(|ecx| {
+            ecx.relate(param_env, lhs, variance, rhs)?;
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        }));
+
+        if let Some(result) = self.try_merge_responses(&candidates) {
+            Ok(result)
+        } else {
+            self.flounder(&candidates)
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
new file mode 100644
index 00000000000..caf9470b4c6
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -0,0 +1,951 @@
+//! Code shared by trait and projection goals for candidate assembly.
+
+use super::{EvalCtxt, SolverMode};
+use crate::solve::GoalSource;
+use crate::traits::coherence;
+use rustc_hir::def_id::DefId;
+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,
+};
+use rustc_middle::traits::BuiltinImplSource;
+use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{fast_reject, TypeFoldable};
+use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
+use rustc_span::ErrorGuaranteed;
+use std::fmt::Debug;
+
+pub(super) mod structural_traits;
+
+/// A candidate is a possible way to prove a goal.
+///
+/// It consists of both the `source`, which describes how that goal would be proven,
+/// and the `result` when using the given `source`.
+#[derive(Debug, Clone)]
+pub(super) struct Candidate<'tcx> {
+    pub(super) source: CandidateSource,
+    pub(super) result: CanonicalResponse<'tcx>,
+}
+
+/// Methods used to assemble candidates for either trait or projection goals.
+pub(super) trait GoalKind<'tcx>:
+    TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
+{
+    fn self_ty(self) -> Ty<'tcx>;
+
+    fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>;
+
+    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
+
+    fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
+
+    /// Try equating an assumption predicate against a goal's predicate. If it
+    /// holds, then execute the `then` callback, which should do any additional
+    /// work, then produce a response (typically by executing
+    /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
+    fn probe_and_match_goal_against_assumption(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        assumption: ty::Clause<'tcx>,
+        then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
+    ) -> QueryResult<'tcx>;
+
+    /// Consider a clause, which consists of a "assumption" and some "requirements",
+    /// to satisfy a goal. If the requirements hold, then attempt to satisfy our
+    /// goal by equating it with the assumption.
+    fn consider_implied_clause(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        assumption: ty::Clause<'tcx>,
+        requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
+    ) -> QueryResult<'tcx> {
+        Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
+            // FIXME(-Znext-solver=coinductive): check whether this should be
+            // `GoalSource::ImplWhereBound` for any caller.
+            ecx.add_goals(GoalSource::Misc, requirements);
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    /// Consider a bound originating from the item bounds of an alias. For this we
+    /// require that the well-formed requirements of the self type of the goal
+    /// are "satisfied from the param-env".
+    /// See [`EvalCtxt::validate_alias_bound_self_from_param_env`].
+    fn consider_alias_bound_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        assumption: ty::Clause<'tcx>,
+    ) -> QueryResult<'tcx> {
+        Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
+            ecx.validate_alias_bound_self_from_param_env(goal)
+        })
+    }
+
+    /// Consider a clause specifically for a `dyn Trait` self type. This requires
+    /// additionally checking all of the supertraits and object bounds to hold,
+    /// since they're not implied by the well-formedness of the object type.
+    fn consider_object_bound_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        assumption: ty::Clause<'tcx>,
+    ) -> QueryResult<'tcx> {
+        Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
+            let tcx = ecx.tcx();
+            let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
+                bug!("expected object type in `consider_object_bound_candidate`");
+            };
+            // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
+            ecx.add_goals(
+                GoalSource::Misc,
+                structural_traits::predicates_for_object_candidate(
+                    ecx,
+                    goal.param_env,
+                    goal.predicate.trait_ref(tcx),
+                    bounds,
+                ),
+            );
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    fn consider_impl_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        impl_def_id: DefId,
+    ) -> Result<Candidate<'tcx>, NoSolution>;
+
+    /// If the predicate contained an error, we want to avoid emitting unnecessary trait
+    /// errors but still want to emit errors for other trait goals. We have some special
+    /// handling for this case.
+    ///
+    /// Trait goals always hold while projection goals never do. This is a bit arbitrary
+    /// but prevents incorrect normalization while hiding any trait errors.
+    fn consider_error_guaranteed_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        guar: ErrorGuaranteed,
+    ) -> QueryResult<'tcx>;
+
+    /// A type implements an `auto trait` if its components do as well.
+    ///
+    /// These components are given by built-in rules from
+    /// [`structural_traits::instantiate_constituent_tys_for_auto_trait`].
+    fn consider_auto_trait_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// A trait alias holds if the RHS traits and `where` clauses hold.
+    fn consider_trait_alias_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// A type is `Copy` or `Clone` if its components are `Sized`.
+    ///
+    /// These components are given by built-in rules from
+    /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`].
+    fn consider_builtin_sized_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`.
+    ///
+    /// These components are given by built-in rules from
+    /// [`structural_traits::instantiate_constituent_tys_for_copy_clone_trait`].
+    fn consider_builtin_copy_clone_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// A type is `PointerLike` if we can compute its layout, and that layout
+    /// matches the layout of `usize`.
+    fn consider_builtin_pointer_like_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// A type is a `FnPtr` if it is of `FnPtr` type.
+    fn consider_builtin_fn_ptr_trait_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
+    /// family of traits where `A` is given by the signature of the type.
+    fn consider_builtin_fn_trait_candidates(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        kind: ty::ClosureKind,
+    ) -> QueryResult<'tcx>;
+
+    /// `Tuple` is implemented if the `Self` type is a tuple.
+    fn consider_builtin_tuple_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// `Pointee` is always implemented.
+    ///
+    /// See the projection implementation for the `Metadata` types for all of
+    /// the built-in types. For structs, the metadata type is given by the struct
+    /// tail.
+    fn consider_builtin_pointee_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// A coroutine (that comes from an `async` desugaring) is known to implement
+    /// `Future<Output = O>`, where `O` is given by the coroutine's return type
+    /// that was computed during type-checking.
+    fn consider_builtin_future_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// A coroutine (that comes from a `gen` desugaring) is known to implement
+    /// `Iterator<Item = O>`, where `O` is given by the generator's yield type
+    /// that was computed during type-checking.
+    fn consider_builtin_iterator_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    fn consider_builtin_async_iterator_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to
+    /// implement `Coroutine<R, Yield = Y, Return = O>`, given the resume, yield,
+    /// and return types of the coroutine computed during type-checking.
+    fn consider_builtin_coroutine_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    fn consider_builtin_discriminant_kind_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    fn consider_builtin_destruct_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    fn consider_builtin_transmute_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    /// Consider (possibly several) candidates to upcast or unsize a type to another
+    /// type, excluding the coercion of a sized type into a `dyn Trait`.
+    ///
+    /// We return the `BuiltinImplSource` for each candidate as it is needed
+    /// for unsize coercion in hir typeck and because it is difficult to
+    /// otherwise recompute this for codegen. This is a bit of a mess but the
+    /// easiest way to maintain the existing behavior for now.
+    fn consider_structural_builtin_unsize_candidates(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
+
+    /// Consider the `Unsize` candidate corresponding to coercing a sized type
+    /// into a `dyn Trait`.
+    ///
+    /// This is computed separately from the rest of the `Unsize` candidates
+    /// since it is only done once per self type, and not once per
+    /// *normalization step* (in `assemble_candidates_via_self_ty`).
+    fn consider_unsize_to_dyn_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+    ) -> Vec<Candidate<'tcx>> {
+        debug_assert_eq!(goal, self.resolve_vars_if_possible(goal));
+        if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) {
+            return vec![ambig];
+        }
+
+        let mut candidates = self.assemble_candidates_via_self_ty(goal, 0);
+
+        self.assemble_unsize_to_dyn_candidate(goal, &mut candidates);
+
+        self.assemble_blanket_impl_candidates(goal, &mut candidates);
+
+        self.assemble_param_env_candidates(goal, &mut candidates);
+
+        self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
+
+        candidates
+    }
+
+    /// `?0: Trait` is ambiguous, because it may be satisfied via a builtin rule,
+    /// object bound, alias bound, etc. We are unable to determine this until we can at
+    /// least structurally resolve the type one layer.
+    ///
+    /// It would also require us to consider all impls of the trait, which is both pretty
+    /// bad for perf and would also constrain the self type if there is just a single impl.
+    fn assemble_self_ty_infer_ambiguity_response<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+    ) -> Option<Candidate<'tcx>> {
+        if goal.predicate.self_ty().is_ty_var() {
+            debug!("adding self_ty_infer_ambiguity_response");
+            let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
+            let result = self
+                .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+                .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);
+            Some(Candidate { source, result })
+        } else {
+            None
+        }
+    }
+
+    /// Assemble candidates which apply to the self type. This only looks at candidate which
+    /// apply to the specific self type and ignores all others.
+    ///
+    /// Returns `None` if the self type is still ambiguous.
+    fn assemble_candidates_via_self_ty<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        num_steps: usize,
+    ) -> Vec<Candidate<'tcx>> {
+        debug_assert_eq!(goal, self.resolve_vars_if_possible(goal));
+        if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) {
+            return vec![ambig];
+        }
+
+        let mut candidates = Vec::new();
+
+        self.assemble_non_blanket_impl_candidates(goal, &mut candidates);
+
+        self.assemble_builtin_impl_candidates(goal, &mut candidates);
+
+        self.assemble_alias_bound_candidates(goal, &mut candidates);
+
+        self.assemble_object_bound_candidates(goal, &mut candidates);
+
+        self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates, num_steps);
+        candidates
+    }
+
+    /// If the self type of a goal is an alias we first try to normalize the self type
+    /// and compute the candidates for the normalized self type in case that succeeds.
+    ///
+    /// These candidates are used in addition to the ones with the alias as a self type.
+    /// We do this to simplify both builtin candidates and for better performance.
+    ///
+    /// We generate the builtin candidates on the fly by looking at the self type, e.g.
+    /// add `FnPtr` candidates if the self type is a function pointer. Handling builtin
+    /// candidates while the self type is still an alias seems difficult. This is similar
+    /// to `try_structurally_resolve_type` during hir typeck (FIXME once implemented).
+    ///
+    /// Looking at all impls for some trait goal is prohibitively expensive. We therefore
+    /// only look at implementations with a matching self type. Because of this function,
+    /// we can avoid looking at all existing impls if the self type is an alias.
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+        num_steps: usize,
+    ) {
+        let tcx = self.tcx();
+        let &ty::Alias(_, alias) = goal.predicate.self_ty().kind() else { return };
+
+        candidates.extend(self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
+            if tcx.recursion_limit().value_within_limit(num_steps) {
+                let normalized_ty = ecx.next_ty_infer();
+                let normalizes_to_goal =
+                    goal.with(tcx, ty::NormalizesTo { alias, term: normalized_ty.into() });
+                ecx.add_goal(GoalSource::Misc, normalizes_to_goal);
+                if let Err(NoSolution) = ecx.try_evaluate_added_goals() {
+                    debug!("self type normalization failed");
+                    return vec![];
+                }
+                let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
+                debug!(?normalized_ty, "self type normalized");
+                // NOTE: Alternatively we could call `evaluate_goal` here and only
+                // have a `Normalized` candidate. This doesn't work as long as we
+                // use `CandidateSource` in winnowing.
+                let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
+                ecx.assemble_candidates_via_self_ty(goal, num_steps + 1)
+            } else {
+                match ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) {
+                    Ok(result) => vec![Candidate {
+                        source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                        result,
+                    }],
+                    Err(NoSolution) => vec![],
+                }
+            }
+        }));
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        let self_ty = goal.predicate.self_ty();
+        let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
+        let mut consider_impls_for_simplified_type = |simp| {
+            if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) {
+                for &impl_def_id in impls_for_type {
+                    match G::consider_impl_candidate(self, goal, impl_def_id) {
+                        Ok(candidate) => candidates.push(candidate),
+                        Err(NoSolution) => (),
+                    }
+                }
+            }
+        };
+
+        match self_ty.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Closure(_, _)
+            | ty::Coroutine(_, _)
+            | ty::Never
+            | ty::Tuple(_) => {
+                let simp =
+                    fast_reject::simplify_type(tcx, self_ty, TreatParams::ForLookup).unwrap();
+                consider_impls_for_simplified_type(simp);
+            }
+
+            // HACK: For integer and float variables we have to manually look at all impls
+            // which have some integer or float as a self type.
+            ty::Infer(ty::IntVar(_)) => {
+                use ty::IntTy::*;
+                use ty::UintTy::*;
+                // This causes a compiler error if any new integer kinds are added.
+                let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy;
+                let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy;
+                let possible_integers = [
+                    // signed integers
+                    SimplifiedType::Int(I8),
+                    SimplifiedType::Int(I16),
+                    SimplifiedType::Int(I32),
+                    SimplifiedType::Int(I64),
+                    SimplifiedType::Int(I128),
+                    SimplifiedType::Int(Isize),
+                    // unsigned integers
+                    SimplifiedType::Uint(U8),
+                    SimplifiedType::Uint(U16),
+                    SimplifiedType::Uint(U32),
+                    SimplifiedType::Uint(U64),
+                    SimplifiedType::Uint(U128),
+                    SimplifiedType::Uint(Usize),
+                ];
+                for simp in possible_integers {
+                    consider_impls_for_simplified_type(simp);
+                }
+            }
+
+            ty::Infer(ty::FloatVar(_)) => {
+                // This causes a compiler error if any new float kinds are added.
+                let (ty::FloatTy::F32 | ty::FloatTy::F64);
+                let possible_floats = [
+                    SimplifiedType::Float(ty::FloatTy::F32),
+                    SimplifiedType::Float(ty::FloatTy::F64),
+                ];
+
+                for simp in possible_floats {
+                    consider_impls_for_simplified_type(simp);
+                }
+            }
+
+            // The only traits applying to aliases and placeholders are blanket impls.
+            //
+            // Impls which apply to an alias after normalization are handled by
+            // `assemble_candidates_after_normalizing_self_ty`.
+            ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (),
+
+            // FIXME: These should ideally not exist as a self type. It would be nice for
+            // the builtin auto trait impls of coroutines to instead directly recurse
+            // into the witness.
+            ty::CoroutineWitness(..) => (),
+
+            // These variants should not exist as a self type.
+            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+            | ty::Param(_)
+            | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"),
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_unsize_to_dyn_candidate<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        if tcx.lang_items().unsize_trait() == Some(goal.predicate.trait_def_id(tcx)) {
+            match G::consider_unsize_to_dyn_candidate(self, goal) {
+                Ok(result) => candidates.push(Candidate {
+                    source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                    result,
+                }),
+                Err(NoSolution) => (),
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
+        for &impl_def_id in trait_impls.blanket_impls() {
+            match G::consider_impl_candidate(self, goal, impl_def_id) {
+                Ok(candidate) => candidates.push(candidate),
+                Err(NoSolution) => (),
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        let lang_items = tcx.lang_items();
+        let trait_def_id = goal.predicate.trait_def_id(tcx);
+
+        // N.B. When assembling built-in candidates for lang items that are also
+        // `auto` traits, then the auto trait candidate that is assembled in
+        // `consider_auto_trait_candidate` MUST be disqualified to remain sound.
+        //
+        // Instead of adding the logic here, it's a better idea to add it in
+        // `EvalCtxt::disqualify_auto_trait_candidate_due_to_possible_impl` in
+        // `solve::trait_goals` instead.
+        let result = if let Err(guar) = goal.predicate.error_reported() {
+            G::consider_error_guaranteed_candidate(self, guar)
+        } else if tcx.trait_is_auto(trait_def_id) {
+            G::consider_auto_trait_candidate(self, goal)
+        } else if tcx.trait_is_alias(trait_def_id) {
+            G::consider_trait_alias_candidate(self, goal)
+        } else if lang_items.sized_trait() == Some(trait_def_id) {
+            G::consider_builtin_sized_candidate(self, goal)
+        } else if lang_items.copy_trait() == Some(trait_def_id)
+            || lang_items.clone_trait() == Some(trait_def_id)
+        {
+            G::consider_builtin_copy_clone_candidate(self, goal)
+        } else if lang_items.pointer_like() == Some(trait_def_id) {
+            G::consider_builtin_pointer_like_candidate(self, goal)
+        } else if lang_items.fn_ptr_trait() == Some(trait_def_id) {
+            G::consider_builtin_fn_ptr_trait_candidate(self, goal)
+        } else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) {
+            G::consider_builtin_fn_trait_candidates(self, goal, kind)
+        } else if lang_items.tuple_trait() == Some(trait_def_id) {
+            G::consider_builtin_tuple_candidate(self, goal)
+        } else if lang_items.pointee_trait() == Some(trait_def_id) {
+            G::consider_builtin_pointee_candidate(self, goal)
+        } else if lang_items.future_trait() == Some(trait_def_id) {
+            G::consider_builtin_future_candidate(self, goal)
+        } else if lang_items.iterator_trait() == Some(trait_def_id) {
+            G::consider_builtin_iterator_candidate(self, goal)
+        } else if lang_items.async_iterator_trait() == Some(trait_def_id) {
+            G::consider_builtin_async_iterator_candidate(self, goal)
+        } else if lang_items.coroutine_trait() == Some(trait_def_id) {
+            G::consider_builtin_coroutine_candidate(self, goal)
+        } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
+            G::consider_builtin_discriminant_kind_candidate(self, goal)
+        } else if lang_items.destruct_trait() == Some(trait_def_id) {
+            G::consider_builtin_destruct_candidate(self, goal)
+        } else if lang_items.transmute_trait() == Some(trait_def_id) {
+            G::consider_builtin_transmute_candidate(self, goal)
+        } else {
+            Err(NoSolution)
+        };
+
+        match result {
+            Ok(result) => candidates.push(Candidate {
+                source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                result,
+            }),
+            Err(NoSolution) => (),
+        }
+
+        // There may be multiple unsize candidates for a trait with several supertraits:
+        // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
+        if lang_items.unsize_trait() == Some(trait_def_id) {
+            for (result, source) in G::consider_structural_builtin_unsize_candidates(self, goal) {
+                candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_param_env_candidates<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
+            match G::consider_implied_clause(self, goal, assumption, []) {
+                Ok(result) => {
+                    candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result })
+                }
+                Err(NoSolution) => (),
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_alias_bound_candidates<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let alias_ty = match goal.predicate.self_ty().kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(..)
+            | ty::Closure(..)
+            | ty::Coroutine(..)
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Tuple(_)
+            | 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,
+        };
+
+        for assumption in
+            self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args)
+        {
+            match G::consider_alias_bound_candidate(self, goal, assumption) {
+                Ok(result) => {
+                    candidates.push(Candidate { source: CandidateSource::AliasBound, result })
+                }
+                Err(NoSolution) => (),
+            }
+        }
+    }
+
+    /// Check that we are allowed to use an alias bound originating from the self
+    /// type of this goal. This means something different depending on the self type's
+    /// alias kind.
+    ///
+    /// * Projection: Given a goal with a self type such as `<Ty as Trait>::Assoc`,
+    /// we require that the bound `Ty: Trait` can be proven using either a nested alias
+    /// bound candidate, or a param-env candidate.
+    ///
+    /// * Opaque: The param-env must be in `Reveal::UserFacing` mode. Otherwise,
+    /// the goal should be proven by using the hidden type instead.
+    #[instrument(level = "debug", skip(self), ret)]
+    pub(super) fn validate_alias_bound_self_from_param_env<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+    ) -> QueryResult<'tcx> {
+        match *goal.predicate.self_ty().kind() {
+            ty::Alias(ty::Projection, projection_ty) => {
+                let mut param_env_candidates = vec![];
+                let self_trait_ref = projection_ty.trait_ref(self.tcx());
+
+                if self_trait_ref.self_ty().is_ty_var() {
+                    return self
+                        .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+                }
+
+                let trait_goal: Goal<'_, ty::TraitPredicate<'tcx>> = goal.with(
+                    self.tcx(),
+                    ty::TraitPredicate {
+                        trait_ref: self_trait_ref,
+                        polarity: ty::ImplPolarity::Positive,
+                    },
+                );
+
+                self.assemble_param_env_candidates(trait_goal, &mut param_env_candidates);
+                // FIXME: We probably need some sort of recursion depth check here.
+                // Can't come up with an example yet, though, and the worst case
+                // we can have is a compiler stack overflow...
+                self.assemble_alias_bound_candidates(trait_goal, &mut param_env_candidates);
+
+                // FIXME: We must also consider alias-bound candidates for a peculiar
+                // class of built-in candidates that I'll call "defaulted" built-ins.
+                //
+                // For example, we always know that `T: Pointee` is implemented, but
+                // we do not always know what `<T as Pointee>::Metadata` actually is,
+                // similar to if we had a user-defined impl with a `default type ...`.
+                // For these traits, since we're not able to always normalize their
+                // associated types to a concrete type, we must consider their alias bounds
+                // instead, so we can prove bounds such as `<T as Pointee>::Metadata: Copy`.
+                self.assemble_alias_bound_candidates_for_builtin_impl_default_items(
+                    trait_goal,
+                    &mut param_env_candidates,
+                );
+
+                self.merge_candidates(param_env_candidates)
+            }
+            ty::Alias(ty::Opaque, _opaque_ty) => match goal.param_env.reveal() {
+                Reveal::UserFacing => {
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                }
+                Reveal::All => return Err(NoSolution),
+            },
+            _ => bug!("only expected to be called on alias tys"),
+        }
+    }
+
+    /// Assemble a subset of builtin impl candidates for a class of candidates called
+    /// "defaulted" built-in traits.
+    ///
+    /// For example, we always know that `T: Pointee` is implemented, but we do not
+    /// always know what `<T as Pointee>::Metadata` actually is! See the comment in
+    /// [`EvalCtxt::validate_alias_bound_self_from_param_env`] for more detail.
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_alias_bound_candidates_for_builtin_impl_default_items<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let lang_items = self.tcx().lang_items();
+        let trait_def_id = goal.predicate.trait_def_id(self.tcx());
+
+        // You probably shouldn't add anything to this list unless you
+        // know what you're doing.
+        let result = if lang_items.pointee_trait() == Some(trait_def_id) {
+            G::consider_builtin_pointee_candidate(self, goal)
+        } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
+            G::consider_builtin_discriminant_kind_candidate(self, goal)
+        } else {
+            Err(NoSolution)
+        };
+
+        match result {
+            Ok(result) => candidates.push(Candidate {
+                source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                result,
+            }),
+            Err(NoSolution) => (),
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_object_bound_candidates<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        if !tcx.trait_def(goal.predicate.trait_def_id(tcx)).implement_via_object {
+            return;
+        }
+
+        let self_ty = goal.predicate.self_ty();
+        let bounds = match *self_ty.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Alias(..)
+            | ty::Closure(..)
+            | ty::Coroutine(..)
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Tuple(_)
+            | ty::Param(_)
+            | ty::Placeholder(..)
+            | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+            | ty::Error(_) => return,
+            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+            | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
+            ty::Dynamic(bounds, ..) => bounds,
+        };
+
+        // Do not consider built-in object impls for non-object-safe types.
+        if bounds.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
+            return;
+        }
+
+        // Consider all of the auto-trait and projection bounds, which don't
+        // need to be recorded as a `BuiltinImplSource::Object` since they don't
+        // really have a vtable base...
+        for bound in bounds {
+            match bound.skip_binder() {
+                ty::ExistentialPredicate::Trait(_) => {
+                    // Skip principal
+                }
+                ty::ExistentialPredicate::Projection(_)
+                | ty::ExistentialPredicate::AutoTrait(_) => {
+                    match G::consider_object_bound_candidate(
+                        self,
+                        goal,
+                        bound.with_self_ty(tcx, self_ty),
+                    ) {
+                        Ok(result) => candidates.push(Candidate {
+                            source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                            result,
+                        }),
+                        Err(NoSolution) => (),
+                    }
+                }
+            }
+        }
+
+        // FIXME: We only need to do *any* of this if we're considering a trait goal,
+        // since we don't need to look at any supertrait or anything if we are doing
+        // a projection goal.
+        if let Some(principal) = bounds.principal() {
+            let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
+            self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| {
+                match G::consider_object_bound_candidate(ecx, goal, assumption.to_predicate(tcx)) {
+                    Ok(result) => candidates.push(Candidate {
+                        source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object {
+                            vtable_base,
+                        }),
+                        result,
+                    }),
+                    Err(NoSolution) => (),
+                }
+            });
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        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)]
+            struct Overflow;
+            let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) {
+                Some(ty) => Ok(ty),
+                None => Err(Overflow),
+            };
+
+            match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) {
+                Err(Overflow) => {
+                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
+                }
+                Ok(Ok(())) => Err(NoSolution),
+                Ok(Err(_)) => {
+                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+                }
+            }
+        });
+
+        match result {
+            Ok(result) => candidates.push(Candidate {
+                source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                result,
+            }),
+            Err(NoSolution) => {}
+        }
+    }
+
+    /// 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>>,
+    ) -> 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);
+        }
+
+        // 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
new file mode 100644
index 00000000000..274a75a125c
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -0,0 +1,424 @@
+//! Code which is used by built-in goals that match "structurally", such a auto
+//! traits, `Copy`/`Clone`.
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::{def_id::DefId, Movability, Mutability};
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::Goal;
+use rustc_middle::ty::{
+    self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
+};
+
+use crate::solve::EvalCtxt;
+
+// Calculates the constituent types of a type for `auto trait` purposes.
+//
+// For types with an "existential" binder, i.e. coroutine witnesses, we also
+// instantiate the binder with placeholders eagerly.
+#[instrument(level = "debug", skip(ecx), ret)]
+pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
+    ecx: &EvalCtxt<'_, 'tcx>,
+    ty: Ty<'tcx>,
+) -> Result<Vec<Ty<'tcx>>, NoSolution> {
+    let tcx = ecx.tcx();
+    match *ty.kind() {
+        ty::Uint(_)
+        | ty::Int(_)
+        | ty::Bool
+        | ty::Float(_)
+        | ty::FnDef(..)
+        | ty::FnPtr(_)
+        | ty::Error(_)
+        | ty::Never
+        | 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::Dynamic(..)
+        | ty::Param(..)
+        | ty::Foreign(..)
+        | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
+        | ty::Placeholder(..)
+        | ty::Bound(..)
+        | ty::Infer(_) => {
+            bug!("unexpected type `{ty}`")
+        }
+
+        ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => {
+            Ok(vec![element_ty])
+        }
+
+        ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]),
+
+        ty::Tuple(tys) => {
+            // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
+            Ok(tys.iter().collect())
+        }
+
+        ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]),
+
+        ty::Coroutine(_, args) => {
+            let coroutine_args = args.as_coroutine();
+            Ok(vec![coroutine_args.tupled_upvars_ty(), 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),
+                ))
+            })
+            .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) => Ok(def.all_fields().map(|f| 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)])
+        }
+    }
+}
+
+pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> ty::Binder<'tcx, Ty<'tcx>> {
+    debug_assert!(!ty.has_bound_regions());
+    let mut counter = 0;
+    let ty = tcx.fold_regions(ty, |r, current_depth| match r.kind() {
+        ty::ReErased => {
+            let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon };
+            counter += 1;
+            ty::Region::new_bound(tcx, current_depth, br)
+        }
+        // All free regions should be erased here.
+        r => bug!("unexpected region: {r:?}"),
+    });
+    let bound_vars = tcx.mk_bound_variable_kinds_from_iter(
+        (0..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon)),
+    );
+    ty::Binder::bind_with_vars(ty, bound_vars)
+}
+
+#[instrument(level = "debug", skip(ecx), ret)]
+pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
+    ecx: &EvalCtxt<'_, 'tcx>,
+    ty: Ty<'tcx>,
+) -> Result<Vec<Ty<'tcx>>, NoSolution> {
+    match *ty.kind() {
+        ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+        | ty::Uint(_)
+        | ty::Int(_)
+        | ty::Bool
+        | ty::Float(_)
+        | ty::FnDef(..)
+        | ty::FnPtr(_)
+        | ty::RawPtr(..)
+        | ty::Char
+        | ty::Ref(..)
+        | ty::Coroutine(..)
+        | ty::CoroutineWitness(..)
+        | ty::Array(..)
+        | ty::Closure(..)
+        | ty::Never
+        | ty::Dynamic(_, _, ty::DynStar)
+        | ty::Error(_) => Ok(vec![]),
+
+        ty::Str
+        | ty::Slice(_)
+        | ty::Dynamic(..)
+        | ty::Foreign(..)
+        | ty::Alias(..)
+        | ty::Param(_)
+        | ty::Placeholder(..) => Err(NoSolution),
+
+        ty::Bound(..)
+        | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+            bug!("unexpected type `{ty}`")
+        }
+
+        ty::Tuple(tys) => Ok(tys.to_vec()),
+
+        ty::Adt(def, args) => {
+            let sized_crit = def.sized_constraint(ecx.tcx());
+            Ok(sized_crit.iter_instantiated(ecx.tcx(), args).collect())
+        }
+    }
+}
+
+#[instrument(level = "debug", skip(ecx), ret)]
+pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
+    ecx: &EvalCtxt<'_, 'tcx>,
+    ty: Ty<'tcx>,
+) -> Result<Vec<Ty<'tcx>>, NoSolution> {
+    match *ty.kind() {
+        ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]),
+
+        // Implementations are provided in core
+        ty::Uint(_)
+        | ty::Int(_)
+        | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+        | ty::Bool
+        | ty::Float(_)
+        | ty::Char
+        | ty::RawPtr(..)
+        | ty::Never
+        | ty::Ref(_, _, Mutability::Not)
+        | ty::Array(..) => Err(NoSolution),
+
+        ty::Dynamic(..)
+        | ty::Str
+        | ty::Slice(_)
+        | ty::Foreign(..)
+        | ty::Ref(_, _, Mutability::Mut)
+        | ty::Adt(_, _)
+        | ty::Alias(_, _)
+        | ty::Param(_)
+        | ty::Placeholder(..) => Err(NoSolution),
+
+        ty::Bound(..)
+        | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+            bug!("unexpected type `{ty}`")
+        }
+
+        ty::Tuple(tys) => Ok(tys.to_vec()),
+
+        ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]),
+
+        ty::Coroutine(def_id, args) => match ecx.tcx().coroutine_movability(def_id) {
+            Movability::Static => Err(NoSolution),
+            Movability::Movable => {
+                if ecx.tcx().features().coroutine_clone {
+                    let coroutine = args.as_coroutine();
+                    Ok(vec![coroutine.tupled_upvars_ty(), coroutine.witness()])
+                } else {
+                    Err(NoSolution)
+                }
+            }
+        },
+
+        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(
+                    ecx.tcx(),
+                    bty.instantiate(ecx.tcx(), args),
+                ))
+            })
+            .collect()),
+    }
+}
+
+// Returns a binder of the tupled inputs types and output type from a builtin callable type.
+pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    self_ty: Ty<'tcx>,
+    goal_kind: ty::ClosureKind,
+) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> {
+    match *self_ty.kind() {
+        // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
+        ty::FnDef(def_id, args) => {
+            let sig = tcx.fn_sig(def_id);
+            if sig.skip_binder().is_fn_trait_compatible()
+                && tcx.codegen_fn_attrs(def_id).target_features.is_empty()
+            {
+                Ok(Some(
+                    sig.instantiate(tcx, args)
+                        .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())),
+                ))
+            } else {
+                Err(NoSolution)
+            }
+        }
+        // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
+        ty::FnPtr(sig) => {
+            if sig.is_fn_trait_compatible() {
+                Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output()))))
+            } else {
+                Err(NoSolution)
+            }
+        }
+        ty::Closure(_, args) => {
+            let closure_args = args.as_closure();
+            match closure_args.kind_ty().to_opt_closure_kind() {
+                // If the closure's kind doesn't extend the goal kind,
+                // then the closure doesn't implement the trait.
+                Some(closure_kind) => {
+                    if !closure_kind.extends(goal_kind) {
+                        return Err(NoSolution);
+                    }
+                }
+                // Closure kind is not yet determined, so we return ambiguity unless
+                // the expected kind is `FnOnce` as that is always implemented.
+                None => {
+                    if goal_kind != ty::ClosureKind::FnOnce {
+                        return Ok(None);
+                    }
+                }
+            }
+            Ok(Some(closure_args.sig().map_bound(|sig| (sig.inputs()[0], sig.output()))))
+        }
+        ty::Bool
+        | ty::Char
+        | ty::Int(_)
+        | ty::Uint(_)
+        | ty::Float(_)
+        | ty::Adt(_, _)
+        | ty::Foreign(_)
+        | ty::Str
+        | ty::Array(_, _)
+        | ty::Slice(_)
+        | ty::RawPtr(_)
+        | ty::Ref(_, _, _)
+        | ty::Dynamic(_, _, _)
+        | ty::Coroutine(_, _)
+        | ty::CoroutineWitness(..)
+        | ty::Never
+        | ty::Tuple(_)
+        | ty::Alias(_, _)
+        | ty::Param(_)
+        | ty::Placeholder(..)
+        | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+        | ty::Error(_) => Err(NoSolution),
+
+        ty::Bound(..)
+        | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+            bug!("unexpected type `{self_ty}`")
+        }
+    }
+}
+
+/// Assemble a list of predicates that would be present on a theoretical
+/// user impl for an object type. These predicates must be checked any time
+/// we assemble a built-in object candidate for an object type, since they
+/// are not implied by the well-formedness of the type.
+///
+/// For example, given the following traits:
+///
+/// ```rust,ignore (theoretical code)
+/// trait Foo: Baz {
+///     type Bar: Copy;
+/// }
+///
+/// trait Baz {}
+/// ```
+///
+/// For the dyn type `dyn Foo<Item = Ty>`, we can imagine there being a
+/// pair of theoretical impls:
+///
+/// ```rust,ignore (theoretical code)
+/// impl Foo for dyn Foo<Item = Ty>
+/// where
+///     Self: Baz,
+///     <Self as Foo>::Bar: Copy,
+/// {
+///     type Bar = Ty;
+/// }
+///
+/// impl Baz for dyn Foo<Item = Ty> {}
+/// ```
+///
+/// However, in order to make such impls well-formed, we need to do an
+/// additional step of eagerly folding the associated types in the where
+/// clauses of the impl. In this example, that means replacing
+/// `<Self as Foo>::Bar` with `Ty` in the first impl.
+///
+// FIXME: This is only necessary as `<Self as Trait>::Assoc: ItemBound`
+// bounds in impls are trivially proven using the item bound candidates.
+// This is unsound in general and once that is fixed, we don't need to
+// normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9
+// for more details.
+pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
+    ecx: &EvalCtxt<'_, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
+    object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+) -> Vec<Goal<'tcx, ty::Predicate<'tcx>>> {
+    let tcx = ecx.tcx();
+    let mut requirements = vec![];
+    requirements.extend(
+        tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.args).predicates,
+    );
+    for item in tcx.associated_items(trait_ref.def_id).in_definition_order() {
+        // FIXME(associated_const_equality): Also add associated consts to
+        // the requirements here.
+        if item.kind == ty::AssocKind::Type {
+            // associated types that require `Self: Sized` do not show up in the built-in
+            // implementation of `Trait for dyn Trait`, and can be dropped here.
+            if tcx.generics_require_sized_self(item.def_id) {
+                continue;
+            }
+
+            requirements
+                .extend(tcx.item_bounds(item.def_id).iter_instantiated(tcx, trait_ref.args));
+        }
+    }
+
+    let mut replace_projection_with = FxHashMap::default();
+    for bound in object_bound {
+        if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() {
+            let proj = proj.with_self_ty(tcx, trait_ref.self_ty());
+            let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj));
+            assert_eq!(
+                old_ty,
+                None,
+                "{} has two substitutions: {} and {}",
+                proj.projection_ty,
+                proj.term,
+                old_ty.unwrap()
+            );
+        }
+    }
+
+    let mut folder =
+        ReplaceProjectionWith { ecx, param_env, mapping: replace_projection_with, nested: vec![] };
+    let folded_requirements = requirements.fold_with(&mut folder);
+
+    folder
+        .nested
+        .into_iter()
+        .chain(folded_requirements.into_iter().map(|clause| Goal::new(tcx, param_env, clause)))
+        .collect()
+}
+
+struct ReplaceProjectionWith<'a, 'tcx> {
+    ecx: &'a EvalCtxt<'a, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    mapping: FxHashMap<DefId, ty::PolyProjectionPredicate<'tcx>>,
+    nested: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.ecx.tcx()
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if let ty::Alias(ty::Projection, alias_ty) = *ty.kind()
+            && let Some(replacement) = self.mapping.get(&alias_ty.def_id)
+        {
+            // We may have a case where our object type's projection bound is higher-ranked,
+            // but the where clauses we instantiated are not. We can solve this by instantiating
+            // the binder at the usage site.
+            let proj = self.ecx.instantiate_binder_with_infer(*replacement);
+            // FIXME: Technically this equate could be fallible...
+            self.nested.extend(
+                self.ecx
+                    .eq_and_get_goals(self.param_env, alias_ty, proj.projection_ty)
+                    .expect("expected to be able to unify goal projection with dyn's projection"),
+            );
+            proj.term.ty().unwrap()
+        } else {
+            ty.super_fold_with(self)
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
new file mode 100644
index 00000000000..ecdae2521b9
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -0,0 +1,381 @@
+//! Canonicalization is used to separate some goal from its context,
+//! throwing away unnecessary information in the process.
+//!
+//! This is necessary to cache goals containing inference variables
+//! and placeholders without restricting them to the current `InferCtxt`.
+//!
+//! Canonicalization is fairly involved, for more details see the relevant
+//! section of the [rustc-dev-guide][c].
+//!
+//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
+use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
+use crate::solve::{
+    inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response,
+};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_index::IndexVec;
+use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
+use rustc_infer::infer::canonical::CanonicalVarValues;
+use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
+use rustc_infer::infer::resolve::EagerResolver;
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
+use rustc_middle::infer::canonical::Canonical;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::{
+    ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
+};
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
+use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer};
+use rustc_span::DUMMY_SP;
+use std::iter;
+use std::ops::Deref;
+
+trait ResponseT<'tcx> {
+    fn var_values(&self) -> CanonicalVarValues<'tcx>;
+}
+
+impl<'tcx> ResponseT<'tcx> for Response<'tcx> {
+    fn var_values(&self) -> CanonicalVarValues<'tcx> {
+        self.var_values
+    }
+}
+
+impl<'tcx, T> ResponseT<'tcx> for inspect::State<'tcx, T> {
+    fn var_values(&self) -> CanonicalVarValues<'tcx> {
+        self.var_values
+    }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    /// Canonicalizes the goal remembering the original values
+    /// for each bound variable.
+    pub(super) fn canonicalize_goal<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &self,
+        goal: Goal<'tcx, T>,
+    ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
+        let opaque_types = self.infcx.clone_opaque_types_for_query_response();
+        let (goal, opaque_types) =
+            (goal, opaque_types).fold_with(&mut EagerResolver::new(self.infcx));
+
+        let mut orig_values = Default::default();
+        let canonical_goal = Canonicalizer::canonicalize(
+            self.infcx,
+            CanonicalizeMode::Input,
+            &mut orig_values,
+            QueryInput {
+                goal,
+                anchor: self.infcx.defining_use_anchor,
+                predefined_opaques_in_body: self
+                    .tcx()
+                    .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }),
+            },
+        );
+        (orig_values, canonical_goal)
+    }
+
+    /// To return the constraints of a canonical query to the caller, we canonicalize:
+    ///
+    /// - `var_values`: a map from bound variables in the canonical goal to
+    ///   the values inferred while solving the instantiated goal.
+    /// - `external_constraints`: additional constraints which aren't expressible
+    ///   using simple unification of inference variables.
+    #[instrument(level = "debug", skip(self))]
+    pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response(
+        &mut self,
+        certainty: Certainty,
+    ) -> QueryResult<'tcx> {
+        let goals_certainty = self.try_evaluate_added_goals()?;
+        assert_eq!(
+            self.tainted,
+            Ok(()),
+            "EvalCtxt is tainted -- nested goals may have been dropped in a \
+            previous call to `try_evaluate_added_goals!`"
+        );
+
+        let certainty = certainty.unify_with(goals_certainty);
+
+        let var_values = self.var_values;
+        let external_constraints = self.compute_external_query_constraints()?;
+
+        let (var_values, mut external_constraints) =
+            (var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx));
+        // Remove any trivial region constraints once we've resolved regions
+        external_constraints
+            .region_constraints
+            .outlives
+            .retain(|(outlives, _)| outlives.0.as_region().map_or(true, |re| re != outlives.1));
+
+        let canonical = Canonicalizer::canonicalize(
+            self.infcx,
+            CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
+            &mut Default::default(),
+            Response {
+                var_values,
+                certainty,
+                external_constraints: self.tcx().mk_external_constraints(external_constraints),
+            },
+        );
+
+        Ok(canonical)
+    }
+
+    /// Constructs a totally unconstrained, ambiguous response to a goal.
+    ///
+    /// Take care when using this, since often it's useful to respond with
+    /// ambiguity but return constrained variables to guide inference.
+    pub(in crate::solve) fn make_ambiguous_response_no_constraints(
+        &self,
+        maybe_cause: MaybeCause,
+    ) -> CanonicalResponse<'tcx> {
+        response_no_constraints_raw(
+            self.tcx(),
+            self.max_input_universe,
+            self.variables,
+            Certainty::Maybe(maybe_cause),
+        )
+    }
+
+    /// Computes the region constraints and *new* opaque types registered when
+    /// proving a goal.
+    ///
+    /// If an opaque was already constrained before proving this goal, then the
+    /// external constraints do not need to record that opaque, since if it is
+    /// further constrained by inference, that will be passed back in the var
+    /// values.
+    #[instrument(level = "debug", skip(self), ret)]
+    fn compute_external_query_constraints(
+        &self,
+    ) -> Result<ExternalConstraintsData<'tcx>, NoSolution> {
+        // We only check for leaks from universes which were entered inside
+        // of the query.
+        self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
+            debug!(?e, "failed the leak check");
+            NoSolution
+        })?;
+
+        // Cannot use `take_registered_region_obligations` as we may compute the response
+        // inside of a `probe` whenever we have multiple choices inside of the solver.
+        let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
+        let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| {
+            make_query_region_constraints(
+                self.tcx(),
+                region_obligations
+                    .iter()
+                    .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())),
+                region_constraints,
+            )
+        });
+
+        let mut seen = FxHashSet::default();
+        region_constraints.outlives.retain(|outlives| seen.insert(*outlives));
+
+        let mut opaque_types = self.infcx.clone_opaque_types_for_query_response();
+        // Only return opaque type keys for newly-defined opaques
+        opaque_types.retain(|(a, _)| {
+            self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
+        });
+
+        Ok(ExternalConstraintsData { region_constraints, opaque_types })
+    }
+
+    /// After calling a canonical query, we apply the constraints returned
+    /// by the query using this function.
+    ///
+    /// This happens in three steps:
+    /// - we instantiate the bound variables of the query response
+    /// - we unify the `var_values` of the response with the `original_values`
+    /// - we apply the `external_constraints` returned by the query
+    pub(super) fn instantiate_and_apply_query_response(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        original_values: Vec<ty::GenericArg<'tcx>>,
+        response: CanonicalResponse<'tcx>,
+    ) -> Result<(Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
+        let substitution =
+            Self::compute_query_response_substitution(self.infcx, &original_values, &response);
+
+        let Response { var_values, external_constraints, certainty } =
+            response.substitute(self.tcx(), &substitution);
+
+        let nested_goals =
+            Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values)?;
+
+        let ExternalConstraintsData { region_constraints, opaque_types } =
+            external_constraints.deref();
+        self.register_region_constraints(region_constraints);
+        self.register_opaque_types(param_env, opaque_types)?;
+
+        Ok((certainty, nested_goals))
+    }
+
+    /// This returns the substitutions to instantiate the bound variables of
+    /// the canonical response. This depends on the `original_values` for the
+    /// bound variables.
+    fn compute_query_response_substitution<T: ResponseT<'tcx>>(
+        infcx: &InferCtxt<'tcx>,
+        original_values: &[ty::GenericArg<'tcx>],
+        response: &Canonical<'tcx, T>,
+    ) -> CanonicalVarValues<'tcx> {
+        // FIXME: Longterm canonical queries should deal with all placeholders
+        // created inside of the query directly instead of returning them to the
+        // caller.
+        let prev_universe = infcx.universe();
+        let universes_created_in_query = response.max_universe.index();
+        for _ in 0..universes_created_in_query {
+            infcx.create_next_universe();
+        }
+
+        let var_values = response.value.var_values();
+        assert_eq!(original_values.len(), var_values.len());
+
+        // If the query did not make progress with constraining inference variables,
+        // we would normally create a new inference variables for bound existential variables
+        // only then unify this new inference variable with the inference variable from
+        // the input.
+        //
+        // We therefore instantiate the existential variable in the canonical response with the
+        // inference variable of the input right away, which is more performant.
+        let mut opt_values = IndexVec::from_elem_n(None, response.variables.len());
+        for (original_value, result_value) in iter::zip(original_values, var_values.var_values) {
+            match result_value.unpack() {
+                GenericArgKind::Type(t) => {
+                    if let &ty::Bound(debruijn, b) = t.kind() {
+                        assert_eq!(debruijn, ty::INNERMOST);
+                        opt_values[b.var] = Some(*original_value);
+                    }
+                }
+                GenericArgKind::Lifetime(r) => {
+                    if let ty::ReBound(debruijn, br) = *r {
+                        assert_eq!(debruijn, ty::INNERMOST);
+                        opt_values[br.var] = Some(*original_value);
+                    }
+                }
+                GenericArgKind::Const(c) => {
+                    if let ty::ConstKind::Bound(debruijn, b) = c.kind() {
+                        assert_eq!(debruijn, ty::INNERMOST);
+                        opt_values[b] = Some(*original_value);
+                    }
+                }
+            }
+        }
+
+        let var_values = infcx.tcx.mk_args_from_iter(response.variables.iter().enumerate().map(
+            |(index, info)| {
+                if info.universe() != ty::UniverseIndex::ROOT {
+                    // A variable from inside a binder of the query. While ideally these shouldn't
+                    // exist at all (see the FIXME at the start of this method), we have to deal with
+                    // them for now.
+                    infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| {
+                        ty::UniverseIndex::from(prev_universe.index() + idx.index())
+                    })
+                } else if info.is_existential() {
+                    // As an optimization we sometimes avoid creating a new inference variable here.
+                    //
+                    // All new inference variables we create start out in the current universe of the caller.
+                    // This is conceptually wrong as these inference variables would be able to name
+                    // more placeholders then they should be able to. However the inference variables have
+                    // to "come from somewhere", so by equating them with the original values of the caller
+                    // later on, we pull them down into their correct universe again.
+                    if let Some(v) = opt_values[BoundVar::from_usize(index)] {
+                        v
+                    } else {
+                        infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe)
+                    }
+                } else {
+                    // For placeholders which were already part of the input, we simply map this
+                    // universal bound variable back the placeholder of the input.
+                    original_values[info.expect_placeholder_index()]
+                }
+            },
+        ));
+
+        CanonicalVarValues { var_values }
+    }
+
+    #[instrument(level = "debug", skip(infcx, param_env), ret)]
+    fn unify_query_var_values(
+        infcx: &InferCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        original_values: &[ty::GenericArg<'tcx>],
+        var_values: CanonicalVarValues<'tcx>,
+    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
+        assert_eq!(original_values.len(), var_values.len());
+
+        let mut nested_goals = vec![];
+        for (&orig, response) in iter::zip(original_values, var_values.var_values) {
+            nested_goals.extend(
+                infcx
+                    .at(&ObligationCause::dummy(), param_env)
+                    .eq(DefineOpaqueTypes::No, orig, response)
+                    .map(|InferOk { value: (), obligations }| {
+                        obligations.into_iter().map(|o| Goal::from(o))
+                    })
+                    .map_err(|e| {
+                        debug!(?e, "failed to equate");
+                        NoSolution
+                    })?,
+            );
+        }
+
+        Ok(nested_goals)
+    }
+
+    fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstraints<'tcx>) {
+        for &(ty::OutlivesPredicate(lhs, rhs), _) in &region_constraints.outlives {
+            match lhs.unpack() {
+                GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs),
+                GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs),
+                GenericArgKind::Const(_) => bug!("const outlives: {lhs:?}: {rhs:?}"),
+            }
+        }
+
+        for member_constraint in &region_constraints.member_constraints {
+            // FIXME: Deal with member constraints :<
+            let _ = member_constraint;
+        }
+    }
+
+    fn register_opaque_types(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)],
+    ) -> Result<(), NoSolution> {
+        for &(key, ty) in opaque_types {
+            self.insert_hidden_type(key, param_env, ty)?;
+        }
+        Ok(())
+    }
+}
+
+impl<'tcx> inspect::ProofTreeBuilder<'tcx> {
+    pub fn make_canonical_state<T: TypeFoldable<TyCtxt<'tcx>>>(
+        ecx: &EvalCtxt<'_, 'tcx>,
+        data: T,
+    ) -> inspect::CanonicalState<'tcx, T> {
+        let state = inspect::State { var_values: ecx.var_values, data };
+        let state = state.fold_with(&mut EagerResolver::new(ecx.infcx));
+        Canonicalizer::canonicalize(
+            ecx.infcx,
+            CanonicalizeMode::Response { max_input_universe: ecx.max_input_universe },
+            &mut vec![],
+            state,
+        )
+    }
+
+    pub fn instantiate_canonical_state<T: TypeFoldable<TyCtxt<'tcx>>>(
+        infcx: &InferCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        original_values: &[ty::GenericArg<'tcx>],
+        state: inspect::CanonicalState<'tcx, T>,
+    ) -> Result<(Vec<Goal<'tcx, ty::Predicate<'tcx>>>, T), NoSolution> {
+        let substitution =
+            EvalCtxt::compute_query_response_substitution(infcx, original_values, &state);
+
+        let inspect::State { var_values, data } = state.substitute(infcx.tcx, &substitution);
+
+        let nested_goals =
+            EvalCtxt::unify_query_var_values(infcx, param_env, original_values, var_values)?;
+        Ok((nested_goals, data))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs
new file mode 100644
index 00000000000..67b6801059a
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs
@@ -0,0 +1,45 @@
+use super::{EvalCtxt, NestedGoals};
+use crate::solve::inspect;
+use rustc_middle::traits::query::NoSolution;
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+    pub(in crate::solve) fn commit_if_ok<T>(
+        &mut self,
+        f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result<T, NoSolution>,
+    ) -> Result<T, NoSolution> {
+        let mut nested_ecx = EvalCtxt {
+            infcx: self.infcx,
+            variables: self.variables,
+            var_values: self.var_values,
+            predefined_opaques_in_body: self.predefined_opaques_in_body,
+            max_input_universe: self.max_input_universe,
+            search_graph: self.search_graph,
+            nested_goals: NestedGoals::new(),
+            tainted: self.tainted,
+            inspect: self.inspect.new_probe(),
+        };
+
+        let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx));
+        if result.is_ok() {
+            let EvalCtxt {
+                infcx: _,
+                variables: _,
+                var_values: _,
+                predefined_opaques_in_body: _,
+                max_input_universe: _,
+                search_graph: _,
+                nested_goals,
+                tainted,
+                inspect,
+            } = nested_ecx;
+            self.nested_goals.extend(nested_goals);
+            self.tainted = tainted;
+            self.inspect.integrate_snapshot(inspect);
+        } else {
+            nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk);
+            self.inspect.finish_probe(nested_ecx.inspect);
+        }
+
+        result
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
new file mode 100644
index 00000000000..76c50a11102
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -0,0 +1,1003 @@
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_infer::infer::at::ToTrace;
+use rustc_infer::infer::canonical::CanonicalVarValues;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::{
+    BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt,
+};
+use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::ObligationCause;
+use rustc_middle::infer::canonical::CanonicalVarInfos;
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::traits::solve::inspect;
+use rustc_middle::traits::solve::{
+    CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques,
+    PredefinedOpaquesData, QueryResult,
+};
+use rustc_middle::traits::{specialization_graph, DefiningAnchor};
+use rustc_middle::ty::{
+    self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
+    TypeVisitableExt, TypeVisitor,
+};
+use rustc_session::config::DumpSolverProofTree;
+use rustc_span::DUMMY_SP;
+use std::io::Write;
+use std::iter;
+use std::ops::ControlFlow;
+
+use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
+
+use super::inspect::ProofTreeBuilder;
+use super::{search_graph, GoalEvaluationKind};
+use super::{search_graph::SearchGraph, Goal};
+use super::{GoalSource, SolverMode};
+pub use select::InferCtxtSelectExt;
+
+mod canonical;
+mod commit_if_ok;
+mod probe;
+mod select;
+
+pub struct EvalCtxt<'a, 'tcx> {
+    /// The inference context that backs (mostly) inference and placeholder terms
+    /// instantiated while solving goals.
+    ///
+    /// NOTE: The `InferCtxt` that backs the `EvalCtxt` is intentionally private,
+    /// because the `InferCtxt` is much more general than `EvalCtxt`. Methods such
+    /// as  `take_registered_region_obligations` can mess up query responses,
+    /// using `At::normalize` is totally wrong, calling `evaluate_root_goal` can
+    /// cause coinductive unsoundness, etc.
+    ///
+    /// Methods that are generally of use for trait solving are *intentionally*
+    /// re-declared through the `EvalCtxt` below, often with cleaner signatures
+    /// since we don't care about things like `ObligationCause`s and `Span`s here.
+    /// If some `InferCtxt` method is missing, please first think defensively about
+    /// the method's compatibility with this solver, or if an existing one does
+    /// the job already.
+    infcx: &'a InferCtxt<'tcx>,
+
+    /// The variable info for the `var_values`, only used to make an ambiguous response
+    /// with no constraints.
+    variables: CanonicalVarInfos<'tcx>,
+    pub(super) var_values: CanonicalVarValues<'tcx>,
+
+    predefined_opaques_in_body: PredefinedOpaques<'tcx>,
+
+    /// The highest universe index nameable by the caller.
+    ///
+    /// When we enter a new binder inside of the query we create new universes
+    /// which the caller cannot name. We have to be careful with variables from
+    /// these new universes when creating the query response.
+    ///
+    /// Both because these new universes can prevent us from reaching a fixpoint
+    /// if we have a coinductive cycle and because that's the only way we can return
+    /// new placeholders to the caller.
+    pub(super) max_input_universe: ty::UniverseIndex,
+
+    pub(super) search_graph: &'a mut SearchGraph<'tcx>,
+
+    pub(super) nested_goals: NestedGoals<'tcx>,
+
+    // Has this `EvalCtxt` errored out with `NoSolution` in `try_evaluate_added_goals`?
+    //
+    // If so, then it can no longer be used to make a canonical query response,
+    // since subsequent calls to `try_evaluate_added_goals` have possibly dropped
+    // ambiguous goals. Instead, a probe needs to be introduced somewhere in the
+    // evaluation code.
+    tainted: Result<(), NoSolution>,
+
+    pub(super) inspect: ProofTreeBuilder<'tcx>,
+}
+
+#[derive(Debug, Clone)]
+pub(super) struct NestedGoals<'tcx> {
+    /// This normalizes-to goal that is treated specially during the evaluation
+    /// loop. In each iteration we take the RHS of the projection, replace it with
+    /// a fresh inference variable, and only after evaluating that goal do we
+    /// equate the fresh inference variable with the actual RHS of the predicate.
+    ///
+    /// This is both to improve caching, and to avoid using the RHS of the
+    /// projection predicate to influence the normalizes-to candidate we select.
+    ///
+    /// This is not a 'real' nested goal. We must not forget to replace the RHS
+    /// with a fresh inference variable when we evaluate this goal. That can result
+    /// in a trait solver cycle. This would currently result in overflow but can be
+    /// can be unsound with more powerful coinduction in the future.
+    pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
+    /// The rest of the goals which have not yet processed or remain ambiguous.
+    pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
+}
+
+impl<'tcx> NestedGoals<'tcx> {
+    pub(super) fn new() -> Self {
+        Self { normalizes_to_hack_goal: None, goals: Vec::new() }
+    }
+
+    pub(super) fn is_empty(&self) -> bool {
+        self.normalizes_to_hack_goal.is_none() && self.goals.is_empty()
+    }
+
+    pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) {
+        assert_eq!(other.normalizes_to_hack_goal, None);
+        self.goals.extend(other.goals)
+    }
+}
+
+#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+pub enum GenerateProofTree {
+    Yes,
+    IfEnabled,
+    Never,
+}
+
+pub trait InferCtxtEvalExt<'tcx> {
+    /// Evaluates a goal from **outside** of the trait solver.
+    ///
+    /// Using this while inside of the solver is wrong as it uses a new
+    /// search graph which would break cycle detection.
+    fn evaluate_root_goal(
+        &self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        generate_proof_tree: GenerateProofTree,
+    ) -> (
+        Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
+        Option<inspect::GoalEvaluation<'tcx>>,
+    );
+}
+
+impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
+    #[instrument(level = "debug", skip(self))]
+    fn evaluate_root_goal(
+        &self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        generate_proof_tree: GenerateProofTree,
+    ) -> (
+        Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
+        Option<inspect::GoalEvaluation<'tcx>>,
+    ) {
+        EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
+            ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal)
+        })
+    }
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+    pub(super) fn solver_mode(&self) -> SolverMode {
+        self.search_graph.solver_mode()
+    }
+
+    pub(super) fn local_overflow_limit(&self) -> usize {
+        self.search_graph.local_overflow_limit()
+    }
+
+    /// Creates a root evaluation context and search graph. This should only be
+    /// used from outside of any evaluation, and other methods should be preferred
+    /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
+    fn enter_root<R>(
+        infcx: &InferCtxt<'tcx>,
+        generate_proof_tree: GenerateProofTree,
+        f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R,
+    ) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
+        let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
+        let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);
+
+        let mut ecx = EvalCtxt {
+            search_graph: &mut search_graph,
+            infcx,
+            nested_goals: NestedGoals::new(),
+            inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
+
+            // Only relevant when canonicalizing the response,
+            // which we don't do within this evaluation context.
+            predefined_opaques_in_body: infcx
+                .tcx
+                .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
+            max_input_universe: ty::UniverseIndex::ROOT,
+            variables: ty::List::empty(),
+            var_values: CanonicalVarValues::dummy(),
+            tainted: Ok(()),
+        };
+        let result = f(&mut ecx);
+
+        let tree = ecx.inspect.finalize();
+        if let (Some(tree), DumpSolverProofTree::Always) = (
+            &tree,
+            infcx.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default(),
+        ) {
+            let mut lock = std::io::stdout().lock();
+            let _ = lock.write_fmt(format_args!("{tree:?}\n"));
+            let _ = lock.flush();
+        }
+
+        assert!(
+            ecx.nested_goals.is_empty(),
+            "root `EvalCtxt` should not have any goals added to it"
+        );
+
+        assert!(search_graph.is_empty());
+        (result, tree)
+    }
+
+    /// Creates a nested evaluation context that shares the same search graph as the
+    /// one passed in. This is suitable for evaluation, granted that the search graph
+    /// has had the nested goal recorded on its stack ([`SearchGraph::with_new_goal`]),
+    /// but it's preferable to use other methods that call this one rather than this
+    /// method directly.
+    ///
+    /// This function takes care of setting up the inference context, setting the anchor,
+    /// and registering opaques from the canonicalized input.
+    fn enter_canonical<R>(
+        tcx: TyCtxt<'tcx>,
+        search_graph: &'a mut search_graph::SearchGraph<'tcx>,
+        canonical_input: CanonicalInput<'tcx>,
+        canonical_goal_evaluation: &mut ProofTreeBuilder<'tcx>,
+        f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
+    ) -> R {
+        let intercrate = match search_graph.solver_mode() {
+            SolverMode::Normal => false,
+            SolverMode::Coherence => true,
+        };
+        let (ref infcx, input, var_values) = tcx
+            .infer_ctxt()
+            .intercrate(intercrate)
+            .with_next_trait_solver(true)
+            .with_opaque_type_inference(canonical_input.value.anchor)
+            .build_with_canonical(DUMMY_SP, &canonical_input);
+
+        let mut ecx = EvalCtxt {
+            infcx,
+            variables: canonical_input.variables,
+            var_values,
+            predefined_opaques_in_body: input.predefined_opaques_in_body,
+            max_input_universe: canonical_input.max_universe,
+            search_graph,
+            nested_goals: NestedGoals::new(),
+            tainted: Ok(()),
+            inspect: canonical_goal_evaluation.new_goal_evaluation_step(input),
+        };
+
+        for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
+            ecx.insert_hidden_type(key, input.goal.param_env, ty)
+                .expect("failed to prepopulate opaque types");
+        }
+
+        if !ecx.nested_goals.is_empty() {
+            panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
+        }
+
+        let result = f(&mut ecx, input.goal);
+
+        canonical_goal_evaluation.goal_evaluation_step(ecx.inspect);
+
+        // When creating a query response we clone the opaque type constraints
+        // instead of taking them. This would cause an ICE here, since we have
+        // assertions against dropping an `InferCtxt` without taking opaques.
+        // FIXME: Once we remove support for the old impl we can remove this.
+        if input.anchor != DefiningAnchor::Error {
+            // This seems ok, but fragile.
+            let _ = infcx.take_opaque_types();
+        }
+
+        result
+    }
+
+    /// The entry point of the solver.
+    ///
+    /// This function deals with (coinductive) cycles, overflow, and caching
+    /// and then calls [`EvalCtxt::compute_goal`] which contains the actual
+    /// logic of the solver.
+    ///
+    /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
+    /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
+    /// outside of it.
+    #[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
+    fn evaluate_canonical_goal(
+        tcx: TyCtxt<'tcx>,
+        search_graph: &'a mut search_graph::SearchGraph<'tcx>,
+        canonical_input: CanonicalInput<'tcx>,
+        goal_evaluation: &mut ProofTreeBuilder<'tcx>,
+    ) -> QueryResult<'tcx> {
+        let mut canonical_goal_evaluation =
+            goal_evaluation.new_canonical_goal_evaluation(canonical_input);
+
+        // Deal with overflow, caching, and coinduction.
+        //
+        // The actual solver logic happens in `ecx.compute_goal`.
+        let result = ensure_sufficient_stack(|| {
+            search_graph.with_new_goal(
+                tcx,
+                canonical_input,
+                &mut canonical_goal_evaluation,
+                |search_graph, canonical_goal_evaluation| {
+                    EvalCtxt::enter_canonical(
+                        tcx,
+                        search_graph,
+                        canonical_input,
+                        canonical_goal_evaluation,
+                        |ecx, goal| {
+                            let result = ecx.compute_goal(goal);
+                            ecx.inspect.query_result(result);
+                            result
+                        },
+                    )
+                },
+            )
+        });
+
+        canonical_goal_evaluation.query_result(result);
+        goal_evaluation.canonical_goal_evaluation(canonical_goal_evaluation);
+        result
+    }
+
+    /// Recursively evaluates `goal`, returning whether any inference vars have
+    /// been constrained and the certainty of the result.
+    fn evaluate_goal(
+        &mut self,
+        goal_evaluation_kind: GoalEvaluationKind,
+        source: GoalSource,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
+        let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
+        let mut goal_evaluation =
+            self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
+        let canonical_response = EvalCtxt::evaluate_canonical_goal(
+            self.tcx(),
+            self.search_graph,
+            canonical_goal,
+            &mut goal_evaluation,
+        );
+        let canonical_response = match canonical_response {
+            Err(e) => {
+                self.inspect.goal_evaluation(goal_evaluation);
+                return Err(e);
+            }
+            Ok(response) => response,
+        };
+
+        let (certainty, has_changed, nested_goals) = match self
+            .instantiate_response_discarding_overflow(
+                goal.param_env,
+                source,
+                orig_values,
+                canonical_response,
+            ) {
+            Err(e) => {
+                self.inspect.goal_evaluation(goal_evaluation);
+                return Err(e);
+            }
+            Ok(response) => response,
+        };
+        goal_evaluation.returned_goals(&nested_goals);
+        self.inspect.goal_evaluation(goal_evaluation);
+
+        if !has_changed && !nested_goals.is_empty() {
+            bug!("an unchanged goal shouldn't have any side-effects on instantiation");
+        }
+
+        // FIXME: We previously had an assert here that checked that recomputing
+        // a goal after applying its constraints did not change its response.
+        //
+        // This assert was removed as it did not hold for goals constraining
+        // an inference variable to a recursive alias, e.g. in
+        // tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs.
+        //
+        // Once we have decided on how to handle trait-system-refactor-initiative#75,
+        // we should re-add an assert here.
+
+        Ok((has_changed, certainty, nested_goals))
+    }
+
+    fn instantiate_response_discarding_overflow(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        source: GoalSource,
+        original_values: Vec<ty::GenericArg<'tcx>>,
+        response: CanonicalResponse<'tcx>,
+    ) -> Result<(Certainty, bool, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
+        // The old solver did not evaluate nested goals when normalizing.
+        // It returned the selection constraints allowing a `Projection`
+        // obligation to not hold in coherence while avoiding the fatal error
+        // from overflow.
+        //
+        // We match this behavior here by considering all constraints
+        // from nested goals which are not from where-bounds. We will already
+        // need to track which nested goals are required by impl where-bounds
+        // for coinductive cycles, so we simply reuse that here.
+        //
+        // While we could consider overflow constraints in more cases, this should
+        // not be necessary for backcompat and results in better perf. It also
+        // avoids a potential inconsistency which would otherwise require some
+        // tracking for root goals as well. See #119071 for an example.
+        let keep_overflow_constraints = || {
+            self.search_graph.current_goal_is_normalizes_to()
+                && source != GoalSource::ImplWhereBound
+        };
+
+        if response.value.certainty == Certainty::OVERFLOW && !keep_overflow_constraints() {
+            Ok((Certainty::OVERFLOW, false, Vec::new()))
+        } else {
+            let has_changed = !response.value.var_values.is_identity_modulo_regions()
+                || !response.value.external_constraints.opaque_types.is_empty();
+
+            let (certainty, nested_goals) =
+                self.instantiate_and_apply_query_response(param_env, original_values, response)?;
+            Ok((certainty, has_changed, nested_goals))
+        }
+    }
+
+    fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
+        let Goal { param_env, predicate } = goal;
+        let kind = predicate.kind();
+        if let Some(kind) = kind.no_bound_vars() {
+            match kind {
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
+                    self.compute_trait_goal(Goal { param_env, predicate })
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
+                    self.compute_projection_goal(Goal { param_env, predicate })
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => {
+                    self.compute_type_outlives_goal(Goal { param_env, predicate })
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => {
+                    self.compute_region_outlives_goal(Goal { param_env, predicate })
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
+                    self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
+                }
+                ty::PredicateKind::Subtype(predicate) => {
+                    self.compute_subtype_goal(Goal { param_env, predicate })
+                }
+                ty::PredicateKind::Coerce(predicate) => {
+                    self.compute_coerce_goal(Goal { param_env, predicate })
+                }
+                ty::PredicateKind::ObjectSafe(trait_def_id) => {
+                    self.compute_object_safe_goal(trait_def_id)
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+                    self.compute_well_formed_goal(Goal { param_env, predicate: arg })
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
+                    self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct })
+                }
+                ty::PredicateKind::ConstEquate(_, _) => {
+                    bug!("ConstEquate should not be emitted when `-Znext-solver` is active")
+                }
+                ty::PredicateKind::NormalizesTo(predicate) => {
+                    self.compute_normalizes_to_goal(Goal { param_env, predicate })
+                }
+                ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self
+                    .compute_alias_relate_goal(Goal {
+                        param_env,
+                        predicate: (lhs, rhs, direction),
+                    }),
+                ty::PredicateKind::Ambiguous => {
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+                }
+            }
+        } 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)
+        }
+    }
+
+    // Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning
+    // the certainty of all the goals.
+    #[instrument(level = "debug", skip(self))]
+    pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> {
+        let inspect = self.inspect.new_evaluate_added_goals();
+        let inspect = core::mem::replace(&mut self.inspect, inspect);
+
+        let mut response = Ok(Certainty::OVERFLOW);
+        for _ in 0..self.local_overflow_limit() {
+            // FIXME: This match is a bit ugly, it might be nice to change the inspect
+            // stuff to use a closure instead. which should hopefully simplify this a bit.
+            match self.evaluate_added_goals_step() {
+                Ok(Some(cert)) => {
+                    response = Ok(cert);
+                    break;
+                }
+                Ok(None) => {}
+                Err(NoSolution) => {
+                    response = Err(NoSolution);
+                    break;
+                }
+            }
+        }
+
+        self.inspect.eval_added_goals_result(response);
+
+        if response.is_err() {
+            self.tainted = Err(NoSolution);
+        }
+
+        let goal_evaluations = std::mem::replace(&mut self.inspect, inspect);
+        self.inspect.added_goals_evaluation(goal_evaluations);
+
+        response
+    }
+
+    /// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning.
+    ///
+    /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
+    fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
+        let tcx = self.tcx();
+        let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
+
+        self.inspect.evaluate_added_goals_loop_start();
+
+        fn with_misc_source<'tcx>(
+            it: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
+        ) -> impl Iterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)> {
+            iter::zip(iter::repeat(GoalSource::Misc), it)
+        }
+
+        // If this loop did not result in any progress, what's our final certainty.
+        let mut unchanged_certainty = Some(Certainty::Yes);
+        if let Some(goal) = goals.normalizes_to_hack_goal.take() {
+            // Replace the goal with an unconstrained infer var, so the
+            // RHS does not affect projection candidate assembly.
+            let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
+            let unconstrained_goal = goal.with(
+                tcx,
+                ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs },
+            );
+
+            let (_, certainty, instantiate_goals) = self.evaluate_goal(
+                GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes },
+                GoalSource::Misc,
+                unconstrained_goal,
+            )?;
+            self.nested_goals.goals.extend(with_misc_source(instantiate_goals));
+
+            // Finally, equate the goal's RHS with the unconstrained var.
+            // We put the nested goals from this into goals instead of
+            // next_goals to avoid needing to process the loop one extra
+            // time if this goal returns something -- I don't think this
+            // matters in practice, though.
+            let eq_goals =
+                self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?;
+            goals.goals.extend(with_misc_source(eq_goals));
+
+            // We only look at the `projection_ty` part here rather than
+            // looking at the "has changed" return from evaluate_goal,
+            // because we expect the `unconstrained_rhs` part of the predicate
+            // to have changed -- that means we actually normalized successfully!
+            if goal.predicate.alias != self.resolve_vars_if_possible(goal.predicate.alias) {
+                unchanged_certainty = None;
+            }
+
+            match certainty {
+                Certainty::Yes => {}
+                Certainty::Maybe(_) => {
+                    // We need to resolve vars here so that we correctly
+                    // deal with `has_changed` in the next iteration.
+                    self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal));
+                    unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
+                }
+            }
+        }
+
+        for (source, goal) in goals.goals.drain(..) {
+            let (has_changed, certainty, instantiate_goals) = self.evaluate_goal(
+                GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
+                source,
+                goal,
+            )?;
+            self.nested_goals.goals.extend(with_misc_source(instantiate_goals));
+            if has_changed {
+                unchanged_certainty = None;
+            }
+
+            match certainty {
+                Certainty::Yes => {}
+                Certainty::Maybe(_) => {
+                    self.nested_goals.goals.push((source, goal));
+                    unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
+                }
+            }
+        }
+
+        Ok(unchanged_certainty)
+    }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    pub(super) fn next_ty_infer(&self) -> Ty<'tcx> {
+        self.infcx.next_ty_var(TypeVariableOrigin {
+            kind: TypeVariableOriginKind::MiscVariable,
+            span: DUMMY_SP,
+        })
+    }
+
+    pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> {
+        self.infcx.next_const_var(
+            ty,
+            ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span: DUMMY_SP },
+        )
+    }
+
+    /// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`.
+    /// If `kind` is an integer inference variable this will still return a ty infer var.
+    pub(super) fn next_term_infer_of_kind(&self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> {
+        match kind.unpack() {
+            ty::TermKind::Ty(_) => self.next_ty_infer().into(),
+            ty::TermKind::Const(ct) => self.next_const_infer(ct.ty()).into(),
+        }
+    }
+
+    /// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`.
+    ///
+    /// This is the case if the `term` is an inference variable in the innermost universe
+    /// and does not occur in any other part of the predicate.
+    #[instrument(level = "debug", skip(self), ret)]
+    pub(super) fn term_is_fully_unconstrained(
+        &self,
+        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+    ) -> bool {
+        let term_is_infer = match goal.predicate.term.unpack() {
+            ty::TermKind::Ty(ty) => {
+                if let &ty::Infer(ty::TyVar(vid)) = ty.kind() {
+                    match self.infcx.probe_ty_var(vid) {
+                        Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"),
+                        Err(universe) => universe == self.infcx.universe(),
+                    }
+                } else {
+                    false
+                }
+            }
+            ty::TermKind::Const(ct) => {
+                if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() {
+                    match self.infcx.probe_const_var(vid) {
+                        Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"),
+                        Err(universe) => universe == self.infcx.universe(),
+                    }
+                } else {
+                    false
+                }
+            }
+        };
+
+        // Guard against `<T as Trait<?0>>::Assoc = ?0>`.
+        struct ContainsTerm<'a, 'tcx> {
+            term: ty::Term<'tcx>,
+            infcx: &'a InferCtxt<'tcx>,
+        }
+        impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTerm<'_, 'tcx> {
+            type BreakTy = ();
+            fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+                if let Some(vid) = t.ty_vid()
+                    && let ty::TermKind::Ty(term) = self.term.unpack()
+                    && let Some(term_vid) = term.ty_vid()
+                    && self.infcx.root_var(vid) == self.infcx.root_var(term_vid)
+                {
+                    ControlFlow::Break(())
+                } else if t.has_non_region_infer() {
+                    t.super_visit_with(self)
+                } else {
+                    ControlFlow::Continue(())
+                }
+            }
+
+            fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+                if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = c.kind()
+                    && let ty::TermKind::Const(term) = self.term.unpack()
+                    && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind()
+                    && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid)
+                {
+                    ControlFlow::Break(())
+                } else if c.has_non_region_infer() {
+                    c.super_visit_with(self)
+                } else {
+                    ControlFlow::Continue(())
+                }
+            }
+        }
+
+        let mut visitor = ContainsTerm { infcx: self.infcx, term: goal.predicate.term };
+
+        term_is_infer
+            && goal.predicate.alias.visit_with(&mut visitor).is_continue()
+            && goal.param_env.visit_with(&mut visitor).is_continue()
+    }
+
+    #[instrument(level = "debug", skip(self, param_env), ret)]
+    pub(super) fn eq<T: ToTrace<'tcx>>(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        lhs: T,
+        rhs: T,
+    ) -> Result<(), NoSolution> {
+        self.infcx
+            .at(&ObligationCause::dummy(), param_env)
+            .eq(DefineOpaqueTypes::No, lhs, rhs)
+            .map(|InferOk { value: (), obligations }| {
+                self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
+            })
+            .map_err(|e| {
+                debug!(?e, "failed to equate");
+                NoSolution
+            })
+    }
+
+    #[instrument(level = "debug", skip(self, param_env), ret)]
+    pub(super) fn sub<T: ToTrace<'tcx>>(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        sub: T,
+        sup: T,
+    ) -> Result<(), NoSolution> {
+        self.infcx
+            .at(&ObligationCause::dummy(), param_env)
+            .sub(DefineOpaqueTypes::No, sub, sup)
+            .map(|InferOk { value: (), obligations }| {
+                self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
+            })
+            .map_err(|e| {
+                debug!(?e, "failed to subtype");
+                NoSolution
+            })
+    }
+
+    #[instrument(level = "debug", skip(self, param_env), ret)]
+    pub(super) fn relate<T: ToTrace<'tcx>>(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        lhs: T,
+        variance: ty::Variance,
+        rhs: T,
+    ) -> Result<(), NoSolution> {
+        self.infcx
+            .at(&ObligationCause::dummy(), param_env)
+            .relate(DefineOpaqueTypes::No, lhs, variance, rhs)
+            .map(|InferOk { value: (), obligations }| {
+                self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
+            })
+            .map_err(|e| {
+                debug!(?e, "failed to relate");
+                NoSolution
+            })
+    }
+
+    /// Equates two values returning the nested goals without adding them
+    /// to the nested goals of the `EvalCtxt`.
+    ///
+    /// If possible, try using `eq` instead which automatically handles nested
+    /// goals correctly.
+    #[instrument(level = "trace", skip(self, param_env), ret)]
+    pub(super) fn eq_and_get_goals<T: ToTrace<'tcx>>(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        lhs: T,
+        rhs: T,
+    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
+        self.infcx
+            .at(&ObligationCause::dummy(), param_env)
+            .eq(DefineOpaqueTypes::No, lhs, rhs)
+            .map(|InferOk { value: (), obligations }| {
+                obligations.into_iter().map(|o| o.into()).collect()
+            })
+            .map_err(|e| {
+                debug!(?e, "failed to equate");
+                NoSolution
+            })
+    }
+
+    pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<TyCtxt<'tcx>> + Copy>(
+        &self,
+        value: ty::Binder<'tcx, T>,
+    ) -> T {
+        self.infcx.instantiate_binder_with_fresh_vars(
+            DUMMY_SP,
+            BoundRegionConversionTime::HigherRankedType,
+            value,
+        )
+    }
+
+    pub(super) fn instantiate_binder_with_placeholders<T: TypeFoldable<TyCtxt<'tcx>> + Copy>(
+        &self,
+        value: ty::Binder<'tcx, T>,
+    ) -> T {
+        self.infcx.instantiate_binder_with_placeholders(value)
+    }
+
+    pub(super) fn resolve_vars_if_possible<T>(&self, value: T) -> T
+    where
+        T: TypeFoldable<TyCtxt<'tcx>>,
+    {
+        self.infcx.resolve_vars_if_possible(value)
+    }
+
+    pub(super) fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> {
+        self.infcx.fresh_args_for_item(DUMMY_SP, def_id)
+    }
+
+    pub(super) fn translate_args(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        source_impl: DefId,
+        source_args: ty::GenericArgsRef<'tcx>,
+        target_node: specialization_graph::Node,
+    ) -> ty::GenericArgsRef<'tcx> {
+        crate::traits::translate_args(self.infcx, param_env, source_impl, source_args, target_node)
+    }
+
+    pub(super) fn register_ty_outlives(&self, ty: Ty<'tcx>, lt: ty::Region<'tcx>) {
+        self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy());
+    }
+
+    pub(super) fn register_region_outlives(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) {
+        // `b : a` ==> `a <= b`
+        // (inlined from `InferCtxt::region_outlives_predicate`)
+        self.infcx.sub_regions(
+            rustc_infer::infer::SubregionOrigin::RelateRegionParamBound(DUMMY_SP),
+            b,
+            a,
+        );
+    }
+
+    /// Computes the list of goals required for `arg` to be well-formed
+    pub(super) fn well_formed_goals(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        arg: ty::GenericArg<'tcx>,
+    ) -> Option<impl Iterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>> {
+        crate::traits::wf::unnormalized_obligations(self.infcx, param_env, arg)
+            .map(|obligations| obligations.into_iter().map(|obligation| obligation.into()))
+    }
+
+    pub(super) fn is_transmutable(
+        &self,
+        src_and_dst: rustc_transmute::Types<'tcx>,
+        scope: Ty<'tcx>,
+        assume: rustc_transmute::Assume,
+    ) -> Result<Certainty, NoSolution> {
+        use rustc_transmute::Answer;
+        // FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
+        match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
+            ObligationCause::dummy(),
+            src_and_dst,
+            scope,
+            assume,
+        ) {
+            Answer::Yes => Ok(Certainty::Yes),
+            Answer::No(_) | Answer::If(_) => Err(NoSolution),
+        }
+    }
+
+    pub(super) fn can_define_opaque_ty(&self, def_id: LocalDefId) -> bool {
+        self.infcx.opaque_type_origin(def_id).is_some()
+    }
+
+    pub(super) fn insert_hidden_type(
+        &mut self,
+        opaque_type_key: OpaqueTypeKey<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        hidden_ty: Ty<'tcx>,
+    ) -> Result<(), NoSolution> {
+        let mut obligations = Vec::new();
+        self.infcx.insert_hidden_type(
+            opaque_type_key,
+            &ObligationCause::dummy(),
+            param_env,
+            hidden_ty,
+            true,
+            &mut obligations,
+        )?;
+        self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
+        Ok(())
+    }
+
+    pub(super) fn add_item_bounds_for_hidden_type(
+        &mut self,
+        opaque_def_id: DefId,
+        opaque_args: ty::GenericArgsRef<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        hidden_ty: Ty<'tcx>,
+    ) {
+        let mut obligations = Vec::new();
+        self.infcx.add_item_bounds_for_hidden_type(
+            opaque_def_id,
+            opaque_args,
+            ObligationCause::dummy(),
+            param_env,
+            hidden_ty,
+            &mut obligations,
+        );
+        self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
+    }
+
+    // Do something for each opaque/hidden pair defined with `def_id` in the
+    // current inference context.
+    pub(super) fn unify_existing_opaque_tys(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        key: ty::OpaqueTypeKey<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Vec<CanonicalResponse<'tcx>> {
+        // FIXME: Super inefficient to be cloning this...
+        let opaques = self.infcx.clone_opaque_types_for_query_response();
+
+        let mut values = vec![];
+        for (candidate_key, candidate_ty) in opaques {
+            if candidate_key.def_id != key.def_id {
+                continue;
+            }
+            values.extend(self.probe_misc_candidate("opaque type storage").enter(|ecx| {
+                for (a, b) in std::iter::zip(candidate_key.args, key.args) {
+                    ecx.eq(param_env, a, b)?;
+                }
+                ecx.eq(param_env, candidate_ty, ty)?;
+                ecx.add_item_bounds_for_hidden_type(
+                    candidate_key.def_id.to_def_id(),
+                    candidate_key.args,
+                    param_env,
+                    candidate_ty,
+                );
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            }));
+        }
+        values
+    }
+
+    // Try to evaluate a const, or return `None` if the const is too generic.
+    // This doesn't mean the const isn't evaluatable, though, and should be treated
+    // as an ambiguity rather than no-solution.
+    pub(super) fn try_const_eval_resolve(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        unevaluated: ty::UnevaluatedConst<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Option<ty::Const<'tcx>> {
+        use rustc_middle::mir::interpret::ErrorHandled;
+        match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) {
+            Ok(ct) => Some(ct),
+            Err(ErrorHandled::Reported(e, _)) => {
+                Some(ty::Const::new_error(self.tcx(), e.into(), ty))
+            }
+            Err(ErrorHandled::TooGeneric(_)) => None,
+        }
+    }
+
+    /// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor`
+    /// for every trait ref encountered (including the principal). Passes both the vtable
+    /// base and the (optional) vptr slot.
+    pub(super) fn walk_vtable(
+        &mut self,
+        principal: ty::PolyTraitRef<'tcx>,
+        mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option<usize>),
+    ) {
+        let tcx = self.tcx();
+        let mut offset = 0;
+        prepare_vtable_segments::<()>(tcx, principal, |segment| {
+            match segment {
+                VtblSegment::MetadataDSA => {
+                    offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
+                }
+                VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+                    let own_vtable_entries = count_own_vtable_entries(tcx, trait_ref);
+
+                    supertrait_visitor(
+                        self,
+                        trait_ref,
+                        offset,
+                        emit_vptr.then(|| offset + own_vtable_entries),
+                    );
+
+                    offset += own_vtable_entries;
+                    if emit_vptr {
+                        offset += 1;
+                    }
+                }
+            }
+            ControlFlow::Continue(())
+        });
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
new file mode 100644
index 00000000000..91fd48807a4
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -0,0 +1,108 @@
+use crate::solve::assembly::Candidate;
+
+use super::EvalCtxt;
+use rustc_middle::traits::{
+    query::NoSolution,
+    solve::{inspect, CandidateSource, QueryResult},
+};
+use std::marker::PhantomData;
+
+pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> {
+    ecx: &'me mut EvalCtxt<'a, 'tcx>,
+    probe_kind: F,
+    _result: PhantomData<T>,
+}
+
+impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T>
+where
+    F: FnOnce(&T) -> inspect::ProbeKind<'tcx>,
+{
+    pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T {
+        let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self;
+
+        let mut nested_ecx = EvalCtxt {
+            infcx: outer_ecx.infcx,
+            variables: outer_ecx.variables,
+            var_values: outer_ecx.var_values,
+            predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
+            max_input_universe: outer_ecx.max_input_universe,
+            search_graph: outer_ecx.search_graph,
+            nested_goals: outer_ecx.nested_goals.clone(),
+            tainted: outer_ecx.tainted,
+            inspect: outer_ecx.inspect.new_probe(),
+        };
+        let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx));
+        if !outer_ecx.inspect.is_noop() {
+            let probe_kind = probe_kind(&r);
+            nested_ecx.inspect.probe_kind(probe_kind);
+            outer_ecx.inspect.finish_probe(nested_ecx.inspect);
+        }
+        r
+    }
+}
+
+pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, 'tcx, F> {
+    cx: ProbeCtxt<'me, 'a, 'tcx, F, QueryResult<'tcx>>,
+    source: CandidateSource,
+}
+
+impl<'tcx, F> TraitProbeCtxt<'_, '_, 'tcx, F>
+where
+    F: FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>,
+{
+    pub(in crate::solve) fn enter(
+        self,
+        f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
+    ) -> Result<Candidate<'tcx>, NoSolution> {
+        self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result })
+    }
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+    /// `probe_kind` is only called when proof tree building is enabled so it can be
+    /// as expensive as necessary to output the desired information.
+    pub(in crate::solve) fn probe<F, T>(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, 'tcx, F, T>
+    where
+        F: FnOnce(&T) -> inspect::ProbeKind<'tcx>,
+    {
+        ProbeCtxt { ecx: self, probe_kind, _result: PhantomData }
+    }
+
+    pub(in crate::solve) fn probe_misc_candidate(
+        &mut self,
+        name: &'static str,
+    ) -> ProbeCtxt<
+        '_,
+        'a,
+        'tcx,
+        impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>,
+        QueryResult<'tcx>,
+    > {
+        ProbeCtxt {
+            ecx: self,
+            probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::MiscCandidate {
+                name,
+                result: *result,
+            },
+            _result: PhantomData,
+        }
+    }
+
+    pub(in crate::solve) fn probe_trait_candidate(
+        &mut self,
+        source: CandidateSource,
+    ) -> TraitProbeCtxt<'_, 'a, 'tcx, impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>>
+    {
+        TraitProbeCtxt {
+            cx: ProbeCtxt {
+                ecx: self,
+                probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate {
+                    source,
+                    result: *result,
+                },
+                _result: PhantomData,
+            },
+            source,
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
new file mode 100644
index 00000000000..315df06be41
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -0,0 +1,398 @@
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt};
+use rustc_infer::traits::{
+    Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine,
+};
+use rustc_middle::traits::solve::{CandidateSource, CanonicalInput, Certainty, Goal};
+use rustc_middle::traits::{
+    BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError,
+};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::DUMMY_SP;
+
+use crate::solve::assembly::Candidate;
+use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
+use crate::solve::inspect::ProofTreeBuilder;
+use crate::traits::StructurallyNormalizeExt;
+use crate::traits::TraitEngineExt;
+
+pub trait InferCtxtSelectExt<'tcx> {
+    fn select_in_new_trait_solver(
+        &self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> SelectionResult<'tcx, Selection<'tcx>>;
+}
+
+impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
+    fn select_in_new_trait_solver(
+        &self,
+        obligation: &PolyTraitObligation<'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);
+                        }
+                    }
+                }
+            }
+
+            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)
+            }
+
+            // 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)))
+            }
+
+            (Certainty::Maybe(_), _) => Ok(None),
+        }
+    }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    fn compute_canonical_trait_candidates(
+        &mut self,
+        canonical_input: CanonicalInput<'tcx>,
+    ) -> Vec<Candidate<'tcx>> {
+        // This doesn't record the canonical goal on the stack during the
+        // candidate assembly step, but that's fine. Selection is conceptually
+        // outside of the solver, and if there were any cycles, we'd encounter
+        // the cycle anyways one step later.
+        EvalCtxt::enter_canonical(
+            self.tcx(),
+            self.search_graph,
+            canonical_input,
+            // FIXME: This is wrong, idk if we even want to track stuff here.
+            &mut ProofTreeBuilder::new_noop(),
+            |ecx, goal| {
+                let trait_goal = Goal {
+                    param_env: goal.param_env,
+                    predicate: goal
+                        .predicate
+                        .to_opt_poly_trait_pred()
+                        .expect("we canonicalized a trait goal")
+                        .no_bound_vars()
+                        .expect("we instantiated all bound vars"),
+                };
+                ecx.assemble_and_evaluate_candidates(trait_goal)
+            },
+        )
+    }
+}
+
+fn candidate_should_be_dropped_in_favor_of<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    victim: &Candidate<'tcx>,
+    other: &Candidate<'tcx>,
+) -> bool {
+    match (victim.source, other.source) {
+        (CandidateSource::ParamEnv(victim_idx), CandidateSource::ParamEnv(other_idx)) => {
+            victim_idx >= other_idx
+        }
+        (_, CandidateSource::ParamEnv(_)) => true,
+
+        // FIXME: we could prefer earlier vtable bases perhaps...
+        (
+            CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
+            CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
+        ) => false,
+        (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true,
+
+        (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
+            tcx.specializes((other_def_id, victim_def_id))
+                && other.result.value.certainty == Certainty::Yes
+        }
+
+        _ => false,
+    }
+}
+
+fn rematch_impl<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
+    impl_def_id: DefId,
+    mut nested: Vec<PredicateObligation<'tcx>>,
+) -> SelectionResult<'tcx, Selection<'tcx>> {
+    let args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
+    let impl_trait_ref =
+        infcx.tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(infcx.tcx, args);
+
+    nested.extend(
+        infcx
+            .at(&ObligationCause::dummy(), goal.param_env)
+            .eq(DefineOpaqueTypes::No, goal.predicate.trait_ref, impl_trait_ref)
+            .map_err(|_| SelectionError::Unimplemented)?
+            .into_obligations(),
+    );
+
+    nested.extend(
+        infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, args).into_iter().map(
+            |(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred),
+        ),
+    );
+
+    Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested })))
+}
+
+/// The `Unsize` trait is particularly important to coercion, so we try rematch it.
+/// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait
+/// goal assembly in the solver, both for soundness and in order to avoid ICEs.
+fn rematch_unsize<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
+    mut nested: Vec<PredicateObligation<'tcx>>,
+    source: BuiltinImplSource,
+    certainty: Certainty,
+) -> SelectionResult<'tcx, Selection<'tcx>> {
+    let tcx = infcx.tcx;
+    let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
+    let b_ty = structurally_normalize(
+        goal.predicate.trait_ref.args.type_at(1),
+        infcx,
+        goal.param_env,
+        &mut nested,
+    );
+
+    match (a_ty.kind(), b_ty.kind()) {
+        // Don't try to coerce `?0` to `dyn Trait`
+        (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => Ok(None),
+        // Stall any ambiguous upcasting goals, since we can't rematch those
+        (ty::Dynamic(_, _, ty::Dyn), ty::Dynamic(_, _, ty::Dyn)) => match certainty {
+            Certainty::Yes => Ok(Some(ImplSource::Builtin(source, nested))),
+            _ => Ok(None),
+        },
+        // `T` -> `dyn Trait` upcasting
+        (_, &ty::Dynamic(data, region, ty::Dyn)) => {
+            // Check that the type implements all of the predicates of the def-id.
+            // (i.e. the principal, all of the associated types match, and any auto traits)
+            nested.extend(data.iter().map(|pred| {
+                Obligation::new(
+                    infcx.tcx,
+                    ObligationCause::dummy(),
+                    goal.param_env,
+                    pred.with_self_ty(tcx, a_ty),
+                )
+            }));
+            // The type must be Sized to be unsized.
+            let sized_def_id = tcx.require_lang_item(hir::LangItem::Sized, None);
+            nested.push(Obligation::new(
+                infcx.tcx,
+                ObligationCause::dummy(),
+                goal.param_env,
+                ty::TraitRef::new(tcx, sized_def_id, [a_ty]),
+            ));
+            // The type must outlive the lifetime of the `dyn` we're unsizing into.
+            nested.push(Obligation::new(
+                infcx.tcx,
+                ObligationCause::dummy(),
+                goal.param_env,
+                ty::OutlivesPredicate(a_ty, region),
+            ));
+
+            Ok(Some(ImplSource::Builtin(source, nested)))
+        }
+        // `[T; n]` -> `[T]` unsizing
+        (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
+            nested.extend(
+                infcx
+                    .at(&ObligationCause::dummy(), goal.param_env)
+                    .eq(DefineOpaqueTypes::No, a_elem_ty, b_elem_ty)
+                    .expect("expected rematch to succeed")
+                    .into_obligations(),
+            );
+
+            Ok(Some(ImplSource::Builtin(source, nested)))
+        }
+        // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
+        (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
+            if a_def.is_struct() && a_def.did() == b_def.did() =>
+        {
+            let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
+            // We must be unsizing some type parameters. This also implies
+            // that the struct has a tail field.
+            if unsizing_params.is_empty() {
+                bug!("expected rematch to succeed")
+            }
+
+            let tail_field = a_def
+                .non_enum_variant()
+                .fields
+                .raw
+                .last()
+                .expect("expected unsized ADT to have a tail field");
+            let tail_field_ty = tcx.type_of(tail_field.did);
+
+            let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
+            let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
+
+            // Substitute just the unsizing params from B into A. The type after
+            // this substitution must be equal to B. This is so we don't unsize
+            // unrelated type parameters.
+            let new_a_args = tcx.mk_args_from_iter(
+                a_args
+                    .iter()
+                    .enumerate()
+                    .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }),
+            );
+            let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args);
+
+            nested.extend(
+                infcx
+                    .at(&ObligationCause::dummy(), goal.param_env)
+                    .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty)
+                    .expect("expected rematch to succeed")
+                    .into_obligations(),
+            );
+
+            // Finally, we require that `TailA: Unsize<TailB>` for the tail field
+            // types.
+            nested.push(Obligation::new(
+                tcx,
+                ObligationCause::dummy(),
+                goal.param_env,
+                ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
+            ));
+
+            Ok(Some(ImplSource::Builtin(source, nested)))
+        }
+        // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
+        (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
+            if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
+        {
+            let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
+            let b_last_ty = b_tys.last().unwrap();
+
+            // Substitute just the tail field of B., and require that they're equal.
+            let unsized_a_ty =
+                Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
+            nested.extend(
+                infcx
+                    .at(&ObligationCause::dummy(), goal.param_env)
+                    .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty)
+                    .expect("expected rematch to succeed")
+                    .into_obligations(),
+            );
+
+            // Similar to ADTs, require that we can unsize the tail.
+            nested.push(Obligation::new(
+                tcx,
+                ObligationCause::dummy(),
+                goal.param_env,
+                ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
+            ));
+
+            // We need to be able to detect tuple unsizing to require its feature gate.
+            assert_eq!(
+                source,
+                BuiltinImplSource::TupleUnsizing,
+                "compiler-errors wants to know if this can ever be triggered..."
+            );
+            Ok(Some(ImplSource::Builtin(source, nested)))
+        }
+        _ => {
+            assert_ne!(certainty, Certainty::Yes);
+            Ok(None)
+        }
+    }
+}
+
+fn structurally_normalize<'tcx>(
+    ty: Ty<'tcx>,
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    nested: &mut Vec<PredicateObligation<'tcx>>,
+) -> Ty<'tcx> {
+    if matches!(ty.kind(), ty::Alias(..)) {
+        let mut engine = <dyn TraitEngine<'tcx>>::new(infcx);
+        let normalized_ty = infcx
+            .at(&ObligationCause::dummy(), param_env)
+            .structurally_normalize(ty, &mut *engine)
+            .expect("normalization shouldn't fail if we got to here");
+        nested.extend(engine.pending_obligations());
+        normalized_ty
+    } else {
+        ty
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
new file mode 100644
index 00000000000..2139210b873
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -0,0 +1,192 @@
+use std::mem;
+
+use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::solve::MaybeCause;
+use rustc_infer::traits::Obligation;
+use rustc_infer::traits::{
+    query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
+    PredicateObligation, SelectionError, TraitEngine,
+};
+use rustc_middle::ty;
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+
+use super::eval_ctxt::GenerateProofTree;
+use super::{Certainty, InferCtxtEvalExt};
+
+/// A trait engine using the new trait solver.
+///
+/// This is mostly identical to how `evaluate_all` works inside of the
+/// solver, except that the requirements are slightly different.
+///
+/// Unlike `evaluate_all` it is possible to add new obligations later on
+/// and we also have to track diagnostics information by using `Obligation`
+/// instead of `Goal`.
+///
+/// It is also likely that we want to use slightly different datastructures
+/// here as this will have to deal with far more root goals than `evaluate_all`.
+pub struct FulfillmentCtxt<'tcx> {
+    obligations: Vec<PredicateObligation<'tcx>>,
+
+    /// The snapshot in which this context was created. Using the context
+    /// outside of this snapshot leads to subtle bugs if the snapshot
+    /// gets rolled back. Because of this we explicitly check that we only
+    /// use the context in exactly this snapshot.
+    usable_in_snapshot: usize,
+}
+
+impl<'tcx> FulfillmentCtxt<'tcx> {
+    pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
+        FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() }
+    }
+}
+
+impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
+    #[instrument(level = "debug", skip(self, infcx))]
+    fn register_predicate_obligation(
+        &mut self,
+        infcx: &InferCtxt<'tcx>,
+        obligation: PredicateObligation<'tcx>,
+    ) {
+        assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
+        self.obligations.push(obligation);
+    }
+
+    fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
+        self.obligations
+            .drain(..)
+            .map(|obligation| {
+                let code = infcx.probe(|_| {
+                    match infcx
+                        .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::IfEnabled)
+                        .0
+                    {
+                        Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => {
+                            FulfillmentErrorCode::CodeAmbiguity { overflow: false }
+                        }
+                        Ok((_, Certainty::Maybe(MaybeCause::Overflow), _)) => {
+                            FulfillmentErrorCode::CodeAmbiguity { overflow: true }
+                        }
+                        Ok((_, Certainty::Yes, _)) => {
+                            bug!("did not expect successful goal when collecting ambiguity errors")
+                        }
+                        Err(_) => {
+                            bug!("did not expect selection error when collecting ambiguity errors")
+                        }
+                    }
+                });
+
+                FulfillmentError {
+                    obligation: obligation.clone(),
+                    code,
+                    root_obligation: obligation,
+                }
+            })
+            .collect()
+    }
+
+    fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
+        assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
+        let mut errors = Vec::new();
+        for i in 0.. {
+            if !infcx.tcx.recursion_limit().value_within_limit(i) {
+                unimplemented!("overflowed on pending obligations: {:?}", self.obligations);
+            }
+
+            let mut has_changed = false;
+            for obligation in mem::take(&mut self.obligations) {
+                let goal = obligation.clone().into();
+                let (changed, certainty, nested_goals) =
+                    match infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0 {
+                        Ok(result) => result,
+                        Err(NoSolution) => {
+                            errors.push(FulfillmentError {
+                                obligation: obligation.clone(),
+                                code: match goal.predicate.kind().skip_binder() {
+                                    ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
+                                        FulfillmentErrorCode::CodeProjectionError(
+                                            // FIXME: This could be a `Sorts` if the term is a type
+                                            MismatchedProjectionTypes { err: TypeError::Mismatch },
+                                        )
+                                    }
+                                    ty::PredicateKind::NormalizesTo(..) => {
+                                        FulfillmentErrorCode::CodeProjectionError(
+                                            MismatchedProjectionTypes { err: TypeError::Mismatch },
+                                        )
+                                    }
+                                    ty::PredicateKind::AliasRelate(_, _, _) => {
+                                        FulfillmentErrorCode::CodeProjectionError(
+                                            MismatchedProjectionTypes { err: TypeError::Mismatch },
+                                        )
+                                    }
+                                    ty::PredicateKind::Subtype(pred) => {
+                                        let (a, b) = infcx.instantiate_binder_with_placeholders(
+                                            goal.predicate.kind().rebind((pred.a, pred.b)),
+                                        );
+                                        let expected_found = ExpectedFound::new(true, a, b);
+                                        FulfillmentErrorCode::CodeSubtypeError(
+                                            expected_found,
+                                            TypeError::Sorts(expected_found),
+                                        )
+                                    }
+                                    ty::PredicateKind::Coerce(pred) => {
+                                        let (a, b) = infcx.instantiate_binder_with_placeholders(
+                                            goal.predicate.kind().rebind((pred.a, pred.b)),
+                                        );
+                                        let expected_found = ExpectedFound::new(false, a, b);
+                                        FulfillmentErrorCode::CodeSubtypeError(
+                                            expected_found,
+                                            TypeError::Sorts(expected_found),
+                                        )
+                                    }
+                                    ty::PredicateKind::Clause(_)
+                                    | ty::PredicateKind::ObjectSafe(_)
+                                    | ty::PredicateKind::Ambiguous => {
+                                        FulfillmentErrorCode::CodeSelectionError(
+                                            SelectionError::Unimplemented,
+                                        )
+                                    }
+                                    ty::PredicateKind::ConstEquate(..) => {
+                                        bug!("unexpected goal: {goal:?}")
+                                    }
+                                },
+                                root_obligation: obligation,
+                            });
+                            continue;
+                        }
+                    };
+                // Push any nested goals that we get from unifying our canonical response
+                // with our obligation onto the fulfillment context.
+                self.obligations.extend(nested_goals.into_iter().map(|goal| {
+                    Obligation::new(
+                        infcx.tcx,
+                        obligation.cause.clone(),
+                        goal.param_env,
+                        goal.predicate,
+                    )
+                }));
+                has_changed |= changed;
+                match certainty {
+                    Certainty::Yes => {}
+                    Certainty::Maybe(_) => self.obligations.push(obligation),
+                }
+            }
+
+            if !has_changed {
+                break;
+            }
+        }
+
+        errors
+    }
+
+    fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
+        self.obligations.clone()
+    }
+
+    fn drain_unstalled_obligations(
+        &mut self,
+        _: &InferCtxt<'tcx>,
+    ) -> Vec<PredicateObligation<'tcx>> {
+        std::mem::take(&mut self.obligations)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
new file mode 100644
index 00000000000..6db53d6ddc4
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -0,0 +1,236 @@
+/// An infrastructure to mechanically analyse proof trees.
+///
+/// It is unavoidable that this representation is somewhat
+/// lossy as it should hide quite a few semantically relevant things,
+/// e.g. canonicalization and the order of nested goals.
+///
+/// @lcnr: However, a lot of the weirdness here is not strictly necessary
+/// and could be improved in the future. This is mostly good enough for
+/// coherence right now and was annoying to implement, so I am leaving it
+/// as is until we start using it for something else.
+use std::ops::ControlFlow;
+
+use rustc_infer::infer::InferCtxt;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::{inspect, QueryResult};
+use rustc_middle::traits::solve::{Certainty, Goal};
+use rustc_middle::ty;
+
+use crate::solve::inspect::ProofTreeBuilder;
+use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
+
+pub struct InspectGoal<'a, 'tcx> {
+    infcx: &'a InferCtxt<'tcx>,
+    depth: usize,
+    orig_values: &'a [ty::GenericArg<'tcx>],
+    goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    evaluation: &'a inspect::GoalEvaluation<'tcx>,
+}
+
+pub struct InspectCandidate<'a, 'tcx> {
+    goal: &'a InspectGoal<'a, 'tcx>,
+    kind: inspect::ProbeKind<'tcx>,
+    nested_goals: Vec<inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>>,
+    result: QueryResult<'tcx>,
+}
+
+impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
+    pub fn infcx(&self) -> &'a InferCtxt<'tcx> {
+        self.goal.infcx
+    }
+
+    pub fn kind(&self) -> inspect::ProbeKind<'tcx> {
+        self.kind
+    }
+
+    pub fn result(&self) -> Result<Certainty, NoSolution> {
+        self.result.map(|c| c.value.certainty)
+    }
+
+    /// Visit the nested goals of this candidate.
+    ///
+    /// FIXME(@lcnr): we have to slightly adapt this API
+    /// to also use it to compute the most relevant goal
+    /// for fulfillment errors. Will do that once we actually
+    /// need it.
+    pub fn visit_nested<V: ProofTreeVisitor<'tcx>>(
+        &self,
+        visitor: &mut V,
+    ) -> ControlFlow<V::BreakTy> {
+        // HACK: An arbitrary cutoff to avoid dealing with overflow and cycles.
+        if self.goal.depth <= 10 {
+            let infcx = self.goal.infcx;
+            infcx.probe(|_| {
+                let mut instantiated_goals = vec![];
+                for goal in &self.nested_goals {
+                    let goal = match ProofTreeBuilder::instantiate_canonical_state(
+                        infcx,
+                        self.goal.goal.param_env,
+                        self.goal.orig_values,
+                        *goal,
+                    ) {
+                        Ok((_goals, goal)) => goal,
+                        Err(NoSolution) => {
+                            warn!(
+                                "unexpected failure when instantiating {:?}: {:?}",
+                                goal, self.nested_goals
+                            );
+                            return ControlFlow::Continue(());
+                        }
+                    };
+                    instantiated_goals.push(goal);
+                }
+
+                for &goal in &instantiated_goals {
+                    let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes);
+                    let proof_tree = proof_tree.unwrap();
+                    visitor.visit_goal(&InspectGoal::new(
+                        infcx,
+                        self.goal.depth + 1,
+                        &proof_tree,
+                    ))?;
+                }
+
+                ControlFlow::Continue(())
+            })?;
+        }
+        ControlFlow::Continue(())
+    }
+}
+
+impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
+    pub fn infcx(&self) -> &'a InferCtxt<'tcx> {
+        self.infcx
+    }
+
+    pub fn goal(&self) -> Goal<'tcx, ty::Predicate<'tcx>> {
+        self.goal
+    }
+
+    pub fn result(&self) -> Result<Certainty, NoSolution> {
+        self.evaluation.evaluation.result.map(|c| c.value.certainty)
+    }
+
+    fn candidates_recur(
+        &'a self,
+        candidates: &mut Vec<InspectCandidate<'a, 'tcx>>,
+        nested_goals: &mut Vec<inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>>,
+        probe: &inspect::Probe<'tcx>,
+    ) {
+        for step in &probe.steps {
+            match step {
+                &inspect::ProbeStep::AddGoal(_source, goal) => nested_goals.push(goal),
+                inspect::ProbeStep::NestedProbe(ref probe) => {
+                    // Nested probes have to prove goals added in their parent
+                    // but do not leak them, so we truncate the added goals
+                    // afterwards.
+                    let num_goals = nested_goals.len();
+                    self.candidates_recur(candidates, nested_goals, probe);
+                    nested_goals.truncate(num_goals);
+                }
+                inspect::ProbeStep::EvaluateGoals(_)
+                | inspect::ProbeStep::CommitIfOkStart
+                | inspect::ProbeStep::CommitIfOkSuccess => (),
+            }
+        }
+
+        match probe.kind {
+            inspect::ProbeKind::NormalizedSelfTyAssembly
+            | inspect::ProbeKind::UnsizeAssembly
+            | inspect::ProbeKind::UpcastProjectionCompatibility
+            | inspect::ProbeKind::CommitIfOk => (),
+            // We add a candidate for the root evaluation if there
+            // is only one way to prove a given goal, e.g. for `WellFormed`.
+            //
+            // FIXME: This is currently wrong if we don't even try any
+            // candidates, e.g. for a trait goal, as in this case `candidates` is
+            // actually supposed to be empty.
+            inspect::ProbeKind::Root { result } => {
+                if candidates.is_empty() {
+                    candidates.push(InspectCandidate {
+                        goal: self,
+                        kind: probe.kind,
+                        nested_goals: nested_goals.clone(),
+                        result,
+                    });
+                }
+            }
+            inspect::ProbeKind::MiscCandidate { name: _, result }
+            | inspect::ProbeKind::TraitCandidate { source: _, result } => {
+                candidates.push(InspectCandidate {
+                    goal: self,
+                    kind: probe.kind,
+                    nested_goals: nested_goals.clone(),
+                    result,
+                });
+            }
+        }
+    }
+
+    pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
+        let mut candidates = vec![];
+        let last_eval_step = match self.evaluation.evaluation.kind {
+            inspect::CanonicalGoalEvaluationKind::Overflow
+            | inspect::CanonicalGoalEvaluationKind::CycleInStack => {
+                warn!("unexpected root evaluation: {:?}", self.evaluation);
+                return vec![];
+            }
+            inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } => {
+                if let Some(last) = revisions.last() {
+                    last
+                } else {
+                    return vec![];
+                }
+            }
+        };
+
+        let mut nested_goals = vec![];
+        self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step.evaluation);
+
+        candidates
+    }
+
+    fn new(
+        infcx: &'a InferCtxt<'tcx>,
+        depth: usize,
+        root: &'a inspect::GoalEvaluation<'tcx>,
+    ) -> Self {
+        match root.kind {
+            inspect::GoalEvaluationKind::Root { ref orig_values } => InspectGoal {
+                infcx,
+                depth,
+                orig_values,
+                goal: infcx.resolve_vars_if_possible(root.uncanonicalized_goal),
+                evaluation: root,
+            },
+            inspect::GoalEvaluationKind::Nested { .. } => unreachable!(),
+        }
+    }
+}
+
+/// The public API to interact with proof trees.
+pub trait ProofTreeVisitor<'tcx> {
+    type BreakTy;
+
+    fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> ControlFlow<Self::BreakTy>;
+}
+
+pub trait ProofTreeInferCtxtExt<'tcx> {
+    fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
+        &self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        visitor: &mut V,
+    ) -> ControlFlow<V::BreakTy>;
+}
+
+impl<'tcx> ProofTreeInferCtxtExt<'tcx> for InferCtxt<'tcx> {
+    fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
+        &self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        visitor: &mut V,
+    ) -> ControlFlow<V::BreakTy> {
+        let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
+        let proof_tree = proof_tree.unwrap();
+        visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
new file mode 100644
index 00000000000..d8caef5b03f
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
@@ -0,0 +1,556 @@
+//! Building proof trees incrementally during trait solving.
+//!
+//! This code is *a bit* of a mess and can hopefully be
+//! mostly ignored. For a general overview of how it works,
+//! see the comment on [ProofTreeBuilder].
+use std::mem;
+
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::{
+    CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack, QueryInput, QueryResult,
+};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::config::DumpSolverProofTree;
+
+use crate::solve::{self, inspect, EvalCtxt, GenerateProofTree};
+
+/// The core data structure when building proof trees.
+///
+/// In case the current evaluation does not generate a proof
+/// tree, `state` is simply `None` and we avoid any work.
+///
+/// The possible states of the solver are represented via
+/// variants of [DebugSolver]. For any nested computation we call
+/// `ProofTreeBuilder::new_nested_computation_kind` which
+/// creates a new `ProofTreeBuilder` to temporarily replace the
+/// current one. Once that nested computation is done,
+/// `ProofTreeBuilder::nested_computation_kind` is called
+/// to add the finished nested evaluation to the parent.
+///
+/// We provide additional information to the current state
+/// by calling methods such as `ProofTreeBuilder::probe_kind`.
+///
+/// The actual structure closely mirrors the finished proof
+/// trees. At the end of trait solving `ProofTreeBuilder::finalize`
+/// is called to recursively convert the whole structure to a
+/// finished proof tree.
+pub(in crate::solve) struct ProofTreeBuilder<'tcx> {
+    state: Option<Box<DebugSolver<'tcx>>>,
+}
+
+/// The current state of the proof tree builder, at most places
+/// in the code, only one or two variants are actually possible.
+///
+/// We simply ICE in case that assumption is broken.
+#[derive(Debug)]
+enum DebugSolver<'tcx> {
+    Root,
+    GoalEvaluation(WipGoalEvaluation<'tcx>),
+    CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<'tcx>),
+    AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>),
+    GoalEvaluationStep(WipGoalEvaluationStep<'tcx>),
+    Probe(WipProbe<'tcx>),
+}
+
+impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
+    fn from(g: WipGoalEvaluation<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::GoalEvaluation(g)
+    }
+}
+
+impl<'tcx> From<WipCanonicalGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
+    fn from(g: WipCanonicalGoalEvaluation<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::CanonicalGoalEvaluation(g)
+    }
+}
+
+impl<'tcx> From<WipAddedGoalsEvaluation<'tcx>> for DebugSolver<'tcx> {
+    fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::AddedGoalsEvaluation(g)
+    }
+}
+
+impl<'tcx> From<WipGoalEvaluationStep<'tcx>> for DebugSolver<'tcx> {
+    fn from(g: WipGoalEvaluationStep<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::GoalEvaluationStep(g)
+    }
+}
+
+impl<'tcx> From<WipProbe<'tcx>> for DebugSolver<'tcx> {
+    fn from(p: WipProbe<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::Probe(p)
+    }
+}
+
+#[derive(Eq, PartialEq, Debug)]
+struct WipGoalEvaluation<'tcx> {
+    pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    pub kind: WipGoalEvaluationKind<'tcx>,
+    pub evaluation: Option<WipCanonicalGoalEvaluation<'tcx>>,
+    pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+}
+
+impl<'tcx> WipGoalEvaluation<'tcx> {
+    fn finalize(self) -> inspect::GoalEvaluation<'tcx> {
+        inspect::GoalEvaluation {
+            uncanonicalized_goal: self.uncanonicalized_goal,
+            kind: match self.kind {
+                WipGoalEvaluationKind::Root { orig_values } => {
+                    inspect::GoalEvaluationKind::Root { orig_values }
+                }
+                WipGoalEvaluationKind::Nested { is_normalizes_to_hack } => {
+                    inspect::GoalEvaluationKind::Nested { is_normalizes_to_hack }
+                }
+            },
+            evaluation: self.evaluation.unwrap().finalize(),
+            returned_goals: self.returned_goals,
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug)]
+pub(in crate::solve) enum WipGoalEvaluationKind<'tcx> {
+    Root { orig_values: Vec<ty::GenericArg<'tcx>> },
+    Nested { is_normalizes_to_hack: IsNormalizesToHack },
+}
+
+#[derive(Eq, PartialEq)]
+pub(in crate::solve) enum WipCanonicalGoalEvaluationKind<'tcx> {
+    Overflow,
+    CycleInStack,
+    Interned { revisions: &'tcx [inspect::GoalEvaluationStep<'tcx>] },
+}
+
+impl std::fmt::Debug for WipCanonicalGoalEvaluationKind<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Overflow => write!(f, "Overflow"),
+            Self::CycleInStack => write!(f, "CycleInStack"),
+            Self::Interned { revisions: _ } => f.debug_struct("Interned").finish_non_exhaustive(),
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug)]
+struct WipCanonicalGoalEvaluation<'tcx> {
+    goal: CanonicalInput<'tcx>,
+    kind: Option<WipCanonicalGoalEvaluationKind<'tcx>>,
+    /// Only used for uncached goals. After we finished evaluating
+    /// the goal, this is interned and moved into `kind`.
+    revisions: Vec<WipGoalEvaluationStep<'tcx>>,
+    result: Option<QueryResult<'tcx>>,
+}
+
+impl<'tcx> WipCanonicalGoalEvaluation<'tcx> {
+    fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> {
+        assert!(self.revisions.is_empty());
+        let kind = match self.kind.unwrap() {
+            WipCanonicalGoalEvaluationKind::Overflow => {
+                inspect::CanonicalGoalEvaluationKind::Overflow
+            }
+            WipCanonicalGoalEvaluationKind::CycleInStack => {
+                inspect::CanonicalGoalEvaluationKind::CycleInStack
+            }
+            WipCanonicalGoalEvaluationKind::Interned { revisions } => {
+                inspect::CanonicalGoalEvaluationKind::Evaluation { revisions }
+            }
+        };
+
+        inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug)]
+struct WipAddedGoalsEvaluation<'tcx> {
+    evaluations: Vec<Vec<WipGoalEvaluation<'tcx>>>,
+    result: Option<Result<Certainty, NoSolution>>,
+}
+
+impl<'tcx> WipAddedGoalsEvaluation<'tcx> {
+    fn finalize(self) -> inspect::AddedGoalsEvaluation<'tcx> {
+        inspect::AddedGoalsEvaluation {
+            evaluations: self
+                .evaluations
+                .into_iter()
+                .map(|evaluations| {
+                    evaluations.into_iter().map(WipGoalEvaluation::finalize).collect()
+                })
+                .collect(),
+            result: self.result.unwrap(),
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug)]
+struct WipGoalEvaluationStep<'tcx> {
+    instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+
+    evaluation: WipProbe<'tcx>,
+}
+
+impl<'tcx> WipGoalEvaluationStep<'tcx> {
+    fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> {
+        let evaluation = self.evaluation.finalize();
+        match evaluation.kind {
+            inspect::ProbeKind::Root { .. } => (),
+            _ => unreachable!("unexpected root evaluation: {evaluation:?}"),
+        }
+        inspect::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, evaluation }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug)]
+struct WipProbe<'tcx> {
+    pub steps: Vec<WipProbeStep<'tcx>>,
+    pub kind: Option<inspect::ProbeKind<'tcx>>,
+}
+
+impl<'tcx> WipProbe<'tcx> {
+    fn finalize(self) -> inspect::Probe<'tcx> {
+        inspect::Probe {
+            steps: self.steps.into_iter().map(WipProbeStep::finalize).collect(),
+            kind: self.kind.unwrap(),
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug)]
+enum WipProbeStep<'tcx> {
+    AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
+    EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
+    NestedProbe(WipProbe<'tcx>),
+    CommitIfOkStart,
+    CommitIfOkSuccess,
+}
+
+impl<'tcx> WipProbeStep<'tcx> {
+    fn finalize(self) -> inspect::ProbeStep<'tcx> {
+        match self {
+            WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal),
+            WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
+            WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
+            WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart,
+            WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess,
+        }
+    }
+}
+
+impl<'tcx> ProofTreeBuilder<'tcx> {
+    fn new(state: impl Into<DebugSolver<'tcx>>) -> ProofTreeBuilder<'tcx> {
+        ProofTreeBuilder { state: Some(Box::new(state.into())) }
+    }
+
+    fn nested<T: Into<DebugSolver<'tcx>>>(&self, state: impl FnOnce() -> T) -> Self {
+        ProofTreeBuilder { state: self.state.as_ref().map(|_| Box::new(state().into())) }
+    }
+
+    fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> {
+        self.state.as_deref_mut()
+    }
+
+    pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> {
+        match *self.state? {
+            DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
+                Some(wip_goal_evaluation.finalize())
+            }
+            root => unreachable!("unexpected proof tree builder root node: {:?}", root),
+        }
+    }
+
+    pub fn new_maybe_root(
+        tcx: TyCtxt<'tcx>,
+        generate_proof_tree: GenerateProofTree,
+    ) -> ProofTreeBuilder<'tcx> {
+        match generate_proof_tree {
+            GenerateProofTree::Never => ProofTreeBuilder::new_noop(),
+            GenerateProofTree::IfEnabled => {
+                let opts = &tcx.sess.opts.unstable_opts;
+                match opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() {
+                    DumpSolverProofTree::Always => ProofTreeBuilder::new_root(),
+                    // `OnError` is handled by reevaluating goals in error
+                    // reporting with `GenerateProofTree::Yes`.
+                    DumpSolverProofTree::OnError | DumpSolverProofTree::Never => {
+                        ProofTreeBuilder::new_noop()
+                    }
+                }
+            }
+            GenerateProofTree::Yes => ProofTreeBuilder::new_root(),
+        }
+    }
+
+    pub fn new_root() -> ProofTreeBuilder<'tcx> {
+        ProofTreeBuilder::new(DebugSolver::Root)
+    }
+
+    pub fn new_noop() -> ProofTreeBuilder<'tcx> {
+        ProofTreeBuilder { state: None }
+    }
+
+    pub fn is_noop(&self) -> bool {
+        self.state.is_none()
+    }
+
+    pub(in crate::solve) fn new_goal_evaluation(
+        &mut self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        orig_values: &[ty::GenericArg<'tcx>],
+        kind: solve::GoalEvaluationKind,
+    ) -> ProofTreeBuilder<'tcx> {
+        self.nested(|| WipGoalEvaluation {
+            uncanonicalized_goal: goal,
+            kind: match kind {
+                solve::GoalEvaluationKind::Root => {
+                    WipGoalEvaluationKind::Root { orig_values: orig_values.to_vec() }
+                }
+                solve::GoalEvaluationKind::Nested { is_normalizes_to_hack } => {
+                    WipGoalEvaluationKind::Nested { is_normalizes_to_hack }
+                }
+            },
+            evaluation: None,
+            returned_goals: vec![],
+        })
+    }
+
+    pub fn new_canonical_goal_evaluation(
+        &mut self,
+        goal: CanonicalInput<'tcx>,
+    ) -> ProofTreeBuilder<'tcx> {
+        self.nested(|| WipCanonicalGoalEvaluation {
+            goal,
+            kind: None,
+            revisions: vec![],
+            result: None,
+        })
+    }
+
+    pub fn finalize_evaluation(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+    ) -> Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]> {
+        self.as_mut().map(|this| match this {
+            DebugSolver::CanonicalGoalEvaluation(evaluation) => {
+                let revisions = mem::take(&mut evaluation.revisions)
+                    .into_iter()
+                    .map(WipGoalEvaluationStep::finalize);
+                let revisions = &*tcx.arena.alloc_from_iter(revisions);
+                let kind = WipCanonicalGoalEvaluationKind::Interned { revisions };
+                assert_eq!(evaluation.kind.replace(kind), None);
+                revisions
+            }
+            _ => unreachable!(),
+        })
+    }
+
+    pub fn canonical_goal_evaluation(&mut self, canonical_goal_evaluation: ProofTreeBuilder<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match (this, *canonical_goal_evaluation.state.unwrap()) {
+                (
+                    DebugSolver::GoalEvaluation(goal_evaluation),
+                    DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation),
+                ) => goal_evaluation.evaluation = Some(canonical_goal_evaluation),
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match this {
+                DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
+                    assert_eq!(canonical_goal_evaluation.kind.replace(kind), None);
+                }
+                _ => unreachable!(),
+            };
+        }
+    }
+
+    pub fn returned_goals(&mut self, goals: &[Goal<'tcx, ty::Predicate<'tcx>>]) {
+        if let Some(this) = self.as_mut() {
+            match this {
+                DebugSolver::GoalEvaluation(evaluation) => {
+                    assert!(evaluation.returned_goals.is_empty());
+                    evaluation.returned_goals.extend(goals);
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+    pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match (this, *goal_evaluation.state.unwrap()) {
+                (
+                    DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation {
+                        evaluations, ..
+                    }),
+                    DebugSolver::GoalEvaluation(goal_evaluation),
+                ) => evaluations.last_mut().unwrap().push(goal_evaluation),
+                (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation,
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    pub fn new_goal_evaluation_step(
+        &mut self,
+        instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+    ) -> ProofTreeBuilder<'tcx> {
+        self.nested(|| WipGoalEvaluationStep {
+            instantiated_goal,
+            evaluation: WipProbe { steps: vec![], kind: None },
+        })
+    }
+    pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match (this, *goal_evaluation_step.state.unwrap()) {
+                (
+                    DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations),
+                    DebugSolver::GoalEvaluationStep(goal_evaluation_step),
+                ) => {
+                    canonical_goal_evaluations.revisions.push(goal_evaluation_step);
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    pub fn new_probe(&mut self) -> ProofTreeBuilder<'tcx> {
+        self.nested(|| WipProbe { steps: vec![], kind: None })
+    }
+
+    pub fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match this {
+                DebugSolver::Probe(this) => {
+                    assert_eq!(this.kind.replace(probe_kind), None)
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    pub fn add_goal(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        source: GoalSource,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    ) {
+        // Can't use `if let Some(this) = ecx.inspect.as_mut()` here because
+        // we have to immutably use the `EvalCtxt` for `make_canonical_state`.
+        if ecx.inspect.is_noop() {
+            return;
+        }
+
+        let goal = Self::make_canonical_state(ecx, goal);
+
+        match ecx.inspect.as_mut().unwrap() {
+            DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+                evaluation: WipProbe { steps, .. },
+                ..
+            })
+            | DebugSolver::Probe(WipProbe { steps, .. }) => {
+                steps.push(WipProbeStep::AddGoal(source, goal))
+            }
+            s => unreachable!("tried to add {goal:?} to {s:?}"),
+        }
+    }
+
+    pub fn finish_probe(&mut self, probe: ProofTreeBuilder<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match (this, *probe.state.unwrap()) {
+                (
+                    DebugSolver::Probe(WipProbe { steps, .. })
+                    | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+                        evaluation: WipProbe { steps, .. },
+                        ..
+                    }),
+                    DebugSolver::Probe(probe),
+                ) => steps.push(WipProbeStep::NestedProbe(probe)),
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    /// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside
+    /// of the probe into the parent.
+    pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match (this, *probe.state.unwrap()) {
+                (
+                    DebugSolver::Probe(WipProbe { steps, .. })
+                    | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+                        evaluation: WipProbe { steps, .. },
+                        ..
+                    }),
+                    DebugSolver::Probe(probe),
+                ) => {
+                    steps.push(WipProbeStep::CommitIfOkStart);
+                    assert_eq!(probe.kind, None);
+                    steps.extend(probe.steps);
+                    steps.push(WipProbeStep::CommitIfOkSuccess);
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
+        self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None })
+    }
+
+    pub fn evaluate_added_goals_loop_start(&mut self) {
+        if let Some(this) = self.as_mut() {
+            match this {
+                DebugSolver::AddedGoalsEvaluation(this) => {
+                    this.evaluations.push(vec![]);
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    pub fn eval_added_goals_result(&mut self, result: Result<Certainty, NoSolution>) {
+        if let Some(this) = self.as_mut() {
+            match this {
+                DebugSolver::AddedGoalsEvaluation(this) => {
+                    assert_eq!(this.result.replace(result), None);
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    pub fn added_goals_evaluation(&mut self, added_goals_evaluation: ProofTreeBuilder<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match (this, *added_goals_evaluation.state.unwrap()) {
+                (
+                    DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+                        evaluation: WipProbe { steps, .. },
+                        ..
+                    })
+                    | DebugSolver::Probe(WipProbe { steps, .. }),
+                    DebugSolver::AddedGoalsEvaluation(added_goals_evaluation),
+                ) => steps.push(WipProbeStep::EvaluateGoals(added_goals_evaluation)),
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    pub fn query_result(&mut self, result: QueryResult<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match this {
+                DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
+                    assert_eq!(canonical_goal_evaluation.result.replace(result), None);
+                }
+                DebugSolver::GoalEvaluationStep(evaluation_step) => {
+                    assert_eq!(
+                        evaluation_step
+                            .evaluation
+                            .kind
+                            .replace(inspect::ProbeKind::Root { result }),
+                        None
+                    );
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs
new file mode 100644
index 00000000000..60d52305a6b
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs
@@ -0,0 +1,7 @@
+pub use rustc_middle::traits::solve::inspect::*;
+
+mod build;
+pub(in crate::solve) use build::*;
+
+mod analyse;
+pub use analyse::*;
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
new file mode 100644
index 00000000000..2f3111a2414
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -0,0 +1,370 @@
+//! The next-generation trait solver, currently still WIP.
+//!
+//! As a user of rust, you can use `-Znext-solver` to enable the new trait solver.
+//!
+//! As a developer of rustc, you shouldn't be using the new trait
+//! solver without asking the trait-system-refactor-initiative, but it can
+//! be enabled with `InferCtxtBuilder::with_next_trait_solver`. This will
+//! ensure that trait solving using that inference context will be routed
+//! to the new trait solver.
+//!
+//! For a high-level overview of how this solver works, check out the relevant
+//! section of the rustc-dev-guide.
+//!
+//! FIXME(@lcnr): Write that section. If you read this before then ask me
+//! about it on zulip.
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
+use rustc_infer::infer::DefineOpaqueTypes;
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::infer::canonical::CanonicalVarInfos;
+use rustc_middle::traits::solve::{
+    CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack,
+    QueryResult, Response,
+};
+use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex};
+use rustc_middle::ty::{
+    CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
+};
+
+mod alias_relate;
+mod assembly;
+mod eval_ctxt;
+mod fulfill;
+pub mod inspect;
+mod normalize;
+mod normalizes_to;
+mod project_goals;
+mod search_graph;
+mod trait_goals;
+
+pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt};
+pub use fulfill::FulfillmentCtxt;
+pub(crate) use normalize::deeply_normalize_for_diagnostics;
+pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
+
+#[derive(Debug, Clone, Copy)]
+enum SolverMode {
+    /// Ordinary trait solving, using everywhere except for coherence.
+    Normal,
+    /// Trait solving during coherence. There are a few notable differences
+    /// between coherence and ordinary trait solving.
+    ///
+    /// Most importantly, trait solving during coherence must not be incomplete,
+    /// i.e. return `Err(NoSolution)` for goals for which a solution exists.
+    /// This means that we must not make any guesses or arbitrary choices.
+    Coherence,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum GoalEvaluationKind {
+    Root,
+    Nested { is_normalizes_to_hack: IsNormalizesToHack },
+}
+
+trait CanonicalResponseExt {
+    fn has_no_inference_or_external_constraints(&self) -> bool;
+}
+
+impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
+    fn has_no_inference_or_external_constraints(&self) -> bool {
+        self.value.external_constraints.region_constraints.is_empty()
+            && self.value.var_values.is_identity()
+            && self.value.external_constraints.opaque_types.is_empty()
+    }
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+    #[instrument(level = "debug", skip(self))]
+    fn compute_type_outlives_goal(
+        &mut self,
+        goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let ty::OutlivesPredicate(ty, lt) = goal.predicate;
+        self.register_ty_outlives(ty, lt);
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn compute_region_outlives_goal(
+        &mut self,
+        goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let ty::OutlivesPredicate(a, b) = goal.predicate;
+        self.register_region_outlives(a, b);
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn compute_coerce_goal(
+        &mut self,
+        goal: Goal<'tcx, CoercePredicate<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        self.compute_subtype_goal(Goal {
+            param_env: goal.param_env,
+            predicate: SubtypePredicate {
+                a_is_expected: false,
+                a: goal.predicate.a,
+                b: goal.predicate.b,
+            },
+        })
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn compute_subtype_goal(
+        &mut self,
+        goal: Goal<'tcx, SubtypePredicate<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+        } else {
+            self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?;
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        }
+    }
+
+    #[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)
+        } else {
+            Err(NoSolution)
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn compute_well_formed_goal(
+        &mut self,
+        goal: Goal<'tcx, ty::GenericArg<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        match self.well_formed_goals(goal.param_env, goal.predicate) {
+            Some(goals) => {
+                self.add_goals(GoalSource::Misc, goals);
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            }
+            None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS),
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn compute_const_evaluatable_goal(
+        &mut self,
+        Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        match ct.kind() {
+            ty::ConstKind::Unevaluated(uv) => {
+                // We never return `NoSolution` here as `try_const_eval_resolve` emits an
+                // error itself when failing to evaluate, so emitting an additional fulfillment
+                // error in that case is unnecessary noise. This may change in the future once
+                // evaluation failures are allowed to impact selection, e.g. generic const
+                // expressions in impl headers or `where`-clauses.
+
+                // FIXME(generic_const_exprs): Implement handling for generic
+                // const expressions here.
+                if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv, ct.ty()) {
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                } else {
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+                }
+            }
+            ty::ConstKind::Infer(_) => {
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+            }
+            ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            }
+            // We can freely ICE here as:
+            // - `Param` gets replaced with a placeholder during canonicalization
+            // - `Bound` cannot exist as we don't have a binder around the self Type
+            // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet
+            ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => {
+                bug!("unexpect const kind: {:?}", ct)
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip(self), ret)]
+    fn compute_const_arg_has_type_goal(
+        &mut self,
+        goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
+    ) -> QueryResult<'tcx> {
+        let (ct, ty) = goal.predicate;
+        self.eq(goal.param_env, ct.ty(), ty)?;
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    #[instrument(level = "debug", skip(self))]
+    fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
+        assert!(
+            self.nested_goals.normalizes_to_hack_goal.is_none(),
+            "attempted to set the projection eq hack goal when one already exists"
+        );
+        self.nested_goals.normalizes_to_hack_goal = Some(goal);
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
+        inspect::ProofTreeBuilder::add_goal(self, source, goal);
+        self.nested_goals.goals.push((source, goal));
+    }
+
+    #[instrument(level = "debug", skip(self, goals))]
+    fn add_goals(
+        &mut self,
+        source: GoalSource,
+        goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
+    ) {
+        for goal in goals {
+            self.add_goal(source, goal);
+        }
+    }
+
+    /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`.
+    ///
+    /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
+    #[instrument(level = "debug", skip(self), ret)]
+    fn try_merge_responses(
+        &mut self,
+        responses: &[CanonicalResponse<'tcx>],
+    ) -> Option<CanonicalResponse<'tcx>> {
+        if responses.is_empty() {
+            return None;
+        }
+
+        // FIXME(-Znext-solver): We should instead try to find a `Certainty::Yes` response with
+        // a subset of the constraints that all the other responses have.
+        let one = responses[0];
+        if responses[1..].iter().all(|&resp| resp == one) {
+            return Some(one);
+        }
+
+        responses
+            .iter()
+            .find(|response| {
+                response.value.certainty == Certainty::Yes
+                    && response.has_no_inference_or_external_constraints()
+            })
+            .copied()
+    }
+
+    /// If we fail to merge responses we flounder and return overflow or ambiguity.
+    #[instrument(level = "debug", skip(self), ret)]
+    fn flounder(&mut self, responses: &[CanonicalResponse<'tcx>]) -> QueryResult<'tcx> {
+        if responses.is_empty() {
+            return Err(NoSolution);
+        }
+
+        let Certainty::Maybe(maybe_cause) =
+            responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
+                certainty.unify_with(response.value.certainty)
+            })
+        else {
+            bug!("expected flounder response to be ambiguous")
+        };
+
+        Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
+    }
+
+    /// Normalize a type when it is structually matched on.
+    ///
+    /// For self types this is generally already handled through
+    /// `assemble_candidates_after_normalizing_self_ty`, so anything happening
+    /// in [`EvalCtxt::assemble_candidates_via_self_ty`] does not have to normalize
+    /// the self type. It is required when structurally matching on any other
+    /// arguments of a trait goal, e.g. when assembling builtin unsize candidates.
+    #[instrument(level = "debug", skip(self), ret)]
+    fn try_normalize_ty(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Option<Ty<'tcx>> {
+        self.try_normalize_ty_recur(param_env, DefineOpaqueTypes::Yes, 0, ty)
+    }
+
+    fn try_normalize_ty_recur(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        define_opaque_types: DefineOpaqueTypes,
+        depth: usize,
+        ty: Ty<'tcx>,
+    ) -> Option<Ty<'tcx>> {
+        if !self.tcx().recursion_limit().value_within_limit(depth) {
+            return None;
+        }
+
+        let ty::Alias(kind, alias) = *ty.kind() else {
+            return Some(ty);
+        };
+
+        // We do no always define opaque types eagerly to allow non-defining uses in the defining scope.
+        if let (DefineOpaqueTypes::No, ty::AliasKind::Opaque) = (define_opaque_types, kind) {
+            if let Some(def_id) = alias.def_id.as_local() {
+                if self
+                    .unify_existing_opaque_tys(
+                        param_env,
+                        OpaqueTypeKey { def_id, args: alias.args },
+                        self.next_ty_infer(),
+                    )
+                    .is_empty()
+                {
+                    return Some(ty);
+                }
+            }
+        }
+
+        match self.commit_if_ok(|this| {
+            let normalized_ty = this.next_ty_infer();
+            let normalizes_to_goal = Goal::new(
+                this.tcx(),
+                param_env,
+                ty::NormalizesTo { alias, term: normalized_ty.into() },
+            );
+            this.add_goal(GoalSource::Misc, normalizes_to_goal);
+            this.try_evaluate_added_goals()?;
+            let ty = this.resolve_vars_if_possible(normalized_ty);
+            Ok(this.try_normalize_ty_recur(param_env, define_opaque_types, depth + 1, ty))
+        }) {
+            Ok(ty) => ty,
+            Err(NoSolution) => Some(ty),
+        }
+    }
+}
+
+fn response_no_constraints_raw<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    max_universe: UniverseIndex,
+    variables: CanonicalVarInfos<'tcx>,
+    certainty: Certainty,
+) -> CanonicalResponse<'tcx> {
+    Canonical {
+        max_universe,
+        variables,
+        value: Response {
+            var_values: CanonicalVarValues::make_identity(tcx, variables),
+            // FIXME: maybe we should store the "no response" version in tcx, like
+            // we do for tcx.types and stuff.
+            external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
+            certainty,
+        },
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
new file mode 100644
index 00000000000..55b79e6fc39
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -0,0 +1,272 @@
+use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_infer::infer::at::At;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::TraitEngineExt;
+use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine};
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::traits::{ObligationCause, Reveal};
+use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex};
+use rustc_middle::ty::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
+
+use super::FulfillmentCtxt;
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+pub fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
+    at: At<'_, 'tcx>,
+    value: T,
+) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+    assert!(!value.has_escaping_bound_vars());
+    deeply_normalize_with_skipped_universes(at, value, vec![])
+}
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+///
+/// Additionally takes a list of universes which represents the binders which have been
+/// entered before passing `value` to the function. This is currently needed for
+/// `normalize_erasing_regions`, which skips binders as it walks through a type.
+pub fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
+    at: At<'_, 'tcx>,
+    value: T,
+    universes: Vec<Option<UniverseIndex>>,
+) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+    let fulfill_cx = FulfillmentCtxt::new(at.infcx);
+    let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes };
+
+    value.try_fold_with(&mut folder)
+}
+
+struct NormalizationFolder<'me, 'tcx> {
+    at: At<'me, 'tcx>,
+    fulfill_cx: FulfillmentCtxt<'tcx>,
+    depth: usize,
+    universes: Vec<Option<UniverseIndex>>,
+}
+
+impl<'tcx> NormalizationFolder<'_, 'tcx> {
+    fn normalize_alias_ty(
+        &mut self,
+        alias: AliasTy<'tcx>,
+    ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
+        let infcx = self.at.infcx;
+        let tcx = infcx.tcx;
+        let recursion_limit = tcx.recursion_limit();
+        if !recursion_limit.value_within_limit(self.depth) {
+            self.at.infcx.err_ctxt().report_overflow_error(
+                &alias.to_ty(tcx),
+                self.at.cause.span,
+                true,
+                |_| {},
+            );
+        }
+
+        self.depth += 1;
+
+        let new_infer_ty = infcx.next_ty_var(TypeVariableOrigin {
+            kind: TypeVariableOriginKind::NormalizeProjectionType,
+            span: self.at.cause.span,
+        });
+        let obligation = Obligation::new(
+            tcx,
+            self.at.cause.clone(),
+            self.at.param_env,
+            ty::NormalizesTo { alias, term: new_infer_ty.into() },
+        );
+
+        // Do not emit an error if normalization is known to fail but instead
+        // keep the projection unnormalized. This is the case for projections
+        // with a `T: Trait` where-clause and opaque types outside of the defining
+        // scope.
+        let result = if infcx.predicate_may_hold(&obligation) {
+            self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+            let errors = self.fulfill_cx.select_all_or_error(infcx);
+            if !errors.is_empty() {
+                return Err(errors);
+            }
+            let ty = infcx.resolve_vars_if_possible(new_infer_ty);
+            ty.try_fold_with(self)?
+        } else {
+            alias.to_ty(tcx).try_super_fold_with(self)?
+        };
+
+        self.depth -= 1;
+        Ok(result)
+    }
+
+    fn normalize_unevaluated_const(
+        &mut self,
+        ty: Ty<'tcx>,
+        uv: ty::UnevaluatedConst<'tcx>,
+    ) -> Result<ty::Const<'tcx>, Vec<FulfillmentError<'tcx>>> {
+        let infcx = self.at.infcx;
+        let tcx = infcx.tcx;
+        let recursion_limit = tcx.recursion_limit();
+        if !recursion_limit.value_within_limit(self.depth) {
+            self.at.infcx.err_ctxt().report_overflow_error(
+                &ty::Const::new_unevaluated(tcx, uv, ty),
+                self.at.cause.span,
+                true,
+                |_| {},
+            );
+        }
+
+        self.depth += 1;
+
+        let new_infer_ct = infcx.next_const_var(
+            ty,
+            ConstVariableOrigin {
+                kind: ConstVariableOriginKind::MiscVariable,
+                span: self.at.cause.span,
+            },
+        );
+        let obligation = Obligation::new(
+            tcx,
+            self.at.cause.clone(),
+            self.at.param_env,
+            ty::NormalizesTo {
+                alias: AliasTy::new(tcx, uv.def, uv.args),
+                term: new_infer_ct.into(),
+            },
+        );
+
+        let result = if infcx.predicate_may_hold(&obligation) {
+            self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+            let errors = self.fulfill_cx.select_all_or_error(infcx);
+            if !errors.is_empty() {
+                return Err(errors);
+            }
+            let ct = infcx.resolve_vars_if_possible(new_infer_ct);
+            ct.try_fold_with(self)?
+        } else {
+            ty::Const::new_unevaluated(tcx, uv, ty).try_super_fold_with(self)?
+        };
+
+        self.depth -= 1;
+        Ok(result)
+    }
+}
+
+impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
+    type Error = Vec<FulfillmentError<'tcx>>;
+
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.at.infcx.tcx
+    }
+
+    fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> Result<ty::Binder<'tcx, T>, Self::Error> {
+        self.universes.push(None);
+        let t = t.try_super_fold_with(self)?;
+        self.universes.pop();
+        Ok(t)
+    }
+
+    fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
+        let reveal = self.at.param_env.reveal();
+        let infcx = self.at.infcx;
+        debug_assert_eq!(ty, infcx.shallow_resolve(ty));
+        if !needs_normalization(&ty, reveal) {
+            return Ok(ty);
+        }
+
+        // We don't normalize opaque types unless we have
+        // `Reveal::All`, even if we're in the defining scope.
+        let data = match *ty.kind() {
+            ty::Alias(kind, alias_ty) if kind != ty::Opaque || reveal == Reveal::All => alias_ty,
+            _ => return ty.try_super_fold_with(self),
+        };
+
+        if data.has_escaping_bound_vars() {
+            let (data, mapped_regions, mapped_types, mapped_consts) =
+                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+            let result = ensure_sufficient_stack(|| self.normalize_alias_ty(data))?;
+            Ok(PlaceholderReplacer::replace_placeholders(
+                infcx,
+                mapped_regions,
+                mapped_types,
+                mapped_consts,
+                &self.universes,
+                result,
+            ))
+        } else {
+            ensure_sufficient_stack(|| self.normalize_alias_ty(data))
+        }
+    }
+
+    fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
+        let reveal = self.at.param_env.reveal();
+        let infcx = self.at.infcx;
+        debug_assert_eq!(ct, infcx.shallow_resolve(ct));
+        if !needs_normalization(&ct, reveal) {
+            return Ok(ct);
+        }
+
+        let uv = match ct.kind() {
+            ty::ConstKind::Unevaluated(ct) => ct,
+            _ => return ct.try_super_fold_with(self),
+        };
+
+        if uv.has_escaping_bound_vars() {
+            let (uv, mapped_regions, mapped_types, mapped_consts) =
+                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv);
+            let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))?;
+            Ok(PlaceholderReplacer::replace_placeholders(
+                infcx,
+                mapped_regions,
+                mapped_types,
+                mapped_consts,
+                &self.universes,
+                result,
+            ))
+        } else {
+            ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))
+        }
+    }
+}
+
+// Deeply normalize a value and return it
+pub(crate) fn deeply_normalize_for_diagnostics<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    t: T,
+) -> T {
+    t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder {
+        at: infcx.at(&ObligationCause::dummy(), param_env),
+    })
+}
+
+struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> {
+    at: At<'a, 'tcx>,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.at.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        deeply_normalize_with_skipped_universes(
+            self.at,
+            ty,
+            vec![None; ty.outer_exclusive_binder().as_usize()],
+        )
+        .unwrap_or_else(|_| ty.super_fold_with(self))
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        deeply_normalize_with_skipped_universes(
+            self.at,
+            ct,
+            vec![None; ct.outer_exclusive_binder().as_usize()],
+        )
+        .unwrap_or_else(|_| ct.super_fold_with(self))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
new file mode 100644
index 00000000000..b2dff9b48ff
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
@@ -0,0 +1,56 @@
+//! Computes a normalizes-to (projection) goal for inherent associated types,
+//! `#![feature(inherent_associated_type)]`. Since astconv already determines
+//! which impl the IAT is being projected from, we just:
+//! 1. instantiate substs,
+//! 2. equate the self type, and
+//! 3. instantiate and register where clauses.
+use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult};
+use rustc_middle::ty;
+
+use crate::solve::EvalCtxt;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    pub(super) fn normalize_inherent_associated_type(
+        &mut self,
+        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let tcx = self.tcx();
+        let inherent = goal.predicate.alias;
+        let expected = goal.predicate.term.ty().expect("inherent consts are treated separately");
+
+        let impl_def_id = tcx.parent(inherent.def_id);
+        let impl_substs = self.fresh_args_for_item(impl_def_id);
+
+        // Equate impl header and add impl where clauses
+        self.eq(
+            goal.param_env,
+            inherent.self_ty(),
+            tcx.type_of(impl_def_id).instantiate(tcx, impl_substs),
+        )?;
+
+        // Equate IAT with the RHS of the project goal
+        let inherent_substs = inherent.rebase_inherent_args_onto_impl(impl_substs, tcx);
+        self.eq(
+            goal.param_env,
+            expected,
+            tcx.type_of(inherent.def_id).instantiate(tcx, inherent_substs),
+        )
+        .expect("expected goal term to be fully unconstrained");
+
+        // Check both where clauses on the impl and IAT
+        //
+        // FIXME(-Znext-solver=coinductive): I think this should be split
+        // and we tag the impl bounds with `GoalSource::ImplWhereBound`?
+        // Right not this includes both the impl and the assoc item where bounds,
+        // and I don't think the assoc item where-bounds are allowed to be coinductive.
+        self.add_goals(
+            GoalSource::Misc,
+            tcx.predicates_of(inherent.def_id)
+                .instantiate(tcx, inherent_substs)
+                .into_iter()
+                .map(|(pred, _)| goal.with(tcx, pred)),
+        );
+
+        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
new file mode 100644
index 00000000000..ccee6f8eb29
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -0,0 +1,719 @@
+use crate::traits::{check_args_compatible, specialization_graph};
+
+use super::assembly::{self, structural_traits, Candidate};
+use super::{EvalCtxt, GoalSource};
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_hir::LangItem;
+use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::specialization_graph::LeafDef;
+use rustc_infer::traits::Reveal;
+use rustc_middle::traits::solve::{
+    CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult,
+};
+use rustc_middle::traits::BuiltinImplSource;
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_middle::ty::NormalizesTo;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
+use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};
+
+mod inherent;
+mod opaques;
+mod weak_types;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    #[instrument(level = "debug", skip(self), ret)]
+    pub(super) fn compute_normalizes_to_goal(
+        &mut self,
+        goal: Goal<'tcx, NormalizesTo<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let def_id = goal.predicate.def_id();
+        match self.tcx().def_kind(def_id) {
+            DefKind::AssocTy | DefKind::AssocConst => {
+                // To only compute normalization once for each projection we only
+                // assemble normalization candidates if the expected term is an
+                // unconstrained inference variable.
+                //
+                // Why: For better cache hits, since if we have an unconstrained RHS then
+                // there are only as many cache keys as there are (canonicalized) alias
+                // types in each normalizes-to goal. This also weakens inference in a
+                // forwards-compatible way so we don't use the value of the RHS term to
+                // affect candidate assembly for projections.
+                //
+                // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
+                // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
+                // `U` and equate it with `u32`. This means that we don't need a separate
+                // projection cache in the solver, since we're piggybacking off of regular
+                // goal caching.
+                if self.term_is_fully_unconstrained(goal) {
+                    match self.tcx().associated_item(def_id).container {
+                        ty::AssocItemContainer::TraitContainer => {
+                            let candidates = self.assemble_and_evaluate_candidates(goal);
+                            self.merge_candidates(candidates)
+                        }
+                        ty::AssocItemContainer::ImplContainer => {
+                            self.normalize_inherent_associated_type(goal)
+                        }
+                    }
+                } else {
+                    self.set_normalizes_to_hack_goal(goal);
+                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                }
+            }
+            DefKind::AnonConst => self.normalize_anon_const(goal),
+            DefKind::OpaqueTy => self.normalize_opaque_type(goal),
+            DefKind::TyAlias => self.normalize_weak_type(goal),
+            kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
+        }
+    }
+
+    #[instrument(level = "debug", skip(self), ret)]
+    fn normalize_anon_const(
+        &mut self,
+        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        if let Some(normalized_const) = self.try_const_eval_resolve(
+            goal.param_env,
+            ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args),
+            self.tcx()
+                .type_of(goal.predicate.alias.def_id)
+                .no_bound_vars()
+                .expect("const ty should not rely on other generics"),
+        ) {
+            self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        } else {
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+        }
+    }
+}
+
+impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
+    fn self_ty(self) -> Ty<'tcx> {
+        self.self_ty()
+    }
+
+    fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
+        self.alias.trait_ref(tcx)
+    }
+
+    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+        self.with_self_ty(tcx, self_ty)
+    }
+
+    fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
+        self.trait_def_id(tcx)
+    }
+
+    fn probe_and_match_goal_against_assumption(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        assumption: ty::Clause<'tcx>,
+        then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
+    ) -> QueryResult<'tcx> {
+        if let Some(projection_pred) = assumption.as_projection_clause() {
+            if projection_pred.projection_def_id() == goal.predicate.def_id() {
+                let tcx = ecx.tcx();
+                ecx.probe_misc_candidate("assumption").enter(|ecx| {
+                    let assumption_projection_pred =
+                        ecx.instantiate_binder_with_infer(projection_pred);
+                    ecx.eq(
+                        goal.param_env,
+                        goal.predicate.alias,
+                        assumption_projection_pred.projection_ty,
+                    )?;
+                    ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
+                        .expect("expected goal term to be fully unconstrained");
+
+                    // Add GAT where clauses from the trait's definition
+                    ecx.add_goals(
+                        GoalSource::Misc,
+                        tcx.predicates_of(goal.predicate.def_id())
+                            .instantiate_own(tcx, goal.predicate.alias.args)
+                            .map(|(pred, _)| goal.with(tcx, pred)),
+                    );
+
+                    then(ecx)
+                })
+            } else {
+                Err(NoSolution)
+            }
+        } else {
+            Err(NoSolution)
+        }
+    }
+
+    fn consider_impl_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, NormalizesTo<'tcx>>,
+        impl_def_id: DefId,
+    ) -> Result<Candidate<'tcx>, NoSolution> {
+        let tcx = ecx.tcx();
+
+        let goal_trait_ref = goal.predicate.alias.trait_ref(tcx);
+        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+        let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
+        if !drcx.args_may_unify(goal_trait_ref.args, impl_trait_ref.skip_binder().args) {
+            return Err(NoSolution);
+        }
+
+        ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
+            let impl_args = ecx.fresh_args_for_item(impl_def_id);
+            let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
+
+            ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
+
+            let where_clause_bounds = tcx
+                .predicates_of(impl_def_id)
+                .instantiate(tcx, impl_args)
+                .predicates
+                .into_iter()
+                .map(|pred| goal.with(tcx, pred));
+            ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
+
+            // Add GAT where clauses from the trait's definition
+            ecx.add_goals(
+                GoalSource::Misc,
+                tcx.predicates_of(goal.predicate.def_id())
+                    .instantiate_own(tcx, goal.predicate.alias.args)
+                    .map(|(pred, _)| goal.with(tcx, pred)),
+            );
+
+            // In case the associated item is hidden due to specialization, we have to
+            // return ambiguity this would otherwise be incomplete, resulting in
+            // unsoundness during coherence (#105782).
+            let Some(assoc_def) = fetch_eligible_assoc_item_def(
+                ecx,
+                goal.param_env,
+                goal_trait_ref,
+                goal.predicate.def_id(),
+                impl_def_id,
+            )?
+            else {
+                return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+            };
+
+            let error_response = |ecx: &mut EvalCtxt<'_, 'tcx>, reason| {
+                let guar = tcx.dcx().span_delayed_bug(tcx.def_span(assoc_def.item.def_id), reason);
+                let error_term = match assoc_def.item.kind {
+                    ty::AssocKind::Const => ty::Const::new_error(
+                        tcx,
+                        guar,
+                        tcx.type_of(goal.predicate.def_id())
+                            .instantiate(tcx, goal.predicate.alias.args),
+                    )
+                    .into(),
+                    ty::AssocKind::Type => Ty::new_error(tcx, guar).into(),
+                    // This makes no sense...
+                    ty::AssocKind::Fn => span_bug!(
+                        tcx.def_span(assoc_def.item.def_id),
+                        "cannot project to an associated function"
+                    ),
+                };
+                ecx.eq(goal.param_env, goal.predicate.term, error_term)
+                    .expect("expected goal term to be fully unconstrained");
+                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            };
+
+            if !assoc_def.item.defaultness(tcx).has_value() {
+                return error_response(ecx, "missing value for assoc item in impl");
+            }
+
+            // Getting the right args here is complex, e.g. given:
+            // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
+            // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
+            // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
+            //
+            // We first rebase the goal args onto the impl, going from `[Vec<u32>, i32, u64]`
+            // to `[u32, u64]`.
+            //
+            // And then map these args to the args of the defining impl of `Assoc`, going
+            // from `[u32, u64]` to `[u32, i32, u64]`.
+            let impl_args_with_gat =
+                goal.predicate.alias.args.rebase_onto(tcx, goal_trait_ref.def_id, impl_args);
+            let args = ecx.translate_args(
+                goal.param_env,
+                impl_def_id,
+                impl_args_with_gat,
+                assoc_def.defining_node,
+            );
+
+            if !check_args_compatible(tcx, assoc_def.item, args) {
+                return error_response(
+                    ecx,
+                    "associated item has mismatched generic item arguments",
+                );
+            }
+
+            // Finally we construct the actual value of the associated type.
+            let term = match assoc_def.item.kind {
+                ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()),
+                ty::AssocKind::Const => {
+                    if tcx.features().associated_const_equality {
+                        bug!("associated const projection is not supported yet")
+                    } else {
+                        ty::EarlyBinder::bind(
+                            ty::Const::new_error_with_message(
+                                tcx,
+                                tcx.type_of(assoc_def.item.def_id).instantiate_identity(),
+                                DUMMY_SP,
+                                "associated const projection is not supported yet",
+                            )
+                            .into(),
+                        )
+                    }
+                }
+                ty::AssocKind::Fn => unreachable!("we should never project to a fn"),
+            };
+
+            ecx.eq(goal.param_env, goal.predicate.term, term.instantiate(tcx, args))
+                .expect("expected goal term to be fully unconstrained");
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error`
+    /// and succeed. Can experiment with this to figure out what results in better error messages.
+    fn consider_error_guaranteed_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        _guar: ErrorGuaranteed,
+    ) -> QueryResult<'tcx> {
+        Err(NoSolution)
+    }
+
+    fn consider_auto_trait_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        ecx.tcx().dcx().span_delayed_bug(
+            ecx.tcx().def_span(goal.predicate.def_id()),
+            "associated types not allowed on auto traits",
+        );
+        Err(NoSolution)
+    }
+
+    fn consider_trait_alias_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("trait aliases do not have associated types: {:?}", goal);
+    }
+
+    fn consider_builtin_sized_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("`Sized` does not have an associated type: {:?}", goal);
+    }
+
+    fn consider_builtin_copy_clone_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
+    }
+
+    fn consider_builtin_pointer_like_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("`PointerLike` does not have an associated type: {:?}", goal);
+    }
+
+    fn consider_builtin_fn_ptr_trait_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("`FnPtr` does not have an associated type: {:?}", goal);
+    }
+
+    fn consider_builtin_fn_trait_candidates(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        goal_kind: ty::ClosureKind,
+    ) -> QueryResult<'tcx> {
+        let tcx = ecx.tcx();
+        let tupled_inputs_and_output =
+            match structural_traits::extract_tupled_inputs_and_output_from_callable(
+                tcx,
+                goal.predicate.self_ty(),
+                goal_kind,
+            )? {
+                Some(tupled_inputs_and_output) => tupled_inputs_and_output,
+                None => {
+                    return ecx
+                        .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+                }
+            };
+        let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
+            ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
+        });
+
+        let pred = tupled_inputs_and_output
+            .map_bound(|(inputs, output)| ty::ProjectionPredicate {
+                projection_ty: ty::AliasTy::new(
+                    tcx,
+                    goal.predicate.def_id(),
+                    [goal.predicate.self_ty(), inputs],
+                ),
+                term: output.into(),
+            })
+            .to_predicate(tcx);
+
+        // A built-in `Fn` impl only holds if the output is sized.
+        // (FIXME: technically we only need to check this if the type is a fn ptr...)
+        Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)])
+    }
+
+    fn consider_builtin_tuple_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("`Tuple` does not have an associated type: {:?}", goal);
+    }
+
+    fn consider_builtin_pointee_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let tcx = ecx.tcx();
+        ecx.probe_misc_candidate("builtin pointee").enter(|ecx| {
+            let metadata_ty = match goal.predicate.self_ty().kind() {
+                ty::Bool
+                | ty::Char
+                | ty::Int(..)
+                | ty::Uint(..)
+                | ty::Float(..)
+                | ty::Array(..)
+                | ty::RawPtr(..)
+                | ty::Ref(..)
+                | ty::FnDef(..)
+                | ty::FnPtr(..)
+                | ty::Closure(..)
+                | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
+                | ty::Coroutine(..)
+                | ty::CoroutineWitness(..)
+                | ty::Never
+                | ty::Foreign(..) => tcx.types.unit,
+
+                ty::Error(e) => Ty::new_error(tcx, *e),
+
+                ty::Str | ty::Slice(_) => tcx.types.usize,
+
+                ty::Dynamic(_, _, _) => {
+                    let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
+                    tcx.type_of(dyn_metadata)
+                        .instantiate(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
+                }
+
+                ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
+                    // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
+                    let sized_predicate = ty::TraitRef::from_lang_item(
+                        tcx,
+                        LangItem::Sized,
+                        DUMMY_SP,
+                        [ty::GenericArg::from(goal.predicate.self_ty())],
+                    );
+                    // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
+                    ecx.add_goal(GoalSource::Misc, goal.with(tcx, sized_predicate));
+                    tcx.types.unit
+                }
+
+                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);
+                    }
+                },
+                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);
+                    }
+                },
+
+                ty::Infer(
+                    ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
+                )
+                | ty::Bound(..) => bug!(
+                    "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
+                    goal.predicate.self_ty()
+                ),
+            };
+
+            ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())
+                .expect("expected goal term to be fully unconstrained");
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    fn consider_builtin_future_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+            return Err(NoSolution);
+        };
+
+        // Coroutines are not futures unless they come from `async` desugaring
+        let tcx = ecx.tcx();
+        if !tcx.coroutine_is_async(def_id) {
+            return Err(NoSolution);
+        }
+
+        let term = args.as_coroutine().return_ty().into();
+
+        Self::consider_implied_clause(
+            ecx,
+            goal,
+            ty::ProjectionPredicate {
+                projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]),
+                term,
+            }
+            .to_predicate(tcx),
+            // Technically, we need to check that the future type is Sized,
+            // but that's already proven by the coroutine being WF.
+            [],
+        )
+    }
+
+    fn consider_builtin_iterator_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+            return Err(NoSolution);
+        };
+
+        // Coroutines are not Iterators unless they come from `gen` desugaring
+        let tcx = ecx.tcx();
+        if !tcx.coroutine_is_gen(def_id) {
+            return Err(NoSolution);
+        }
+
+        let term = args.as_coroutine().yield_ty().into();
+
+        Self::consider_implied_clause(
+            ecx,
+            goal,
+            ty::ProjectionPredicate {
+                projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]),
+                term,
+            }
+            .to_predicate(tcx),
+            // Technically, we need to check that the iterator type is Sized,
+            // but that's already proven by the generator being WF.
+            [],
+        )
+    }
+
+    fn consider_builtin_async_iterator_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+            return Err(NoSolution);
+        };
+
+        // Coroutines are not AsyncIterators unless they come from `gen` desugaring
+        let tcx = ecx.tcx();
+        if !tcx.coroutine_is_async_gen(def_id) {
+            return Err(NoSolution);
+        }
+
+        ecx.probe_misc_candidate("builtin AsyncIterator kind").enter(|ecx| {
+            // Take `AsyncIterator<Item = I>` and turn it into the corresponding
+            // coroutine yield ty `Poll<Option<I>>`.
+            let expected_ty = Ty::new_adt(
+                tcx,
+                tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)),
+                tcx.mk_args(&[Ty::new_adt(
+                    tcx,
+                    tcx.adt_def(tcx.require_lang_item(LangItem::Option, None)),
+                    tcx.mk_args(&[goal.predicate.term.into()]),
+                )
+                .into()]),
+            );
+            let yield_ty = args.as_coroutine().yield_ty();
+            ecx.eq(goal.param_env, expected_ty, yield_ty)?;
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    fn consider_builtin_coroutine_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+            return Err(NoSolution);
+        };
+
+        // `async`-desugared coroutines do not implement the coroutine trait
+        let tcx = ecx.tcx();
+        if !tcx.is_general_coroutine(def_id) {
+            return Err(NoSolution);
+        }
+
+        let coroutine = args.as_coroutine();
+
+        let name = tcx.associated_item(goal.predicate.def_id()).name;
+        let term = if name == sym::Return {
+            coroutine.return_ty().into()
+        } else if name == sym::Yield {
+            coroutine.yield_ty().into()
+        } else {
+            bug!("unexpected associated item `<{self_ty} as Coroutine>::{name}`")
+        };
+
+        Self::consider_implied_clause(
+            ecx,
+            goal,
+            ty::ProjectionPredicate {
+                projection_ty: ty::AliasTy::new(
+                    ecx.tcx(),
+                    goal.predicate.def_id(),
+                    [self_ty, coroutine.resume_ty()],
+                ),
+                term,
+            }
+            .to_predicate(tcx),
+            // Technically, we need to check that the coroutine type is Sized,
+            // but that's already proven by the coroutine being WF.
+            [],
+        )
+    }
+
+    fn consider_unsize_to_dyn_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("`Unsize` does not have an associated type: {:?}", goal)
+    }
+
+    fn consider_structural_builtin_unsize_candidates(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
+        bug!("`Unsize` does not have an associated type: {:?}", goal);
+    }
+
+    fn consider_builtin_discriminant_kind_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        let discriminant_ty = match *self_ty.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(..)
+            | ty::Uint(..)
+            | ty::Float(..)
+            | ty::Array(..)
+            | ty::RawPtr(..)
+            | ty::Ref(..)
+            | ty::FnDef(..)
+            | ty::FnPtr(..)
+            | ty::Closure(..)
+            | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
+            | ty::Coroutine(..)
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Foreign(..)
+            | ty::Adt(_, _)
+            | ty::Str
+            | ty::Slice(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Tuple(_)
+            | ty::Error(_) => self_ty.discriminant_ty(ecx.tcx()),
+
+            // We do not call `Ty::discriminant_ty` on alias, param, or placeholder
+            // types, which return `<self_ty as DiscriminantKind>::Discriminant`
+            // (or ICE in the case of placeholders). Projecting a type to itself
+            // is never really productive.
+            ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
+                return Err(NoSolution);
+            }
+
+            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+            | ty::Bound(..) => bug!(
+                "unexpected self ty `{:?}` when normalizing `<T as DiscriminantKind>::Discriminant`",
+                goal.predicate.self_ty()
+            ),
+        };
+
+        ecx.probe_misc_candidate("builtin discriminant kind").enter(|ecx| {
+            ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
+                .expect("expected goal term to be fully unconstrained");
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    fn consider_builtin_destruct_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("`Destruct` does not have an associated type: {:?}", goal);
+    }
+
+    fn consider_builtin_transmute_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal)
+    }
+}
+
+/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
+///
+/// FIXME: We should merge these 3 implementations as it's likely that they otherwise
+/// diverge.
+#[instrument(level = "debug", skip(ecx, param_env), ret)]
+fn fetch_eligible_assoc_item_def<'tcx>(
+    ecx: &EvalCtxt<'_, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    goal_trait_ref: ty::TraitRef<'tcx>,
+    trait_assoc_def_id: DefId,
+    impl_def_id: DefId,
+) -> Result<Option<LeafDef>, NoSolution> {
+    let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id)
+        .map_err(|ErrorGuaranteed { .. }| NoSolution)?;
+
+    let eligible = if node_item.is_final() {
+        // Non-specializable items are always projectable.
+        true
+    } else {
+        // Only reveal a specializable default if we're past type-checking
+        // and the obligation is monomorphic, otherwise passes such as
+        // transmute checking and polymorphic MIR optimizations could
+        // get a result which isn't correct for all monomorphizations.
+        if param_env.reveal() == Reveal::All {
+            let poly_trait_ref = ecx.resolve_vars_if_possible(goal_trait_ref);
+            !poly_trait_ref.still_further_specializable()
+        } else {
+            debug!(?node_item.item.def_id, "not eligible due to default");
+            false
+        }
+    };
+
+    if eligible { Ok(Some(node_item)) } else { Ok(None) }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs
new file mode 100644
index 00000000000..b5d1aa06e4e
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs
@@ -0,0 +1,106 @@
+//! Computes a normalizes-to (projection) goal for opaque types. This goal
+//! behaves differently depending on the param-env's reveal mode and whether
+//! the opaque is in a defining scope.
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::traits::Reveal;
+use rustc_middle::ty;
+use rustc_middle::ty::util::NotUniqueParam;
+
+use crate::solve::{EvalCtxt, SolverMode};
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    pub(super) fn normalize_opaque_type(
+        &mut self,
+        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let tcx = self.tcx();
+        let opaque_ty = goal.predicate.alias;
+        let expected = goal.predicate.term.ty().expect("no such thing as an opaque const");
+
+        match (goal.param_env.reveal(), self.solver_mode()) {
+            (Reveal::UserFacing, SolverMode::Normal) => {
+                let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else {
+                    return Err(NoSolution);
+                };
+                // FIXME: at some point we should call queries without defining
+                // new opaque types but having the existing opaque type definitions.
+                // This will require moving this below "Prefer opaques registered already".
+                if !self.can_define_opaque_ty(opaque_ty_def_id) {
+                    return Err(NoSolution);
+                }
+                // FIXME: This may have issues when the args contain aliases...
+                match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.args) {
+                    Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
+                        return self.evaluate_added_goals_and_make_canonical_response(
+                            Certainty::AMBIGUOUS,
+                        );
+                    }
+                    Err(_) => {
+                        return Err(NoSolution);
+                    }
+                    Ok(()) => {}
+                }
+                // Prefer opaques registered already.
+                let opaque_type_key =
+                    ty::OpaqueTypeKey { def_id: opaque_ty_def_id, args: opaque_ty.args };
+                // FIXME: This also unifies the previous hidden type with the expected.
+                //
+                // If that fails, we insert `expected` as a new hidden type instead of
+                // eagerly emitting an error.
+                let matches =
+                    self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected);
+                if !matches.is_empty() {
+                    if let Some(response) = self.try_merge_responses(&matches) {
+                        return Ok(response);
+                    } else {
+                        return self.flounder(&matches);
+                    }
+                }
+
+                let expected = match self.try_normalize_ty(goal.param_env, expected) {
+                    Some(ty) => {
+                        if ty.is_ty_var() {
+                            return self.evaluate_added_goals_and_make_canonical_response(
+                                Certainty::AMBIGUOUS,
+                            );
+                        } else {
+                            ty
+                        }
+                    }
+                    None => {
+                        return self
+                            .evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
+                    }
+                };
+
+                // Otherwise, define a new opaque type
+                self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
+                self.add_item_bounds_for_hidden_type(
+                    opaque_ty.def_id,
+                    opaque_ty.args,
+                    goal.param_env,
+                    expected,
+                );
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            }
+            (Reveal::UserFacing, SolverMode::Coherence) => {
+                // An impossible opaque type bound is the only way this goal will fail
+                // e.g. assigning `impl Copy := NotCopy`
+                self.add_item_bounds_for_hidden_type(
+                    opaque_ty.def_id,
+                    opaque_ty.args,
+                    goal.param_env,
+                    expected,
+                );
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+            }
+            (Reveal::All, _) => {
+                // FIXME: Add an assertion that opaque type storage is empty.
+                let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, opaque_ty.args);
+                self.eq(goal.param_env, expected, actual)?;
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
new file mode 100644
index 00000000000..6d5728797d1
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
@@ -0,0 +1,35 @@
+//! Computes a normalizes-to (projection) goal for inherent associated types,
+//! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`.
+//!
+//! Since a weak alias is not ambiguous, this just computes the `type_of` of
+//! the alias and registers the where-clauses of the type alias.
+use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult};
+use rustc_middle::ty;
+
+use crate::solve::EvalCtxt;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    pub(super) fn normalize_weak_type(
+        &mut self,
+        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let tcx = self.tcx();
+        let weak_ty = goal.predicate.alias;
+        let expected = goal.predicate.term.ty().expect("no such thing as a const alias");
+
+        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
+        self.eq(goal.param_env, expected, actual)?;
+
+        // Check where clauses
+        self.add_goals(
+            GoalSource::Misc,
+            tcx.predicates_of(weak_ty.def_id)
+                .instantiate(tcx, weak_ty.args)
+                .predicates
+                .into_iter()
+                .map(|pred| goal.with(tcx, pred)),
+        );
+
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
new file mode 100644
index 00000000000..30ae385a8a0
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -0,0 +1,38 @@
+use crate::solve::GoalSource;
+
+use super::EvalCtxt;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty::{self, ProjectionPredicate};
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    #[instrument(level = "debug", skip(self), ret)]
+    pub(super) fn compute_projection_goal(
+        &mut self,
+        goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let tcx = self.tcx();
+        let projection_term = match goal.predicate.term.unpack() {
+            ty::TermKind::Ty(_) => goal.predicate.projection_ty.to_ty(tcx).into(),
+            ty::TermKind::Const(_) => ty::Const::new_unevaluated(
+                tcx,
+                ty::UnevaluatedConst::new(
+                    goal.predicate.projection_ty.def_id,
+                    goal.predicate.projection_ty.args,
+                ),
+                tcx.type_of(goal.predicate.projection_ty.def_id)
+                    .instantiate(tcx, goal.predicate.projection_ty.args),
+            )
+            .into(),
+        };
+        let goal = goal.with(
+            tcx,
+            ty::PredicateKind::AliasRelate(
+                projection_term,
+                goal.predicate.term,
+                ty::AliasRelationDirection::Equate,
+            ),
+        );
+        self.add_goal(GoalSource::Misc, goal);
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs
new file mode 100644
index 00000000000..2a161c2d956
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs
@@ -0,0 +1,360 @@
+use super::inspect;
+use super::inspect::ProofTreeBuilder;
+use super::SolverMode;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_index::Idx;
+use rustc_index::IndexVec;
+use rustc_middle::dep_graph::dep_kinds;
+use rustc_middle::traits::solve::CacheData;
+use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
+use rustc_middle::ty;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::Limit;
+use std::collections::hash_map::Entry;
+
+rustc_index::newtype_index! {
+    #[orderable]
+    pub struct StackDepth {}
+}
+
+#[derive(Debug)]
+struct StackEntry<'tcx> {
+    input: CanonicalInput<'tcx>,
+    available_depth: Limit,
+    // The maximum depth reached by this stack entry, only up-to date
+    // for the top of the stack and lazily updated for the rest.
+    reached_depth: StackDepth,
+    // In case of a cycle, the depth of the root.
+    cycle_root_depth: StackDepth,
+
+    encountered_overflow: bool,
+    has_been_used: bool,
+    /// Starts out as `None` and gets set when rerunning this
+    /// goal in case we encounter a cycle.
+    provisional_result: Option<QueryResult<'tcx>>,
+
+    /// We put only the root goal of a coinductive cycle into the global cache.
+    ///
+    /// If we were to use that result when later trying to prove another cycle
+    /// participant, we can end up with unstable query results.
+    ///
+    /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for
+    /// an example of where this is needed.
+    cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
+}
+
+pub(super) struct SearchGraph<'tcx> {
+    mode: SolverMode,
+    local_overflow_limit: usize,
+    /// The stack of goals currently being computed.
+    ///
+    /// An element is *deeper* in the stack if its index is *lower*.
+    stack: IndexVec<StackDepth, StackEntry<'tcx>>,
+    stack_entries: FxHashMap<CanonicalInput<'tcx>, StackDepth>,
+}
+
+impl<'tcx> SearchGraph<'tcx> {
+    pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> {
+        Self {
+            mode,
+            local_overflow_limit: tcx.recursion_limit().0.checked_ilog2().unwrap_or(0) as usize,
+            stack: Default::default(),
+            stack_entries: Default::default(),
+        }
+    }
+
+    pub(super) fn solver_mode(&self) -> SolverMode {
+        self.mode
+    }
+
+    pub(super) fn local_overflow_limit(&self) -> usize {
+        self.local_overflow_limit
+    }
+
+    /// Update the stack and reached depths on cache hits.
+    #[instrument(level = "debug", skip(self))]
+    fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) {
+        let reached_depth = self.stack.next_index().plus(additional_depth);
+        if let Some(last) = self.stack.raw.last_mut() {
+            last.reached_depth = last.reached_depth.max(reached_depth);
+            last.encountered_overflow |= encountered_overflow;
+        }
+    }
+
+    /// Pops the highest goal from the stack, lazily updating the
+    /// the next goal in the stack.
+    ///
+    /// Directly popping from the stack instead of using this method
+    /// would cause us to not track overflow and recursion depth correctly.
+    fn pop_stack(&mut self) -> StackEntry<'tcx> {
+        let elem = self.stack.pop().unwrap();
+        assert!(self.stack_entries.remove(&elem.input).is_some());
+        if let Some(last) = self.stack.raw.last_mut() {
+            last.reached_depth = last.reached_depth.max(elem.reached_depth);
+            last.encountered_overflow |= elem.encountered_overflow;
+        }
+        elem
+    }
+
+    /// The trait solver behavior is different for coherence
+    /// so we use a separate cache. Alternatively we could use
+    /// a single cache and share it between coherence and ordinary
+    /// trait solving.
+    pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> {
+        match self.mode {
+            SolverMode::Normal => &tcx.new_solver_evaluation_cache,
+            SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache,
+        }
+    }
+
+    pub(super) fn is_empty(&self) -> bool {
+        self.stack.is_empty()
+    }
+
+    pub(super) fn current_goal_is_normalizes_to(&self) -> bool {
+        self.stack.raw.last().map_or(false, |e| {
+            matches!(
+                e.input.value.goal.predicate.kind().skip_binder(),
+                ty::PredicateKind::NormalizesTo(..)
+            )
+        })
+    }
+
+    /// Returns the remaining depth allowed for nested goals.
+    ///
+    /// This is generally simply one less than the current depth.
+    /// However, if we encountered overflow, we significantly reduce
+    /// the remaining depth of all nested goals to prevent hangs
+    /// in case there is exponential blowup.
+    fn allowed_depth_for_nested(
+        tcx: TyCtxt<'tcx>,
+        stack: &IndexVec<StackDepth, StackEntry<'tcx>>,
+    ) -> Option<Limit> {
+        if let Some(last) = stack.raw.last() {
+            if last.available_depth.0 == 0 {
+                return None;
+            }
+
+            Some(if last.encountered_overflow {
+                Limit(last.available_depth.0 / 4)
+            } else {
+                Limit(last.available_depth.0 - 1)
+            })
+        } else {
+            Some(tcx.recursion_limit())
+        }
+    }
+
+    /// Probably the most involved method of the whole solver.
+    ///
+    /// Given some goal which is proven via the `prove_goal` closure, this
+    /// handles caching, overflow, and coinductive cycles.
+    pub(super) fn with_new_goal(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        input: CanonicalInput<'tcx>,
+        inspect: &mut ProofTreeBuilder<'tcx>,
+        mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
+    ) -> QueryResult<'tcx> {
+        // Check for overflow.
+        let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else {
+            if let Some(last) = self.stack.raw.last_mut() {
+                last.encountered_overflow = true;
+            }
+
+            inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
+            return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
+        };
+
+        // Try to fetch the goal from the global cache.
+        'global: {
+            let Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) =
+                self.global_cache(tcx).get(
+                    tcx,
+                    input,
+                    |cycle_participants| {
+                        self.stack.iter().any(|entry| cycle_participants.contains(&entry.input))
+                    },
+                    available_depth,
+                )
+            else {
+                break 'global;
+            };
+
+            // If we're building a proof tree and the current cache entry does not
+            // contain a proof tree, we do not use the entry but instead recompute
+            // the goal. We simply overwrite the existing entry once we're done,
+            // caching the proof tree.
+            if !inspect.is_noop() {
+                if let Some(revisions) = proof_tree {
+                    inspect.goal_evaluation_kind(
+                        inspect::WipCanonicalGoalEvaluationKind::Interned { revisions },
+                    );
+                } else {
+                    break 'global;
+                }
+            }
+
+            self.on_cache_hit(reached_depth, encountered_overflow);
+            return result;
+        }
+
+        // Check whether we're in a cycle.
+        match self.stack_entries.entry(input) {
+            // No entry, we push this goal on the stack and try to prove it.
+            Entry::Vacant(v) => {
+                let depth = self.stack.next_index();
+                let entry = StackEntry {
+                    input,
+                    available_depth,
+                    reached_depth: depth,
+                    cycle_root_depth: depth,
+                    encountered_overflow: false,
+                    has_been_used: false,
+                    provisional_result: None,
+                    cycle_participants: Default::default(),
+                };
+                assert_eq!(self.stack.push(entry), depth);
+                v.insert(depth);
+            }
+            // We have a nested goal which relies on a goal `root` deeper in the stack.
+            //
+            // We first store that we may have to reprove `root` in case the provisional
+            // response is not equal to the final response. We also update the depth of all
+            // goals which recursively depend on our current goal to depend on `root`
+            // instead.
+            //
+            // Finally we can return either the provisional response for that goal if we have a
+            // coinductive cycle or an ambiguous result if the cycle is inductive.
+            Entry::Occupied(entry) => {
+                inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CycleInStack);
+
+                let stack_depth = *entry.get();
+                debug!("encountered cycle with depth {stack_depth:?}");
+                // We start by updating the root depth of all cycle participants, and
+                // add all cycle participants to the root.
+                let root_depth = self.stack[stack_depth].cycle_root_depth;
+                let (prev, participants) = self.stack.raw.split_at_mut(stack_depth.as_usize() + 1);
+                let root = &mut prev[root_depth.as_usize()];
+                for entry in participants {
+                    debug_assert!(entry.cycle_root_depth >= root_depth);
+                    entry.cycle_root_depth = root_depth;
+                    root.cycle_participants.insert(entry.input);
+                    // FIXME(@lcnr): I believe that this line is needed as we could
+                    // otherwise access a cache entry for the root of a cycle while
+                    // computing the result for a cycle participant. This can result
+                    // in unstable results due to incompleteness.
+                    //
+                    // However, a test for this would be an even more complex version of
+                    // tests/ui/traits/next-solver/coinduction/incompleteness-unstable-result.rs.
+                    // I did not bother to write such a test and we have no regression test
+                    // for this. It would be good to have such a test :)
+                    #[allow(rustc::potential_query_instability)]
+                    root.cycle_participants.extend(entry.cycle_participants.drain());
+                }
+
+                // If we're in a cycle, we have to retry proving the cycle head
+                // until we reach a fixpoint. It is not enough to simply retry the
+                // `root` goal of this cycle.
+                //
+                // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
+                // for an example.
+                self.stack[stack_depth].has_been_used = true;
+                return if let Some(result) = self.stack[stack_depth].provisional_result {
+                    result
+                } else {
+                    // If we don't have a provisional result yet we're in the first iteration,
+                    // so we start with no constraints.
+                    let is_coinductive = self.stack.raw[stack_depth.index()..]
+                        .iter()
+                        .all(|entry| entry.input.value.goal.predicate.is_coinductive(tcx));
+                    if is_coinductive {
+                        Self::response_no_constraints(tcx, input, Certainty::Yes)
+                    } else {
+                        Self::response_no_constraints(tcx, input, Certainty::OVERFLOW)
+                    }
+                };
+            }
+        }
+
+        // This is for global caching, so we properly track query dependencies.
+        // Everything that affects the `result` should be performed within this
+        // `with_anon_task` closure.
+        let ((final_entry, result), dep_node) =
+            tcx.dep_graph.with_anon_task(tcx, dep_kinds::TraitSelect, || {
+                // When we encounter a coinductive cycle, we have to fetch the
+                // result of that cycle while we are still computing it. Because
+                // of this we continuously recompute the cycle until the result
+                // of the previous iteration is equal to the final result, at which
+                // point we are done.
+                for _ in 0..self.local_overflow_limit() {
+                    let result = prove_goal(self, inspect);
+
+                    // Check whether the current goal is the root of a cycle and whether
+                    // we have to rerun because its provisional result differed from the
+                    // final result.
+                    let stack_entry = self.pop_stack();
+                    debug_assert_eq!(stack_entry.input, input);
+                    if stack_entry.has_been_used
+                        && stack_entry.provisional_result.map_or(true, |r| r != result)
+                    {
+                        // If so, update its provisional result and reevaluate it.
+                        let depth = self.stack.push(StackEntry {
+                            has_been_used: false,
+                            provisional_result: Some(result),
+                            ..stack_entry
+                        });
+                        assert_eq!(self.stack_entries.insert(input, depth), None);
+                    } else {
+                        return (stack_entry, result);
+                    }
+                }
+
+                debug!("canonical cycle overflow");
+                let current_entry = self.pop_stack();
+                let result = Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
+                (current_entry, result)
+            });
+
+        let proof_tree = inspect.finalize_evaluation(tcx);
+
+        // We're now done with this goal. In case this goal is involved in a larger cycle
+        // do not remove it from the provisional cache and update its provisional result.
+        // We only add the root of cycles to the global cache.
+        //
+        // It is not possible for any nested goal to depend on something deeper on the
+        // stack, as this would have also updated the depth of the current goal.
+        if final_entry.cycle_root_depth == self.stack.next_index() {
+            // When encountering a cycle, both inductive and coinductive, we only
+            // move the root into the global cache. We also store all other cycle
+            // participants involved.
+            //
+            // We disable the global cache entry of the root goal if a cycle
+            // participant is on the stack. This is necessary to prevent unstable
+            // results. See the comment of `StackEntry::cycle_participants` for
+            // more details.
+            let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len();
+            self.global_cache(tcx).insert(
+                tcx,
+                input,
+                proof_tree,
+                reached_depth,
+                final_entry.encountered_overflow,
+                final_entry.cycle_participants,
+                dep_node,
+                result,
+            )
+        }
+
+        result
+    }
+
+    fn response_no_constraints(
+        tcx: TyCtxt<'tcx>,
+        goal: CanonicalInput<'tcx>,
+        certainty: Certainty,
+    ) -> QueryResult<'tcx> {
+        Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
new file mode 100644
index 00000000000..be079275684
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -0,0 +1,1021 @@
+//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
+
+use super::assembly::{self, structural_traits, Candidate};
+use super::{EvalCtxt, GoalSource, SolverMode};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{LangItem, Movability};
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::inspect::ProbeKind;
+use rustc_middle::traits::solve::{
+    CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult,
+};
+use rustc_middle::traits::{BuiltinImplSource, Reveal};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
+use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
+use rustc_span::{ErrorGuaranteed, DUMMY_SP};
+
+impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
+    fn self_ty(self) -> Ty<'tcx> {
+        self.self_ty()
+    }
+
+    fn trait_ref(self, _: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
+        self.trait_ref
+    }
+
+    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+        self.with_self_ty(tcx, self_ty)
+    }
+
+    fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId {
+        self.def_id()
+    }
+
+    fn consider_impl_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, TraitPredicate<'tcx>>,
+        impl_def_id: DefId,
+    ) -> Result<Candidate<'tcx>, NoSolution> {
+        let tcx = ecx.tcx();
+
+        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+        let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
+        if !drcx.args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) {
+            return Err(NoSolution);
+        }
+
+        let impl_polarity = tcx.impl_polarity(impl_def_id);
+        // An upper bound of the certainty of this goal, used to lower the certainty
+        // of reservation impl to ambiguous during coherence.
+        let maximal_certainty = match impl_polarity {
+            ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => {
+                match impl_polarity == goal.predicate.polarity {
+                    true => Certainty::Yes,
+                    false => return Err(NoSolution),
+                }
+            }
+            ty::ImplPolarity::Reservation => match ecx.solver_mode() {
+                SolverMode::Normal => return Err(NoSolution),
+                SolverMode::Coherence => Certainty::AMBIGUOUS,
+            },
+        };
+
+        ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
+            let impl_args = ecx.fresh_args_for_item(impl_def_id);
+            let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
+
+            ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
+            let where_clause_bounds = tcx
+                .predicates_of(impl_def_id)
+                .instantiate(tcx, impl_args)
+                .predicates
+                .into_iter()
+                .map(|pred| goal.with(tcx, pred));
+            ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
+
+            ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
+        })
+    }
+
+    fn consider_error_guaranteed_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        _guar: ErrorGuaranteed,
+    ) -> QueryResult<'tcx> {
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn probe_and_match_goal_against_assumption(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        assumption: ty::Clause<'tcx>,
+        then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
+    ) -> QueryResult<'tcx> {
+        if let Some(trait_clause) = assumption.as_trait_clause() {
+            if trait_clause.def_id() == goal.predicate.def_id()
+                && trait_clause.polarity() == goal.predicate.polarity
+            {
+                // FIXME: Constness
+                ecx.probe_misc_candidate("assumption").enter(|ecx| {
+                    let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
+                    ecx.eq(
+                        goal.param_env,
+                        goal.predicate.trait_ref,
+                        assumption_trait_pred.trait_ref,
+                    )?;
+                    then(ecx)
+                })
+            } else {
+                Err(NoSolution)
+            }
+        } else {
+            Err(NoSolution)
+        }
+    }
+
+    fn consider_auto_trait_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) {
+            return result;
+        }
+
+        // Don't call `type_of` on a local TAIT that's in the defining scope,
+        // since that may require calling `typeck` on the same item we're
+        // currently type checking, which will result in a fatal cycle that
+        // ideally we want to avoid, since we can make progress on this goal
+        // via an alias bound or a locally-inferred hidden type instead.
+        //
+        // Also, don't call `type_of` on a TAIT in `Reveal::All` mode, since
+        // we already normalize the self type in
+        // `assemble_candidates_after_normalizing_self_ty`, and we'd
+        // just be registering an identical candidate here.
+        //
+        // We always return `Err(NoSolution)` here in `SolverMode::Coherence`
+        // since we'll always register an ambiguous candidate in
+        // `assemble_candidates_after_normalizing_self_ty` due to normalizing
+        // the TAIT.
+        if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
+            if matches!(goal.param_env.reveal(), Reveal::All)
+                || matches!(ecx.solver_mode(), SolverMode::Coherence)
+                || opaque_ty
+                    .def_id
+                    .as_local()
+                    .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id))
+            {
+                return Err(NoSolution);
+            }
+        }
+
+        ecx.probe_and_evaluate_goal_for_constituent_tys(
+            goal,
+            structural_traits::instantiate_constituent_tys_for_auto_trait,
+        )
+    }
+
+    fn consider_trait_alias_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        let tcx = ecx.tcx();
+
+        ecx.probe_misc_candidate("trait alias").enter(|ecx| {
+            let nested_obligations = tcx
+                .predicates_of(goal.predicate.def_id())
+                .instantiate(tcx, goal.predicate.trait_ref.args);
+            // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
+            ecx.add_goals(
+                GoalSource::Misc,
+                nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)),
+            );
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    fn consider_builtin_sized_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        ecx.probe_and_evaluate_goal_for_constituent_tys(
+            goal,
+            structural_traits::instantiate_constituent_tys_for_sized_trait,
+        )
+    }
+
+    fn consider_builtin_copy_clone_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        ecx.probe_and_evaluate_goal_for_constituent_tys(
+            goal,
+            structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
+        )
+    }
+
+    fn consider_builtin_pointer_like_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        // The regions of a type don't affect the size of the type
+        let tcx = ecx.tcx();
+        // We should erase regions from both the param-env and type, since both
+        // may have infer regions. Specifically, after canonicalizing and instantiating,
+        // early bound regions turn into region vars in both the new and old solver.
+        let key = tcx.erase_regions(goal.param_env.and(goal.predicate.self_ty()));
+        // But if there are inference variables, we have to wait until it's resolved.
+        if key.has_non_region_infer() {
+            return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+        }
+
+        if let Ok(layout) = tcx.layout_of(key)
+            && layout.layout.is_pointer_like(&tcx.data_layout)
+        {
+            // FIXME: We could make this faster by making a no-constraints response
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        } else {
+            Err(NoSolution)
+        }
+    }
+
+    fn consider_builtin_fn_ptr_trait_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        match goal.predicate.polarity {
+            ty::ImplPolarity::Positive => {
+                if self_ty.is_fn_ptr() {
+                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                } else {
+                    Err(NoSolution)
+                }
+            }
+            ty::ImplPolarity::Negative => {
+                // If a type is rigid and not a fn ptr, then we know for certain
+                // that it does *not* implement `FnPtr`.
+                if !self_ty.is_fn_ptr() && self_ty.is_known_rigid() {
+                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                } else {
+                    Err(NoSolution)
+                }
+            }
+            // FIXME: Goal polarity should be split from impl polarity
+            ty::ImplPolarity::Reservation => {
+                bug!("we never expect a `Reservation` polarity in a trait goal")
+            }
+        }
+    }
+
+    fn consider_builtin_fn_trait_candidates(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        goal_kind: ty::ClosureKind,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        let tcx = ecx.tcx();
+        let tupled_inputs_and_output =
+            match structural_traits::extract_tupled_inputs_and_output_from_callable(
+                tcx,
+                goal.predicate.self_ty(),
+                goal_kind,
+            )? {
+                Some(a) => a,
+                None => {
+                    return ecx
+                        .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+                }
+            };
+        let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
+            ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
+        });
+
+        let pred = tupled_inputs_and_output
+            .map_bound(|(inputs, _)| {
+                ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
+            })
+            .to_predicate(tcx);
+        // A built-in `Fn` impl only holds if the output is sized.
+        // (FIXME: technically we only need to check this if the type is a fn ptr...)
+        Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)])
+    }
+
+    fn consider_builtin_tuple_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        } else {
+            Err(NoSolution)
+        }
+    }
+
+    fn consider_builtin_pointee_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn consider_builtin_future_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+            return Err(NoSolution);
+        };
+
+        // Coroutines are not futures unless they come from `async` desugaring
+        let tcx = ecx.tcx();
+        if !tcx.coroutine_is_async(def_id) {
+            return Err(NoSolution);
+        }
+
+        // Async coroutine unconditionally implement `Future`
+        // Technically, we need to check that the future output type is Sized,
+        // but that's already proven by the coroutine being WF.
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn consider_builtin_iterator_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+            return Err(NoSolution);
+        };
+
+        // Coroutines are not iterators unless they come from `gen` desugaring
+        let tcx = ecx.tcx();
+        if !tcx.coroutine_is_gen(def_id) {
+            return Err(NoSolution);
+        }
+
+        // Gen coroutines unconditionally implement `Iterator`
+        // Technically, we need to check that the iterator output type is Sized,
+        // but that's already proven by the coroutines being WF.
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn consider_builtin_async_iterator_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+            return Err(NoSolution);
+        };
+
+        // Coroutines are not iterators unless they come from `gen` desugaring
+        let tcx = ecx.tcx();
+        if !tcx.coroutine_is_async_gen(def_id) {
+            return Err(NoSolution);
+        }
+
+        // Gen coroutines unconditionally implement `Iterator`
+        // Technically, we need to check that the iterator output type is Sized,
+        // but that's already proven by the coroutines being WF.
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn consider_builtin_coroutine_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        let self_ty = goal.predicate.self_ty();
+        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+            return Err(NoSolution);
+        };
+
+        // `async`-desugared coroutines do not implement the coroutine trait
+        let tcx = ecx.tcx();
+        if !tcx.is_general_coroutine(def_id) {
+            return Err(NoSolution);
+        }
+
+        let coroutine = args.as_coroutine();
+        Self::consider_implied_clause(
+            ecx,
+            goal,
+            ty::TraitRef::new(tcx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()])
+                .to_predicate(tcx),
+            // Technically, we need to check that the coroutine types are Sized,
+            // but that's already proven by the coroutine being WF.
+            [],
+        )
+    }
+
+    fn consider_builtin_discriminant_kind_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        // `DiscriminantKind` is automatically implemented for every type.
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn consider_builtin_destruct_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        // FIXME(-Znext-solver): Implement this when we get const working in the new solver
+
+        // `Destruct` is automatically implemented for every type in
+        // non-const environments.
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn consider_builtin_transmute_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        // `rustc_transmute` does not have support for type or const params
+        if goal.has_non_region_placeholders() {
+            return Err(NoSolution);
+        }
+
+        // Erase regions because we compute layouts in `rustc_transmute`,
+        // which will ICE for region vars.
+        let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args);
+
+        let Some(assume) =
+            rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3))
+        else {
+            return Err(NoSolution);
+        };
+
+        let certainty = ecx.is_transmutable(
+            rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) },
+            args.type_at(2),
+            assume,
+        )?;
+        ecx.evaluate_added_goals_and_make_canonical_response(certainty)
+    }
+
+    fn consider_unsize_to_dyn_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| {
+            let a_ty = goal.predicate.self_ty();
+            // We need to normalize the b_ty since it's destructured as a `dyn Trait`.
+            let Some(b_ty) =
+                ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
+            else {
+                return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
+            };
+
+            let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
+                return Err(NoSolution);
+            };
+
+            let tcx = ecx.tcx();
+
+            // Can only unsize to an object-safe trait.
+            if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
+                return Err(NoSolution);
+            }
+
+            // Check that the type implements all of the predicates of the trait object.
+            // (i.e. the principal, all of the associated types match, and any auto traits)
+            ecx.add_goals(
+                GoalSource::ImplWhereBound,
+                b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
+            );
+
+            // The type must be `Sized` to be unsized.
+            if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
+                ecx.add_goal(
+                    GoalSource::ImplWhereBound,
+                    goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])),
+                );
+            } else {
+                return Err(NoSolution);
+            }
+
+            // The type must outlive the lifetime of the `dyn` we're unsizing into.
+            ecx.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    /// ```ignore (builtin impl example)
+    /// trait Trait {
+    ///     fn foo(&self);
+    /// }
+    /// // results in the following builtin impl
+    /// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
+    /// ```
+    fn consider_structural_builtin_unsize_candidates(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return vec![];
+        }
+
+        let misc_candidate = |ecx: &mut EvalCtxt<'_, 'tcx>, certainty| {
+            (
+                ecx.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(),
+                BuiltinImplSource::Misc,
+            )
+        };
+
+        let result_to_single = |result, source| match result {
+            Ok(resp) => vec![(resp, source)],
+            Err(NoSolution) => vec![],
+        };
+
+        ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| {
+            let a_ty = goal.predicate.self_ty();
+            // We need to normalize the b_ty since it's matched structurally
+            // in the other functions below.
+            let b_ty = match ecx
+                .try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
+            {
+                Some(b_ty) => b_ty,
+                None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
+            };
+
+            let goal = goal.with(ecx.tcx(), (a_ty, b_ty));
+            match (a_ty.kind(), b_ty.kind()) {
+                (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"),
+                (_, ty::Infer(ty::TyVar(..))) => vec![misc_candidate(ecx, Certainty::AMBIGUOUS)],
+
+                // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`.
+                (
+                    &ty::Dynamic(a_data, a_region, ty::Dyn),
+                    &ty::Dynamic(b_data, b_region, ty::Dyn),
+                ) => ecx.consider_builtin_dyn_upcast_candidates(
+                    goal, a_data, a_region, b_data, b_region,
+                ),
+
+                // `T` -> `dyn Trait` unsizing is handled separately in `consider_unsize_to_dyn_candidate`
+                (_, &ty::Dynamic(..)) => vec![],
+
+                // `[T; N]` -> `[T]` unsizing
+                (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single(
+                    ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty),
+                    BuiltinImplSource::Misc,
+                ),
+
+                // `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
+                (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
+                    if a_def.is_struct() && a_def == b_def =>
+                {
+                    result_to_single(
+                        ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args),
+                        BuiltinImplSource::Misc,
+                    )
+                }
+
+                //  `(A, B, T)` -> `(A, B, U)` where `T: Unsize<U>`
+                (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
+                    if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
+                {
+                    result_to_single(
+                        ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys),
+                        BuiltinImplSource::TupleUnsizing,
+                    )
+                }
+
+                _ => vec![],
+            }
+        })
+    }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    /// Trait upcasting allows for coercions between trait objects:
+    /// ```ignore (builtin impl example)
+    /// trait Super {}
+    /// trait Trait: Super {}
+    /// // results in builtin impls upcasting to a super trait
+    /// impl<'a, 'b: 'a> Unsize<dyn Super + 'a> for dyn Trait + 'b {}
+    /// // and impls removing auto trait bounds.
+    /// impl<'a, 'b: 'a> Unsize<dyn Trait + 'a> for dyn Trait + Send + 'b {}
+    /// ```
+    fn consider_builtin_dyn_upcast_candidates(
+        &mut self,
+        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+        a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        a_region: ty::Region<'tcx>,
+        b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        b_region: ty::Region<'tcx>,
+    ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
+        let tcx = self.tcx();
+        let Goal { predicate: (a_ty, _b_ty), .. } = goal;
+
+        // All of a's auto traits need to be in b's auto traits.
+        let auto_traits_compatible =
+            b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
+        if !auto_traits_compatible {
+            return vec![];
+        }
+
+        let mut responses = vec![];
+        // If the principal def ids match (or are both none), then we're not doing
+        // trait upcasting. We're just removing auto traits (or shortening the lifetime).
+        if a_data.principal_def_id() == b_data.principal_def_id() {
+            if let Ok(resp) = self.consider_builtin_upcast_to_principal(
+                goal,
+                a_data,
+                a_region,
+                b_data,
+                b_region,
+                a_data.principal(),
+            ) {
+                responses.push((resp, BuiltinImplSource::Misc));
+            }
+        } else if let Some(a_principal) = a_data.principal() {
+            self.walk_vtable(
+                a_principal.with_self_ty(tcx, a_ty),
+                |ecx, new_a_principal, _, vtable_vptr_slot| {
+                    if let Ok(resp) = ecx.probe_misc_candidate("dyn upcast").enter(|ecx| {
+                        ecx.consider_builtin_upcast_to_principal(
+                            goal,
+                            a_data,
+                            a_region,
+                            b_data,
+                            b_region,
+                            Some(new_a_principal.map_bound(|trait_ref| {
+                                ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
+                            })),
+                        )
+                    }) {
+                        responses
+                            .push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }));
+                    }
+                },
+            );
+        }
+
+        responses
+    }
+
+    fn consider_builtin_upcast_to_principal(
+        &mut self,
+        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+        a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        a_region: ty::Region<'tcx>,
+        b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        b_region: ty::Region<'tcx>,
+        upcast_principal: Option<ty::PolyExistentialTraitRef<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let param_env = goal.param_env;
+
+        // More than one projection in a_ty's bounds may match the projection
+        // in b_ty's bound. Use this to first determine *which* apply without
+        // having any inference side-effects. We process obligations because
+        // unification may initially succeed due to deferred projection equality.
+        let projection_may_match =
+            |ecx: &mut Self,
+             source_projection: ty::PolyExistentialProjection<'tcx>,
+             target_projection: ty::PolyExistentialProjection<'tcx>| {
+                source_projection.item_def_id() == target_projection.item_def_id()
+                    && ecx
+                        .probe(|_| ProbeKind::UpcastProjectionCompatibility)
+                        .enter(|ecx| -> Result<(), NoSolution> {
+                            ecx.eq(param_env, source_projection, target_projection)?;
+                            let _ = ecx.try_evaluate_added_goals()?;
+                            Ok(())
+                        })
+                        .is_ok()
+            };
+
+        for bound in b_data {
+            match bound.skip_binder() {
+                // Check that a's supertrait (upcast_principal) is compatible
+                // with the target (b_ty).
+                ty::ExistentialPredicate::Trait(target_principal) => {
+                    self.eq(param_env, upcast_principal.unwrap(), bound.rebind(target_principal))?;
+                }
+                // Check that b_ty's projection is satisfied by exactly one of
+                // a_ty's projections. First, we look through the list to see if
+                // any match. If not, error. Then, if *more* than one matches, we
+                // return ambiguity. Otherwise, if exactly one matches, equate
+                // it with b_ty's projection.
+                ty::ExistentialPredicate::Projection(target_projection) => {
+                    let target_projection = bound.rebind(target_projection);
+                    let mut matching_projections =
+                        a_data.projection_bounds().filter(|source_projection| {
+                            projection_may_match(self, *source_projection, target_projection)
+                        });
+                    let Some(source_projection) = matching_projections.next() else {
+                        return Err(NoSolution);
+                    };
+                    if matching_projections.next().is_some() {
+                        return self.evaluate_added_goals_and_make_canonical_response(
+                            Certainty::AMBIGUOUS,
+                        );
+                    }
+                    self.eq(param_env, source_projection, target_projection)?;
+                }
+                // Check that b_ty's auto traits are present in a_ty's bounds.
+                ty::ExistentialPredicate::AutoTrait(def_id) => {
+                    if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
+                        return Err(NoSolution);
+                    }
+                }
+            }
+        }
+
+        // Also require that a_ty's lifetime outlives b_ty's lifetime.
+        self.add_goal(
+            GoalSource::ImplWhereBound,
+            Goal::new(
+                self.tcx(),
+                param_env,
+                ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
+            ),
+        );
+
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    /// We have the following builtin impls for arrays:
+    /// ```ignore (builtin impl example)
+    /// impl<T: ?Sized, const N: usize> Unsize<[T]> for [T; N] {}
+    /// ```
+    /// While the impl itself could theoretically not be builtin,
+    /// the actual unsizing behavior is builtin. Its also easier to
+    /// make all impls of `Unsize` builtin as we're able to use
+    /// `#[rustc_deny_explicit_impl]` in this case.
+    fn consider_builtin_array_unsize(
+        &mut self,
+        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+        a_elem_ty: Ty<'tcx>,
+        b_elem_ty: Ty<'tcx>,
+    ) -> QueryResult<'tcx> {
+        self.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    /// We generate a builtin `Unsize` impls for structs with generic parameters only
+    /// mentioned by the last field.
+    /// ```ignore (builtin impl example)
+    /// struct Foo<T, U: ?Sized> {
+    ///     sized_field: Vec<T>,
+    ///     unsizable: Box<U>,
+    /// }
+    /// // results in the following builtin impl
+    /// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<Foo<T, V>> for Foo<T, U>
+    /// where
+    ///     Box<U>: Unsize<Box<V>>,
+    /// {}
+    /// ```
+    fn consider_builtin_struct_unsize(
+        &mut self,
+        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+        def: ty::AdtDef<'tcx>,
+        a_args: ty::GenericArgsRef<'tcx>,
+        b_args: ty::GenericArgsRef<'tcx>,
+    ) -> QueryResult<'tcx> {
+        let tcx = self.tcx();
+        let Goal { predicate: (_a_ty, b_ty), .. } = goal;
+
+        let unsizing_params = tcx.unsizing_params_for_adt(def.did());
+        // We must be unsizing some type parameters. This also implies
+        // that the struct has a tail field.
+        if unsizing_params.is_empty() {
+            return Err(NoSolution);
+        }
+
+        let tail_field = def.non_enum_variant().tail();
+        let tail_field_ty = tcx.type_of(tail_field.did);
+
+        let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
+        let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
+
+        // Substitute just the unsizing params from B into A. The type after
+        // this substitution must be equal to B. This is so we don't unsize
+        // unrelated type parameters.
+        let new_a_args = tcx.mk_args_from_iter(
+            a_args
+                .iter()
+                .enumerate()
+                .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }),
+        );
+        let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args);
+
+        // Finally, we require that `TailA: Unsize<TailB>` for the tail field
+        // types.
+        self.eq(goal.param_env, unsized_a_ty, b_ty)?;
+        self.add_goal(
+            GoalSource::ImplWhereBound,
+            goal.with(
+                tcx,
+                ty::TraitRef::new(
+                    tcx,
+                    tcx.lang_items().unsize_trait().unwrap(),
+                    [a_tail_ty, b_tail_ty],
+                ),
+            ),
+        );
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    /// We generate the following builtin impl for tuples of all sizes.
+    ///
+    /// This impl is still unstable and we emit a feature error when it
+    /// when it is used by a coercion.
+    /// ```ignore (builtin impl example)
+    /// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<(T, V)> for (T, U)
+    /// where
+    ///     U: Unsize<V>,
+    /// {}
+    /// ```
+    fn consider_builtin_tuple_unsize(
+        &mut self,
+        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+        a_tys: &'tcx ty::List<Ty<'tcx>>,
+        b_tys: &'tcx ty::List<Ty<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let tcx = self.tcx();
+        let Goal { predicate: (_a_ty, b_ty), .. } = goal;
+
+        let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
+        let &b_last_ty = b_tys.last().unwrap();
+
+        // Substitute just the tail field of B., and require that they're equal.
+        let unsized_a_ty =
+            Ty::new_tup_from_iter(tcx, a_rest_tys.iter().copied().chain([b_last_ty]));
+        self.eq(goal.param_env, unsized_a_ty, b_ty)?;
+
+        // Similar to ADTs, require that we can unsize the tail.
+        self.add_goal(
+            GoalSource::ImplWhereBound,
+            goal.with(
+                tcx,
+                ty::TraitRef::new(
+                    tcx,
+                    tcx.lang_items().unsize_trait().unwrap(),
+                    [a_last_ty, b_last_ty],
+                ),
+            ),
+        );
+        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    // Return `Some` if there is an impl (built-in or user provided) that may
+    // hold for the self type of the goal, which for coherence and soundness
+    // purposes must disqualify the built-in auto impl assembled by considering
+    // the type's constituent types.
+    fn disqualify_auto_trait_candidate_due_to_possible_impl(
+        &mut self,
+        goal: Goal<'tcx, TraitPredicate<'tcx>>,
+    ) -> Option<QueryResult<'tcx>> {
+        let self_ty = goal.predicate.self_ty();
+        match *self_ty.kind() {
+            // Stall int and float vars until they are resolved to a concrete
+            // numerical type. That's because the check for impls below treats
+            // int vars as matching any impl. Even if we filtered such impls,
+            // we probably don't want to treat an `impl !AutoTrait for i32` as
+            // disqualifying the built-in auto impl for `i64: AutoTrait` either.
+            ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
+                Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS))
+            }
+
+            // These types cannot be structurally decomposed into constituent
+            // types, and therefore have no built-in auto impl.
+            ty::Dynamic(..)
+            | ty::Param(..)
+            | ty::Foreign(..)
+            | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..)
+            | ty::Placeholder(..) => Some(Err(NoSolution)),
+
+            ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
+
+            // Coroutines have one special built-in candidate, `Unpin`, which
+            // takes precedence over the structural auto trait candidate being
+            // assembled.
+            ty::Coroutine(def_id, _)
+                if Some(goal.predicate.def_id()) == self.tcx().lang_items().unpin_trait() =>
+            {
+                match self.tcx().coroutine_movability(def_id) {
+                    Movability::Static => Some(Err(NoSolution)),
+                    Movability::Movable => {
+                        Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
+                    }
+                }
+            }
+
+            // For rigid types, any possible implementation that could apply to
+            // the type (even if after unification and processing nested goals
+            // it does not hold) will disqualify the built-in auto impl.
+            //
+            // This differs from the current stable behavior and fixes #84857.
+            // Due to breakage found via crater, we currently instead lint
+            // patterns which can be used to exploit this unsoundness on stable,
+            // see #93367 for more details.
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Closure(_, _)
+            | ty::Coroutine(_, _)
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Tuple(_)
+            | ty::Adt(_, _)
+            // FIXME: Handling opaques here is kinda sus. Especially because we
+            // simplify them to SimplifiedType::Placeholder.
+            | ty::Alias(ty::Opaque, _) => {
+                let mut disqualifying_impl = None;
+                self.tcx().for_each_relevant_impl_treating_projections(
+                    goal.predicate.def_id(),
+                    goal.predicate.self_ty(),
+                    TreatProjections::NextSolverLookup,
+                    |impl_def_id| {
+                        disqualifying_impl = Some(impl_def_id);
+                    },
+                );
+                if let Some(def_id) = disqualifying_impl {
+                    debug!(?def_id, ?goal, "disqualified auto-trait implementation");
+                    // No need to actually consider the candidate here,
+                    // since we do that in `consider_impl_candidate`.
+                    return Some(Err(NoSolution));
+                } else {
+                    None
+                }
+            }
+            ty::Error(_) => None,
+        }
+    }
+
+    /// Convenience function for traits that are structural, i.e. that only
+    /// have nested subgoals that only change the self type. Unlike other
+    /// evaluate-like helpers, this does a probe, so it doesn't need to be
+    /// wrapped in one.
+    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>,
+    ) -> 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)))
+                    .collect::<Vec<_>>(),
+            );
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    pub(super) fn compute_trait_goal(
+        &mut self,
+        goal: Goal<'tcx, TraitPredicate<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        let candidates = self.assemble_and_evaluate_candidates(goal);
+        self.merge_candidates(candidates)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
new file mode 100644
index 00000000000..c4344758574
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -0,0 +1,841 @@
+//! Support code for rustdoc and external tools.
+//! You really don't want to be using this unless you need to.
+
+use super::*;
+
+use crate::errors::UnableToConstructConstantValue;
+use crate::infer::region_constraints::{Constraint, RegionConstraintData};
+use crate::traits::project::ProjectAndUnifyResult;
+use rustc_infer::infer::DefineOpaqueTypes;
+use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::ty::{ImplPolarity, Region, RegionVid};
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
+
+use std::collections::hash_map::Entry;
+use std::collections::VecDeque;
+use std::iter;
+
+// FIXME(twk): this is obviously not nice to duplicate like that
+#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
+pub enum RegionTarget<'tcx> {
+    Region(Region<'tcx>),
+    RegionVid(RegionVid),
+}
+
+#[derive(Default, Debug, Clone)]
+pub struct RegionDeps<'tcx> {
+    larger: FxIndexSet<RegionTarget<'tcx>>,
+    smaller: FxIndexSet<RegionTarget<'tcx>>,
+}
+
+pub enum AutoTraitResult<A> {
+    ExplicitImpl,
+    PositiveImpl(A),
+    NegativeImpl,
+}
+
+#[allow(dead_code)]
+impl<A> AutoTraitResult<A> {
+    fn is_auto(&self) -> bool {
+        matches!(self, AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl)
+    }
+}
+
+pub struct AutoTraitInfo<'cx> {
+    pub full_user_env: ty::ParamEnv<'cx>,
+    pub region_data: RegionConstraintData<'cx>,
+    pub vid_to_region: FxHashMap<ty::RegionVid, ty::Region<'cx>>,
+}
+
+pub struct AutoTraitFinder<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> AutoTraitFinder<'tcx> {
+    pub fn new(tcx: TyCtxt<'tcx>) -> Self {
+        AutoTraitFinder { tcx }
+    }
+
+    /// Makes a best effort to determine whether and under which conditions an auto trait is
+    /// implemented for a type. For example, if you have
+    ///
+    /// ```
+    /// struct Foo<T> { data: Box<T> }
+    /// ```
+    ///
+    /// then this might return that `Foo<T>: Send` if `T: Send` (encoded in the AutoTraitResult
+    /// type). The analysis attempts to account for custom impls as well as other complex cases.
+    /// This result is intended for use by rustdoc and other such consumers.
+    ///
+    /// (Note that due to the coinductive nature of Send, the full and correct result is actually
+    /// quite simple to generate. That is, when a type has no custom impl, it is Send iff its field
+    /// types are all Send. So, in our example, we might have that `Foo<T>: Send` if `Box<T>: Send`.
+    /// But this is often not the best way to present to the user.)
+    ///
+    /// Warning: The API should be considered highly unstable, and it may be refactored or removed
+    /// in the future.
+    pub fn find_auto_trait_generics<A>(
+        &self,
+        ty: Ty<'tcx>,
+        orig_env: ty::ParamEnv<'tcx>,
+        trait_did: DefId,
+        mut auto_trait_callback: impl FnMut(AutoTraitInfo<'tcx>) -> A,
+    ) -> AutoTraitResult<A> {
+        let tcx = self.tcx;
+
+        let trait_ref = ty::TraitRef::new(tcx, trait_did, [ty]);
+
+        let infcx = tcx.infer_ctxt().build();
+        let mut selcx = SelectionContext::new(&infcx);
+        for polarity in [true, false] {
+            let result = selcx.select(&Obligation::new(
+                tcx,
+                ObligationCause::dummy(),
+                orig_env,
+                ty::TraitPredicate {
+                    trait_ref,
+                    polarity: if polarity {
+                        ImplPolarity::Positive
+                    } else {
+                        ImplPolarity::Negative
+                    },
+                },
+            ));
+            if let Ok(Some(ImplSource::UserDefined(_))) = result {
+                debug!(
+                    "find_auto_trait_generics({:?}): \
+                 manual impl found, bailing out",
+                    trait_ref
+                );
+                // If an explicit impl exists, it always takes priority over an auto impl
+                return AutoTraitResult::ExplicitImpl;
+            }
+        }
+
+        let infcx = tcx.infer_ctxt().build();
+        let mut fresh_preds = FxHashSet::default();
+
+        // Due to the way projections are handled by SelectionContext, we need to run
+        // evaluate_predicates twice: once on the original param env, and once on the result of
+        // the first evaluate_predicates call.
+        //
+        // The problem is this: most of rustc, including SelectionContext and traits::project,
+        // are designed to work with a concrete usage of a type (e.g., Vec<u8>
+        // fn<T>() { Vec<T> }. This information will generally never change - given
+        // the 'T' in fn<T>() { ... }, we'll never know anything else about 'T'.
+        // If we're unable to prove that 'T' implements a particular trait, we're done -
+        // there's nothing left to do but error out.
+        //
+        // However, synthesizing an auto trait impl works differently. Here, we start out with
+        // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing
+        // with - and progressively discover the conditions we need to fulfill for it to
+        // implement a certain auto trait. This ends up breaking two assumptions made by trait
+        // selection and projection:
+        //
+        // * We can always cache the result of a particular trait selection for the lifetime of
+        // an InfCtxt
+        // * Given a projection bound such as '<T as SomeTrait>::SomeItem = K', if 'T:
+        // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K'
+        //
+        // We fix the first assumption by manually clearing out all of the InferCtxt's caches
+        // in between calls to SelectionContext.select. This allows us to keep all of the
+        // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift
+        // them between calls.
+        //
+        // We fix the second assumption by reprocessing the result of our first call to
+        // evaluate_predicates. Using the example of '<T as SomeTrait>::SomeItem = K', our first
+        // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass,
+        // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing
+        // SelectionContext to return it back to us.
+
+        let Some((new_env, user_env)) =
+            self.evaluate_predicates(&infcx, trait_did, ty, orig_env, orig_env, &mut fresh_preds)
+        else {
+            return AutoTraitResult::NegativeImpl;
+        };
+
+        let (full_env, full_user_env) = self
+            .evaluate_predicates(&infcx, trait_did, ty, new_env, user_env, &mut fresh_preds)
+            .unwrap_or_else(|| {
+                panic!("Failed to fully process: {ty:?} {trait_did:?} {orig_env:?}")
+            });
+
+        debug!(
+            "find_auto_trait_generics({:?}): fulfilling \
+             with {:?}",
+            trait_ref, full_env
+        );
+        infcx.clear_caches();
+
+        // At this point, we already have all of the bounds we need. FulfillmentContext is used
+        // to store all of the necessary region/lifetime bounds in the InferContext, as well as
+        // an additional sanity check.
+        let ocx = ObligationCtxt::new(&infcx);
+        ocx.register_bound(ObligationCause::dummy(), full_env, ty, trait_did);
+        let errors = ocx.select_all_or_error();
+        if !errors.is_empty() {
+            panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}");
+        }
+
+        let outlives_env = OutlivesEnvironment::new(full_env);
+        infcx.process_registered_region_obligations(&outlives_env);
+
+        let region_data =
+            infcx.inner.borrow_mut().unwrap_region_constraints().region_constraint_data().clone();
+
+        let vid_to_region = self.map_vid_to_region(&region_data);
+
+        let info = AutoTraitInfo { full_user_env, region_data, vid_to_region };
+
+        AutoTraitResult::PositiveImpl(auto_trait_callback(info))
+    }
+}
+
+impl<'tcx> AutoTraitFinder<'tcx> {
+    /// The core logic responsible for computing the bounds for our synthesized impl.
+    ///
+    /// To calculate the bounds, we call `SelectionContext.select` in a loop. Like
+    /// `FulfillmentContext`, we recursively select the nested obligations of predicates we
+    /// encounter. However, whenever we encounter an `UnimplementedError` involving a type
+    /// parameter, we add it to our `ParamEnv`. Since our goal is to determine when a particular
+    /// type implements an auto trait, Unimplemented errors tell us what conditions need to be met.
+    ///
+    /// This method ends up working somewhat similarly to `FulfillmentContext`, but with a few key
+    /// differences. `FulfillmentContext` works under the assumption that it's dealing with concrete
+    /// user code. According, it considers all possible ways that a `Predicate` could be met, which
+    /// isn't always what we want for a synthesized impl. For example, given the predicate `T:
+    /// Iterator`, `FulfillmentContext` can end up reporting an Unimplemented error for `T:
+    /// IntoIterator` -- since there's an implementation of `Iterator` where `T: IntoIterator`,
+    /// `FulfillmentContext` will drive `SelectionContext` to consider that impl before giving up.
+    /// If we were to rely on `FulfillmentContext`s decision, we might end up synthesizing an impl
+    /// like this:
+    /// ```ignore (illustrative)
+    /// impl<T> Send for Foo<T> where T: IntoIterator
+    /// ```
+    /// While it might be technically true that Foo implements Send where `T: IntoIterator`,
+    /// the bound is overly restrictive - it's really only necessary that `T: Iterator`.
+    ///
+    /// For this reason, `evaluate_predicates` handles predicates with type variables specially.
+    /// When we encounter an `Unimplemented` error for a bound such as `T: Iterator`, we immediately
+    /// add it to our `ParamEnv`, and add it to our stack for recursive evaluation. When we later
+    /// select it, we'll pick up any nested bounds, without ever inferring that `T: IntoIterator`
+    /// needs to hold.
+    ///
+    /// One additional consideration is supertrait bounds. Normally, a `ParamEnv` is only ever
+    /// constructed once for a given type. As part of the construction process, the `ParamEnv` will
+    /// have any supertrait bounds normalized -- e.g., if we have a type `struct Foo<T: Copy>`, the
+    /// `ParamEnv` will contain `T: Copy` and `T: Clone`, since `Copy: Clone`. When we construct our
+    /// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate`, or
+    /// else `SelectionContext` will choke on the missing predicates. However, this should never
+    /// show up in the final synthesized generics: we don't want our generated docs page to contain
+    /// something like `T: Copy + Clone`, as that's redundant. Therefore, we keep track of a
+    /// separate `user_env`, which only holds the predicates that will actually be displayed to the
+    /// user.
+    fn evaluate_predicates(
+        &self,
+        infcx: &InferCtxt<'tcx>,
+        trait_did: DefId,
+        ty: Ty<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        user_env: ty::ParamEnv<'tcx>,
+        fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
+    ) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> {
+        let tcx = infcx.tcx;
+
+        // Don't try to process any nested obligations involving predicates
+        // that are already in the `ParamEnv` (modulo regions): we already
+        // know that they must hold.
+        for predicate in param_env.caller_bounds() {
+            fresh_preds.insert(self.clean_pred(infcx, predicate.as_predicate()));
+        }
+
+        let mut select = SelectionContext::new(infcx);
+
+        let mut already_visited = FxHashSet::default();
+        let mut predicates = VecDeque::new();
+        predicates.push_back(ty::Binder::dummy(ty::TraitPredicate {
+            trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]),
+
+            // Auto traits are positive
+            polarity: ty::ImplPolarity::Positive,
+        }));
+
+        let computed_preds = param_env.caller_bounds().iter().map(|c| c.as_predicate());
+        let mut user_computed_preds: FxIndexSet<_> =
+            user_env.caller_bounds().iter().map(|c| c.as_predicate()).collect();
+
+        let mut new_env = param_env;
+        let dummy_cause = ObligationCause::dummy();
+
+        while let Some(pred) = predicates.pop_front() {
+            infcx.clear_caches();
+
+            if !already_visited.insert(pred) {
+                continue;
+            }
+
+            // Call `infcx.resolve_vars_if_possible` to see if we can
+            // get rid of any inference variables.
+            let obligation = infcx.resolve_vars_if_possible(Obligation::new(
+                tcx,
+                dummy_cause.clone(),
+                new_env,
+                pred,
+            ));
+            let result = select.poly_select(&obligation);
+
+            match result {
+                Ok(Some(ref impl_source)) => {
+                    // If we see an explicit negative impl (e.g., `impl !Send for MyStruct`),
+                    // we immediately bail out, since it's impossible for us to continue.
+
+                    if let ImplSource::UserDefined(ImplSourceUserDefinedData {
+                        impl_def_id, ..
+                    }) = impl_source
+                    {
+                        // Blame 'tidy' for the weird bracket placement.
+                        if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative {
+                            debug!(
+                                "evaluate_nested_obligations: found explicit negative impl\
+                                        {:?}, bailing out",
+                                impl_def_id
+                            );
+                            return None;
+                        }
+                    }
+
+                    let obligations = impl_source.borrow_nested_obligations().iter().cloned();
+
+                    if !self.evaluate_nested_obligations(
+                        ty,
+                        obligations,
+                        &mut user_computed_preds,
+                        fresh_preds,
+                        &mut predicates,
+                        &mut select,
+                    ) {
+                        return None;
+                    }
+                }
+                Ok(None) => {}
+                Err(SelectionError::Unimplemented) => {
+                    if self.is_param_no_infer(pred.skip_binder().trait_ref.args) {
+                        already_visited.remove(&pred);
+                        self.add_user_pred(&mut user_computed_preds, pred.to_predicate(self.tcx));
+                        predicates.push_back(pred);
+                    } else {
+                        debug!(
+                            "evaluate_nested_obligations: `Unimplemented` found, bailing: \
+                             {:?} {:?} {:?}",
+                            ty,
+                            pred,
+                            pred.skip_binder().trait_ref.args
+                        );
+                        return None;
+                    }
+                }
+                _ => panic!("Unexpected error for '{ty:?}': {result:?}"),
+            };
+
+            let normalized_preds =
+                elaborate(tcx, computed_preds.clone().chain(user_computed_preds.iter().cloned()));
+            new_env = ty::ParamEnv::new(
+                tcx.mk_clauses_from_iter(normalized_preds.filter_map(|p| p.as_clause())),
+                param_env.reveal(),
+            );
+        }
+
+        let final_user_env = ty::ParamEnv::new(
+            tcx.mk_clauses_from_iter(user_computed_preds.into_iter().filter_map(|p| p.as_clause())),
+            user_env.reveal(),
+        );
+        debug!(
+            "evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \
+             '{:?}'",
+            ty, trait_did, new_env, final_user_env
+        );
+
+        Some((new_env, final_user_env))
+    }
+
+    /// This method is designed to work around the following issue:
+    /// When we compute auto trait bounds, we repeatedly call `SelectionContext.select`,
+    /// progressively building a `ParamEnv` based on the results we get.
+    /// However, our usage of `SelectionContext` differs from its normal use within the compiler,
+    /// in that we capture and re-reprocess predicates from `Unimplemented` errors.
+    ///
+    /// This can lead to a corner case when dealing with region parameters.
+    /// During our selection loop in `evaluate_predicates`, we might end up with
+    /// two trait predicates that differ only in their region parameters:
+    /// one containing a HRTB lifetime parameter, and one containing a 'normal'
+    /// lifetime parameter. For example:
+    /// ```ignore (illustrative)
+    /// T as MyTrait<'a>
+    /// T as MyTrait<'static>
+    /// ```
+    /// If we put both of these predicates in our computed `ParamEnv`, we'll
+    /// confuse `SelectionContext`, since it will (correctly) view both as being applicable.
+    ///
+    /// To solve this, we pick the 'more strict' lifetime bound -- i.e., the HRTB
+    /// Our end goal is to generate a user-visible description of the conditions
+    /// under which a type implements an auto trait. A trait predicate involving
+    /// a HRTB means that the type needs to work with any choice of lifetime,
+    /// not just one specific lifetime (e.g., `'static`).
+    fn add_user_pred(
+        &self,
+        user_computed_preds: &mut FxIndexSet<ty::Predicate<'tcx>>,
+        new_pred: ty::Predicate<'tcx>,
+    ) {
+        let mut should_add_new = true;
+        user_computed_preds.retain(|&old_pred| {
+            if let (
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(new_trait)),
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(old_trait)),
+            ) = (new_pred.kind().skip_binder(), old_pred.kind().skip_binder())
+            {
+                if new_trait.def_id() == old_trait.def_id() {
+                    let new_args = new_trait.trait_ref.args;
+                    let old_args = old_trait.trait_ref.args;
+
+                    if !new_args.types().eq(old_args.types()) {
+                        // We can't compare lifetimes if the types are different,
+                        // so skip checking `old_pred`.
+                        return true;
+                    }
+
+                    for (new_region, old_region) in
+                        iter::zip(new_args.regions(), old_args.regions())
+                    {
+                        match (*new_region, *old_region) {
+                            // If both predicates have an `ReBound` (a HRTB) in the
+                            // same spot, we do nothing.
+                            (ty::ReBound(_, _), ty::ReBound(_, _)) => {}
+
+                            (ty::ReBound(_, _), _) | (_, ty::ReVar(_)) => {
+                                // One of these is true:
+                                // The new predicate has a HRTB in a spot where the old
+                                // predicate does not (if they both had a HRTB, the previous
+                                // match arm would have executed). A HRBT is a 'stricter'
+                                // bound than anything else, so we want to keep the newer
+                                // predicate (with the HRBT) in place of the old predicate.
+                                //
+                                // OR
+                                //
+                                // The old predicate has a region variable where the new
+                                // predicate has some other kind of region. An region
+                                // variable isn't something we can actually display to a user,
+                                // so we choose their new predicate (which doesn't have a region
+                                // variable).
+                                //
+                                // In both cases, we want to remove the old predicate,
+                                // from `user_computed_preds`, and replace it with the new
+                                // one. Having both the old and the new
+                                // predicate in a `ParamEnv` would confuse `SelectionContext`.
+                                //
+                                // We're currently in the predicate passed to 'retain',
+                                // so we return `false` to remove the old predicate from
+                                // `user_computed_preds`.
+                                return false;
+                            }
+                            (_, ty::ReBound(_, _)) | (ty::ReVar(_), _) => {
+                                // This is the opposite situation as the previous arm.
+                                // One of these is true:
+                                //
+                                // The old predicate has a HRTB lifetime in a place where the
+                                // new predicate does not.
+                                //
+                                // OR
+                                //
+                                // The new predicate has a region variable where the old
+                                // predicate has some other type of region.
+                                //
+                                // We want to leave the old
+                                // predicate in `user_computed_preds`, and skip adding
+                                // new_pred to `user_computed_params`.
+                                should_add_new = false
+                            }
+                            _ => {}
+                        }
+                    }
+                }
+            }
+            true
+        });
+
+        if should_add_new {
+            user_computed_preds.insert(new_pred);
+        }
+    }
+
+    /// This is very similar to `handle_lifetimes`. However, instead of matching `ty::Region`s
+    /// to each other, we match `ty::RegionVid`s to `ty::Region`s.
+    fn map_vid_to_region<'cx>(
+        &self,
+        regions: &RegionConstraintData<'cx>,
+    ) -> FxHashMap<ty::RegionVid, ty::Region<'cx>> {
+        let mut vid_map: FxHashMap<RegionTarget<'cx>, RegionDeps<'cx>> = FxHashMap::default();
+        let mut finished_map = FxHashMap::default();
+
+        for (constraint, _) in &regions.constraints {
+            match constraint {
+                &Constraint::VarSubVar(r1, r2) => {
+                    {
+                        let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default();
+                        deps1.larger.insert(RegionTarget::RegionVid(r2));
+                    }
+
+                    let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default();
+                    deps2.smaller.insert(RegionTarget::RegionVid(r1));
+                }
+                &Constraint::RegSubVar(region, vid) => {
+                    {
+                        let deps1 = vid_map.entry(RegionTarget::Region(region)).or_default();
+                        deps1.larger.insert(RegionTarget::RegionVid(vid));
+                    }
+
+                    let deps2 = vid_map.entry(RegionTarget::RegionVid(vid)).or_default();
+                    deps2.smaller.insert(RegionTarget::Region(region));
+                }
+                &Constraint::VarSubReg(vid, region) => {
+                    finished_map.insert(vid, region);
+                }
+                &Constraint::RegSubReg(r1, r2) => {
+                    {
+                        let deps1 = vid_map.entry(RegionTarget::Region(r1)).or_default();
+                        deps1.larger.insert(RegionTarget::Region(r2));
+                    }
+
+                    let deps2 = vid_map.entry(RegionTarget::Region(r2)).or_default();
+                    deps2.smaller.insert(RegionTarget::Region(r1));
+                }
+            }
+        }
+
+        while !vid_map.is_empty() {
+            let target = *vid_map.keys().next().expect("Keys somehow empty");
+            let deps = vid_map.remove(&target).expect("Entry somehow missing");
+
+            for smaller in deps.smaller.iter() {
+                for larger in deps.larger.iter() {
+                    match (smaller, larger) {
+                        (&RegionTarget::Region(_), &RegionTarget::Region(_)) => {
+                            if let Entry::Occupied(v) = vid_map.entry(*smaller) {
+                                let smaller_deps = v.into_mut();
+                                smaller_deps.larger.insert(*larger);
+                                smaller_deps.larger.remove(&target);
+                            }
+
+                            if let Entry::Occupied(v) = vid_map.entry(*larger) {
+                                let larger_deps = v.into_mut();
+                                larger_deps.smaller.insert(*smaller);
+                                larger_deps.smaller.remove(&target);
+                            }
+                        }
+                        (&RegionTarget::RegionVid(v1), &RegionTarget::Region(r1)) => {
+                            finished_map.insert(v1, r1);
+                        }
+                        (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
+                            // Do nothing; we don't care about regions that are smaller than vids.
+                        }
+                        (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
+                            if let Entry::Occupied(v) = vid_map.entry(*smaller) {
+                                let smaller_deps = v.into_mut();
+                                smaller_deps.larger.insert(*larger);
+                                smaller_deps.larger.remove(&target);
+                            }
+
+                            if let Entry::Occupied(v) = vid_map.entry(*larger) {
+                                let larger_deps = v.into_mut();
+                                larger_deps.smaller.insert(*smaller);
+                                larger_deps.smaller.remove(&target);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        finished_map
+    }
+
+    fn is_param_no_infer(&self, args: GenericArgsRef<'_>) -> bool {
+        self.is_of_param(args.type_at(0)) && !args.types().any(|t| t.has_infer_types())
+    }
+
+    pub fn is_of_param(&self, ty: Ty<'_>) -> bool {
+        match ty.kind() {
+            ty::Param(_) => true,
+            ty::Alias(ty::Projection, p) => self.is_of_param(p.self_ty()),
+            _ => false,
+        }
+    }
+
+    fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool {
+        if let Some(ty) = p.term().skip_binder().ty() {
+            matches!(ty.kind(), ty::Alias(ty::Projection, proj) if proj == &p.skip_binder().projection_ty)
+        } else {
+            false
+        }
+    }
+
+    fn evaluate_nested_obligations(
+        &self,
+        ty: Ty<'_>,
+        nested: impl Iterator<Item = PredicateObligation<'tcx>>,
+        computed_preds: &mut FxIndexSet<ty::Predicate<'tcx>>,
+        fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
+        predicates: &mut VecDeque<ty::PolyTraitPredicate<'tcx>>,
+        selcx: &mut SelectionContext<'_, 'tcx>,
+    ) -> bool {
+        let dummy_cause = ObligationCause::dummy();
+
+        for obligation in nested {
+            let is_new_pred =
+                fresh_preds.insert(self.clean_pred(selcx.infcx, obligation.predicate));
+
+            // Resolve any inference variables that we can, to help selection succeed
+            let predicate = selcx.infcx.resolve_vars_if_possible(obligation.predicate);
+
+            // We only add a predicate as a user-displayable bound if
+            // it involves a generic parameter, and doesn't contain
+            // any inference variables.
+            //
+            // Displaying a bound involving a concrete type (instead of a generic
+            // parameter) would be pointless, since it's always true
+            // (e.g. u8: Copy)
+            // Displaying an inference variable is impossible, since they're
+            // an internal compiler detail without a defined visual representation
+            //
+            // We check this by calling is_of_param on the relevant types
+            // from the various possible predicates
+
+            let bound_predicate = predicate.kind();
+            match bound_predicate.skip_binder() {
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => {
+                    // Add this to `predicates` so that we end up calling `select`
+                    // with it. If this predicate ends up being unimplemented,
+                    // then `evaluate_predicates` will handle adding it the `ParamEnv`
+                    // if possible.
+                    predicates.push_back(bound_predicate.rebind(p));
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
+                    let p = bound_predicate.rebind(p);
+                    debug!(
+                        "evaluate_nested_obligations: examining projection predicate {:?}",
+                        predicate
+                    );
+
+                    // As described above, we only want to display
+                    // bounds which include a generic parameter but don't include
+                    // an inference variable.
+                    // Additionally, we check if we've seen this predicate before,
+                    // to avoid rendering duplicate bounds to the user.
+                    if self.is_param_no_infer(p.skip_binder().projection_ty.args)
+                        && !p.term().skip_binder().has_infer_types()
+                        && is_new_pred
+                    {
+                        debug!(
+                            "evaluate_nested_obligations: adding projection predicate \
+                            to computed_preds: {:?}",
+                            predicate
+                        );
+
+                        // Under unusual circumstances, we can end up with a self-referential
+                        // projection predicate. For example:
+                        // <T as MyType>::Value == <T as MyType>::Value
+                        // Not only is displaying this to the user pointless,
+                        // having it in the ParamEnv will cause an issue if we try to call
+                        // poly_project_and_unify_type on the predicate, since this kind of
+                        // predicate will normally never end up in a ParamEnv.
+                        //
+                        // For these reasons, we ignore these weird predicates,
+                        // ensuring that we're able to properly synthesize an auto trait impl
+                        if self.is_self_referential_projection(p) {
+                            debug!(
+                                "evaluate_nested_obligations: encountered a projection
+                                 predicate equating a type with itself! Skipping"
+                            );
+                        } else {
+                            self.add_user_pred(computed_preds, predicate);
+                        }
+                    }
+
+                    // There are three possible cases when we project a predicate:
+                    //
+                    // 1. We encounter an error. This means that it's impossible for
+                    // our current type to implement the auto trait - there's bound
+                    // that we could add to our ParamEnv that would 'fix' this kind
+                    // of error, as it's not caused by an unimplemented type.
+                    //
+                    // 2. We successfully project the predicate (Ok(Some(_))), generating
+                    //  some subobligations. We then process these subobligations
+                    //  like any other generated sub-obligations.
+                    //
+                    // 3. We receive an 'ambiguous' result (Ok(None))
+                    // If we were actually trying to compile a crate,
+                    // we would need to re-process this obligation later.
+                    // However, all we care about is finding out what bounds
+                    // are needed for our type to implement a particular auto trait.
+                    // We've already added this obligation to our computed ParamEnv
+                    // above (if it was necessary). Therefore, we don't need
+                    // to do any further processing of the obligation.
+                    //
+                    // Note that we *must* try to project *all* projection predicates
+                    // we encounter, even ones without inference variable.
+                    // This ensures that we detect any projection errors,
+                    // which indicate that our type can *never* implement the given
+                    // auto trait. In that case, we will generate an explicit negative
+                    // impl (e.g. 'impl !Send for MyType'). However, we don't
+                    // try to process any of the generated subobligations -
+                    // they contain no new information, since we already know
+                    // that our type implements the projected-through trait,
+                    // and can lead to weird region issues.
+                    //
+                    // Normally, we'll generate a negative impl as a result of encountering
+                    // a type with an explicit negative impl of an auto trait
+                    // (for example, raw pointers have !Send and !Sync impls)
+                    // However, through some **interesting** manipulations of the type
+                    // system, it's actually possible to write a type that never
+                    // implements an auto trait due to a projection error, not a normal
+                    // negative impl error. To properly handle this case, we need
+                    // to ensure that we catch any potential projection errors,
+                    // and turn them into an explicit negative impl for our type.
+                    debug!("Projecting and unifying projection predicate {:?}", predicate);
+
+                    match project::poly_project_and_unify_type(selcx, &obligation.with(self.tcx, p))
+                    {
+                        ProjectAndUnifyResult::MismatchedProjectionTypes(e) => {
+                            debug!(
+                                "evaluate_nested_obligations: Unable to unify predicate \
+                                 '{:?}' '{:?}', bailing out",
+                                ty, e
+                            );
+                            return false;
+                        }
+                        ProjectAndUnifyResult::Recursive => {
+                            debug!("evaluate_nested_obligations: recursive projection predicate");
+                            return false;
+                        }
+                        ProjectAndUnifyResult::Holds(v) => {
+                            // We only care about sub-obligations
+                            // when we started out trying to unify
+                            // some inference variables. See the comment above
+                            // for more information
+                            if p.term().skip_binder().has_infer_types() {
+                                if !self.evaluate_nested_obligations(
+                                    ty,
+                                    v.into_iter(),
+                                    computed_preds,
+                                    fresh_preds,
+                                    predicates,
+                                    selcx,
+                                ) {
+                                    return false;
+                                }
+                            }
+                        }
+                        ProjectAndUnifyResult::FailedNormalization => {
+                            // It's ok not to make progress when have no inference variables -
+                            // in that case, we were only performing unification to check if an
+                            // error occurred (which would indicate that it's impossible for our
+                            // type to implement the auto trait).
+                            // However, we should always make progress (either by generating
+                            // subobligations or getting an error) when we started off with
+                            // inference variables
+                            if p.term().skip_binder().has_infer_types() {
+                                panic!("Unexpected result when selecting {ty:?} {obligation:?}")
+                            }
+                        }
+                    }
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(binder)) => {
+                    let binder = bound_predicate.rebind(binder);
+                    selcx.infcx.region_outlives_predicate(&dummy_cause, binder)
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(binder)) => {
+                    let binder = bound_predicate.rebind(binder);
+                    match (
+                        binder.no_bound_vars(),
+                        binder.map_bound_ref(|pred| pred.0).no_bound_vars(),
+                    ) {
+                        (None, Some(t_a)) => {
+                            selcx.infcx.register_region_obligation_with_cause(
+                                t_a,
+                                selcx.infcx.tcx.lifetimes.re_static,
+                                &dummy_cause,
+                            );
+                        }
+                        (Some(ty::OutlivesPredicate(t_a, r_b)), _) => {
+                            selcx.infcx.register_region_obligation_with_cause(
+                                t_a,
+                                r_b,
+                                &dummy_cause,
+                            );
+                        }
+                        _ => {}
+                    };
+                }
+                ty::PredicateKind::ConstEquate(c1, c2) => {
+                    let evaluate = |c: ty::Const<'tcx>| {
+                        if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() {
+                            match selcx.infcx.const_eval_resolve(
+                                obligation.param_env,
+                                unevaluated,
+                                Some(obligation.cause.span),
+                            ) {
+                                Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())),
+                                Ok(None) => {
+                                    let tcx = self.tcx;
+                                    let reported =
+                                        tcx.dcx().emit_err(UnableToConstructConstantValue {
+                                            span: tcx.def_span(unevaluated.def),
+                                            unevaluated: unevaluated,
+                                        });
+                                    Err(ErrorHandled::Reported(reported.into(), tcx.def_span(unevaluated.def)))
+                                }
+                                Err(err) => Err(err),
+                            }
+                        } else {
+                            Ok(c)
+                        }
+                    };
+
+                    match (evaluate(c1), evaluate(c2)) {
+                        (Ok(c1), Ok(c2)) => {
+                            match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(DefineOpaqueTypes::No,c1, c2)
+                            {
+                                Ok(_) => (),
+                                Err(_) => return false,
+                            }
+                        }
+                        _ => return false,
+                    }
+                }
+
+                // There's not really much we can do with these predicates -
+                // we start out with a `ParamEnv` with no inference variables,
+                // and these don't correspond to adding any new bounds to
+                // the `ParamEnv`.
+                ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
+                | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
+                | ty::PredicateKind::NormalizesTo(..)
+                | ty::PredicateKind::AliasRelate(..)
+                | ty::PredicateKind::ObjectSafe(..)
+                | ty::PredicateKind::Subtype(..)
+                // FIXME(generic_const_exprs): you can absolutely add this as a where clauses
+                | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
+                | ty::PredicateKind::Coerce(..) => {}
+                ty::PredicateKind::Ambiguous => return false,
+            };
+        }
+        true
+    }
+
+    pub fn clean_pred(
+        &self,
+        infcx: &InferCtxt<'tcx>,
+        p: ty::Predicate<'tcx>,
+    ) -> ty::Predicate<'tcx> {
+        infcx.freshen(p)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
new file mode 100644
index 00000000000..533fe32f70d
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -0,0 +1,1148 @@
+//! See Rustc Dev Guide chapters on [trait-resolution] and [trait-specialization] for more info on
+//! how this works.
+//!
+//! [trait-resolution]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
+//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
+
+use crate::infer::outlives::env::OutlivesEnvironment;
+use crate::infer::InferOk;
+use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
+use crate::solve::{deeply_normalize_for_diagnostics, inspect};
+use crate::traits::engine::TraitEngineExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs};
+use crate::traits::structural_normalize::StructurallyNormalizeExt;
+use crate::traits::NormalizeExt;
+use crate::traits::SkipLeakCheck;
+use crate::traits::{
+    Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
+    SelectionContext,
+};
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::Diagnostic;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
+use rustc_infer::traits::{util, TraitEngine};
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
+use rustc_middle::traits::specialization_graph::OverlapMode;
+use rustc_middle::traits::DefiningAnchor;
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
+use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE;
+use rustc_span::symbol::sym;
+use rustc_span::DUMMY_SP;
+use std::fmt::Debug;
+use std::ops::ControlFlow;
+
+/// Whether we do the orphan check relative to this crate or
+/// to some remote crate.
+#[derive(Copy, Clone, Debug)]
+enum InCrate {
+    Local,
+    Remote,
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum Conflict {
+    Upstream,
+    Downstream,
+}
+
+pub struct OverlapResult<'tcx> {
+    pub impl_header: ty::ImplHeader<'tcx>,
+    pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
+
+    /// `true` if the overlap might've been permitted before the shift
+    /// to universes.
+    pub involves_placeholder: bool,
+}
+
+pub fn add_placeholder_note(err: &mut Diagnostic) {
+    err.note(
+        "this behavior recently changed as a result of a bug fix; \
+         see rust-lang/rust#56105 for details",
+    );
+}
+
+#[derive(Debug, Clone, Copy)]
+enum TrackAmbiguityCauses {
+    Yes,
+    No,
+}
+
+impl TrackAmbiguityCauses {
+    fn is_yes(self) -> bool {
+        match self {
+            TrackAmbiguityCauses::Yes => true,
+            TrackAmbiguityCauses::No => false,
+        }
+    }
+}
+
+/// If there are types that satisfy both impls, returns `Some`
+/// with a suitably-freshened `ImplHeader` with those types
+/// substituted. Otherwise, returns `None`.
+#[instrument(skip(tcx, skip_leak_check), level = "debug")]
+pub fn overlapping_impls(
+    tcx: TyCtxt<'_>,
+    impl1_def_id: DefId,
+    impl2_def_id: DefId,
+    skip_leak_check: SkipLeakCheck,
+    overlap_mode: OverlapMode,
+) -> Option<OverlapResult<'_>> {
+    // Before doing expensive operations like entering an inference context, do
+    // a quick check via fast_reject to tell if the impl headers could possibly
+    // unify.
+    let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey };
+    let impl1_ref = tcx.impl_trait_ref(impl1_def_id);
+    let impl2_ref = tcx.impl_trait_ref(impl2_def_id);
+    let may_overlap = match (impl1_ref, impl2_ref) {
+        (Some(a), Some(b)) => drcx.args_may_unify(a.skip_binder().args, b.skip_binder().args),
+        (None, None) => {
+            let self_ty1 = tcx.type_of(impl1_def_id).skip_binder();
+            let self_ty2 = tcx.type_of(impl2_def_id).skip_binder();
+            drcx.types_may_unify(self_ty1, self_ty2)
+        }
+        _ => bug!("unexpected impls: {impl1_def_id:?} {impl2_def_id:?}"),
+    };
+
+    if !may_overlap {
+        // Some types involved are definitely different, so the impls couldn't possibly overlap.
+        debug!("overlapping_impls: fast_reject early-exit");
+        return None;
+    }
+
+    let _overlap_with_bad_diagnostics = overlap(
+        tcx,
+        TrackAmbiguityCauses::No,
+        skip_leak_check,
+        impl1_def_id,
+        impl2_def_id,
+        overlap_mode,
+    )?;
+
+    // In the case where we detect an error, run the check again, but
+    // this time tracking intercrate ambiguity causes for better
+    // diagnostics. (These take time and can lead to false errors.)
+    let overlap = overlap(
+        tcx,
+        TrackAmbiguityCauses::Yes,
+        skip_leak_check,
+        impl1_def_id,
+        impl2_def_id,
+        overlap_mode,
+    )
+    .unwrap();
+    Some(overlap)
+}
+
+fn fresh_impl_header<'tcx>(infcx: &InferCtxt<'tcx>, impl_def_id: DefId) -> ty::ImplHeader<'tcx> {
+    let tcx = infcx.tcx;
+    let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
+
+    ty::ImplHeader {
+        impl_def_id,
+        impl_args,
+        self_ty: tcx.type_of(impl_def_id).instantiate(tcx, impl_args),
+        trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.instantiate(tcx, impl_args)),
+        predicates: tcx
+            .predicates_of(impl_def_id)
+            .instantiate(tcx, impl_args)
+            .iter()
+            .map(|(c, _)| c.as_predicate())
+            .collect(),
+    }
+}
+
+fn fresh_impl_header_normalized<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    impl_def_id: DefId,
+) -> ty::ImplHeader<'tcx> {
+    let header = fresh_impl_header(infcx, impl_def_id);
+
+    let InferOk { value: mut header, obligations } =
+        infcx.at(&ObligationCause::dummy(), param_env).normalize(header);
+
+    header.predicates.extend(obligations.into_iter().map(|o| o.predicate));
+    header
+}
+
+/// Can both impl `a` and impl `b` be satisfied by a common type (including
+/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
+#[instrument(level = "debug", skip(tcx))]
+fn overlap<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    track_ambiguity_causes: TrackAmbiguityCauses,
+    skip_leak_check: SkipLeakCheck,
+    impl1_def_id: DefId,
+    impl2_def_id: DefId,
+    overlap_mode: OverlapMode,
+) -> Option<OverlapResult<'tcx>> {
+    if overlap_mode.use_negative_impl() {
+        if impl_intersection_has_negative_obligation(tcx, impl1_def_id, impl2_def_id)
+            || impl_intersection_has_negative_obligation(tcx, impl2_def_id, impl1_def_id)
+        {
+            return None;
+        }
+    }
+
+    let infcx = tcx
+        .infer_ctxt()
+        .with_opaque_type_inference(DefiningAnchor::Bubble)
+        .skip_leak_check(skip_leak_check.is_yes())
+        .intercrate(true)
+        .with_next_trait_solver(tcx.next_trait_solver_in_coherence())
+        .build();
+    let selcx = &mut SelectionContext::new(&infcx);
+    if track_ambiguity_causes.is_yes() {
+        selcx.enable_tracking_intercrate_ambiguity_causes();
+    }
+
+    // For the purposes of this check, we don't bring any placeholder
+    // types into scope; instead, we replace the generic types with
+    // fresh type variables, and hence we do our evaluations in an
+    // empty environment.
+    let param_env = ty::ParamEnv::empty();
+
+    let impl1_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl1_def_id);
+    let impl2_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl2_def_id);
+
+    // Equate the headers to find their intersection (the general type, with infer vars,
+    // that may apply both impls).
+    let mut obligations =
+        equate_impl_headers(selcx.infcx, param_env, &impl1_header, &impl2_header)?;
+    debug!("overlap: unification check succeeded");
+
+    obligations.extend(
+        [&impl1_header.predicates, &impl2_header.predicates].into_iter().flatten().map(
+            |&predicate| Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate),
+        ),
+    );
+
+    if overlap_mode.use_implicit_negative() {
+        for mode in [TreatInductiveCycleAs::Ambig, TreatInductiveCycleAs::Recur] {
+            if let Some(failing_obligation) = selcx.with_treat_inductive_cycle_as(mode, |selcx| {
+                impl_intersection_has_impossible_obligation(selcx, &obligations)
+            }) {
+                if matches!(mode, TreatInductiveCycleAs::Recur) {
+                    let first_local_impl = impl1_header
+                        .impl_def_id
+                        .as_local()
+                        .or(impl2_header.impl_def_id.as_local())
+                        .expect("expected one of the impls to be local");
+                    infcx.tcx.struct_span_lint_hir(
+                        COINDUCTIVE_OVERLAP_IN_COHERENCE,
+                        infcx.tcx.local_def_id_to_hir_id(first_local_impl),
+                        infcx.tcx.def_span(first_local_impl),
+                        format!(
+                            "implementations {} will conflict in the future",
+                            match impl1_header.trait_ref {
+                                Some(trait_ref) => {
+                                    let trait_ref = infcx.resolve_vars_if_possible(trait_ref);
+                                    format!(
+                                        "of `{}` for `{}`",
+                                        trait_ref.print_trait_sugared(),
+                                        trait_ref.self_ty()
+                                    )
+                                }
+                                None => format!(
+                                    "for `{}`",
+                                    infcx.resolve_vars_if_possible(impl1_header.self_ty)
+                                ),
+                            },
+                        ),
+                        |lint| {
+                            lint.note(
+                                "impls that are not considered to overlap may be considered to \
+                                overlap in the future",
+                            )
+                            .span_label(
+                                infcx.tcx.def_span(impl1_header.impl_def_id),
+                                "the first impl is here",
+                            )
+                            .span_label(
+                                infcx.tcx.def_span(impl2_header.impl_def_id),
+                                "the second impl is here",
+                            );
+                            lint.note(format!(
+                                "`{}` may be considered to hold in future releases, \
+                                    causing the impls to overlap",
+                                infcx.resolve_vars_if_possible(failing_obligation.predicate)
+                            ));
+                        },
+                    );
+                }
+
+                return None;
+            }
+        }
+    }
+
+    // We toggle the `leak_check` by using `skip_leak_check` when constructing the
+    // inference context, so this may be a noop.
+    if infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() {
+        debug!("overlap: leak check failed");
+        return None;
+    }
+
+    let intercrate_ambiguity_causes = if !overlap_mode.use_implicit_negative() {
+        Default::default()
+    } else if infcx.next_trait_solver() {
+        compute_intercrate_ambiguity_causes(&infcx, &obligations)
+    } else {
+        selcx.take_intercrate_ambiguity_causes()
+    };
+
+    debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
+    let involves_placeholder = infcx
+        .inner
+        .borrow_mut()
+        .unwrap_region_constraints()
+        .data()
+        .constraints
+        .iter()
+        .any(|c| c.0.involves_placeholders());
+
+    let mut impl_header = infcx.resolve_vars_if_possible(impl1_header);
+
+    // Deeply normalize the impl header for diagnostics, ignoring any errors if this fails.
+    if infcx.next_trait_solver() {
+        impl_header = deeply_normalize_for_diagnostics(&infcx, param_env, impl_header);
+    }
+
+    Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
+}
+
+#[instrument(level = "debug", skip(infcx), ret)]
+fn equate_impl_headers<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    impl1: &ty::ImplHeader<'tcx>,
+    impl2: &ty::ImplHeader<'tcx>,
+) -> Option<PredicateObligations<'tcx>> {
+    let result =
+        match (impl1.trait_ref, impl2.trait_ref) {
+            (Some(impl1_ref), Some(impl2_ref)) => infcx
+                .at(&ObligationCause::dummy(), param_env)
+                .eq(DefineOpaqueTypes::Yes, impl1_ref, impl2_ref),
+            (None, None) => infcx.at(&ObligationCause::dummy(), param_env).eq(
+                DefineOpaqueTypes::Yes,
+                impl1.self_ty,
+                impl2.self_ty,
+            ),
+            _ => bug!("equate_impl_headers given mismatched impl kinds"),
+        };
+
+    result.map(|infer_ok| infer_ok.obligations).ok()
+}
+
+/// Check if both impls can be satisfied by a common type by considering whether
+/// any of either impl's obligations is not known to hold.
+///
+/// For example, given these two impls:
+///     `impl From<MyLocalType> for Box<dyn Error>` (in my crate)
+///     `impl<E> From<E> for Box<dyn Error> where E: Error` (in libstd)
+///
+/// After replacing both impl headers with inference vars (which happens before
+/// this function is called), we get:
+///     `Box<dyn Error>: From<MyLocalType>`
+///     `Box<dyn Error>: From<?E>`
+///
+/// This gives us `?E = MyLocalType`. We then certainly know that `MyLocalType: Error`
+/// never holds in intercrate mode since a local impl does not exist, and a
+/// downstream impl cannot be added -- therefore can consider the intersection
+/// of the two impls above to be empty.
+///
+/// Importantly, this works even if there isn't a `impl !Error for MyLocalType`.
+fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligations: &'a [PredicateObligation<'tcx>],
+) -> Option<&'a PredicateObligation<'tcx>> {
+    let infcx = selcx.infcx;
+
+    obligations.iter().find(|obligation| {
+        let evaluation_result = if infcx.next_trait_solver() {
+            infcx.evaluate_obligation(obligation)
+        } else {
+            // We use `evaluate_root_obligation` to correctly track intercrate
+            // ambiguity clauses. We cannot use this in the new solver.
+            selcx.evaluate_root_obligation(obligation)
+        };
+
+        match evaluation_result {
+            Ok(result) => !result.may_apply(),
+            // If overflow occurs, we need to conservatively treat the goal as possibly holding,
+            // since there can be instantiations of this goal that don't overflow and result in
+            // success. This isn't much of a problem in the old solver, since we treat overflow
+            // fatally (this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>),
+            // but in the new solver, this is very important for correctness, since overflow
+            // *must* be treated as ambiguity for completeness.
+            Err(_overflow) => false,
+        }
+    })
+}
+
+/// Check if both impls can be satisfied by a common type by considering whether
+/// any of first impl's obligations is known not to hold *via a negative predicate*.
+///
+/// For example, given these two impls:
+///     `struct MyCustomBox<T: ?Sized>(Box<T>);`
+///     `impl From<&str> for MyCustomBox<dyn Error>` (in my crate)
+///     `impl<E> From<E> for MyCustomBox<dyn Error> where E: Error` (in my crate)
+///
+/// After replacing the second impl's header with inference vars, we get:
+///     `MyCustomBox<dyn Error>: From<&str>`
+///     `MyCustomBox<dyn Error>: From<?E>`
+///
+/// This gives us `?E = &str`. We then try to prove the first impl's predicates
+/// after negating, giving us `&str: !Error`. This is a negative impl provided by
+/// libstd, and therefore we can guarantee for certain that libstd will never add
+/// a positive impl for `&str: Error` (without it being a breaking change).
+fn impl_intersection_has_negative_obligation(
+    tcx: TyCtxt<'_>,
+    impl1_def_id: DefId,
+    impl2_def_id: DefId,
+) -> bool {
+    debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
+
+    // N.B. We need to unify impl headers *with* intercrate mode, even if proving negative predicates
+    // do not need intercrate mode enabled.
+    let ref infcx = tcx.infer_ctxt().intercrate(true).with_next_trait_solver(true).build();
+    let root_universe = infcx.universe();
+    assert_eq!(root_universe, ty::UniverseIndex::ROOT);
+
+    let impl1_header = fresh_impl_header(infcx, impl1_def_id);
+    let param_env =
+        ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header.impl_args);
+
+    let impl2_header = fresh_impl_header(infcx, impl2_def_id);
+
+    // Equate the headers to find their intersection (the general type, with infer vars,
+    // that may apply both impls).
+    let Some(equate_obligations) =
+        equate_impl_headers(infcx, param_env, &impl1_header, &impl2_header)
+    else {
+        return false;
+    };
+
+    // FIXME(with_negative_coherence): the infcx has constraints from equating
+    // the impl headers. We should use these constraints as assumptions, not as
+    // requirements, when proving the negated where clauses below.
+    drop(equate_obligations);
+    drop(infcx.take_registered_region_obligations());
+    drop(infcx.take_and_reset_region_constraints());
+
+    plug_infer_with_placeholders(
+        infcx,
+        root_universe,
+        (impl1_header.impl_args, impl2_header.impl_args),
+    );
+    let param_env = infcx.resolve_vars_if_possible(param_env);
+
+    util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args))
+        .any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env))
+}
+
+fn plug_infer_with_placeholders<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    universe: ty::UniverseIndex,
+    value: impl TypeVisitable<TyCtxt<'tcx>>,
+) {
+    struct PlugInferWithPlaceholder<'a, 'tcx> {
+        infcx: &'a InferCtxt<'tcx>,
+        universe: ty::UniverseIndex,
+        var: ty::BoundVar,
+    }
+
+    impl<'tcx> PlugInferWithPlaceholder<'_, 'tcx> {
+        fn next_var(&mut self) -> ty::BoundVar {
+            let var = self.var;
+            self.var = self.var + 1;
+            var
+        }
+    }
+
+    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for PlugInferWithPlaceholder<'_, 'tcx> {
+        fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            let ty = self.infcx.shallow_resolve(ty);
+            if ty.is_ty_var() {
+                let Ok(InferOk { value: (), obligations }) =
+                    self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq(
+                        DefineOpaqueTypes::No,
+                        ty,
+                        Ty::new_placeholder(
+                            self.infcx.tcx,
+                            ty::Placeholder {
+                                universe: self.universe,
+                                bound: ty::BoundTy {
+                                    var: self.next_var(),
+                                    kind: ty::BoundTyKind::Anon,
+                                },
+                            },
+                        ),
+                    )
+                else {
+                    bug!("we always expect to be able to plug an infer var with placeholder")
+                };
+                assert_eq!(obligations, &[]);
+                ControlFlow::Continue(())
+            } else {
+                ty.super_visit_with(self)
+            }
+        }
+
+        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+            let ct = self.infcx.shallow_resolve(ct);
+            if ct.is_ct_infer() {
+                let Ok(InferOk { value: (), obligations }) =
+                    self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq(
+                        DefineOpaqueTypes::No,
+                        ct,
+                        ty::Const::new_placeholder(
+                            self.infcx.tcx,
+                            ty::Placeholder { universe: self.universe, bound: self.next_var() },
+                            ct.ty(),
+                        ),
+                    )
+                else {
+                    bug!("we always expect to be able to plug an infer var with placeholder")
+                };
+                assert_eq!(obligations, &[]);
+                ControlFlow::Continue(())
+            } else {
+                ct.super_visit_with(self)
+            }
+        }
+
+        fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+            if let ty::ReVar(vid) = *r {
+                let r = self
+                    .infcx
+                    .inner
+                    .borrow_mut()
+                    .unwrap_region_constraints()
+                    .opportunistic_resolve_var(self.infcx.tcx, vid);
+                if r.is_var() {
+                    let Ok(InferOk { value: (), obligations }) =
+                        self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq(
+                            DefineOpaqueTypes::No,
+                            r,
+                            ty::Region::new_placeholder(
+                                self.infcx.tcx,
+                                ty::Placeholder {
+                                    universe: self.universe,
+                                    bound: ty::BoundRegion {
+                                        var: self.next_var(),
+                                        kind: ty::BoundRegionKind::BrAnon,
+                                    },
+                                },
+                            ),
+                        )
+                    else {
+                        bug!("we always expect to be able to plug an infer var with placeholder")
+                    };
+                    assert_eq!(obligations, &[]);
+                }
+            }
+            ControlFlow::Continue(())
+        }
+    }
+
+    value.visit_with(&mut PlugInferWithPlaceholder {
+        infcx,
+        universe,
+        var: ty::BoundVar::from_u32(0),
+    });
+}
+
+fn try_prove_negated_where_clause<'tcx>(
+    root_infcx: &InferCtxt<'tcx>,
+    clause: ty::Clause<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+) -> bool {
+    let Some(negative_predicate) = clause.as_predicate().flip_polarity(root_infcx.tcx) else {
+        return false;
+    };
+
+    // N.B. We don't need to use intercrate mode here because we're trying to prove
+    // the *existence* of a negative goal, not the non-existence of a positive goal.
+    // Without this, we over-eagerly register coherence ambiguity candidates when
+    // impl candidates do exist.
+    let ref infcx = root_infcx.fork_with_intercrate(false);
+    let ocx = ObligationCtxt::new(infcx);
+
+    ocx.register_obligation(Obligation::new(
+        infcx.tcx,
+        ObligationCause::dummy(),
+        param_env,
+        negative_predicate,
+    ));
+    if !ocx.select_all_or_error().is_empty() {
+        return false;
+    }
+
+    // FIXME: We could use the assumed_wf_types from both impls, I think,
+    // if that wasn't implemented just for LocalDefId, and we'd need to do
+    // the normalization ourselves since this is totally fallible...
+    let outlives_env = OutlivesEnvironment::new(param_env);
+
+    let errors = infcx.resolve_regions(&outlives_env);
+    if !errors.is_empty() {
+        return false;
+    }
+
+    true
+}
+
+/// Returns whether all impls which would apply to the `trait_ref`
+/// e.g. `Ty: Trait<Arg>` are already known in the local crate.
+///
+/// This both checks whether any downstream or sibling crates could
+/// implement it and whether an upstream crate can add this impl
+/// without breaking backwards compatibility.
+#[instrument(level = "debug", skip(tcx, lazily_normalize_ty), ret)]
+pub fn trait_ref_is_knowable<'tcx, E: Debug>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
+    mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
+) -> Result<Result<(), Conflict>, E> {
+    if orphan_check_trait_ref(trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() {
+        // A downstream or cousin crate is allowed to implement some
+        // substitution of this trait-ref.
+        return Ok(Err(Conflict::Downstream));
+    }
+
+    if trait_ref_is_local_or_fundamental(tcx, trait_ref) {
+        // This is a local or fundamental trait, so future-compatibility
+        // is no concern. We know that downstream/cousin crates are not
+        // allowed to implement a substitution of this trait ref, which
+        // means impls could only come from dependencies of this crate,
+        // which we already know about.
+        return Ok(Ok(()));
+    }
+
+    // This is a remote non-fundamental trait, so if another crate
+    // can be the "final owner" of a substitution of this trait-ref,
+    // they are allowed to implement it future-compatibly.
+    //
+    // However, if we are a final owner, then nobody else can be,
+    // and if we are an intermediate owner, then we don't care
+    // about future-compatibility, which means that we're OK if
+    // we are an owner.
+    if orphan_check_trait_ref(trait_ref, InCrate::Local, &mut lazily_normalize_ty)?.is_ok() {
+        Ok(Ok(()))
+    } else {
+        Ok(Err(Conflict::Upstream))
+    }
+}
+
+pub fn trait_ref_is_local_or_fundamental<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
+) -> bool {
+    trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental)
+}
+
+#[derive(Debug)]
+pub enum OrphanCheckErr<'tcx> {
+    NonLocalInputType(Vec<(Ty<'tcx>, bool /* Is this the first input type? */)>),
+    UncoveredTy(Ty<'tcx>, Option<Ty<'tcx>>),
+}
+
+/// Checks the coherence orphan rules. `impl_def_id` should be the
+/// `DefId` of a trait impl. To pass, either the trait must be local, or else
+/// two conditions must be satisfied:
+///
+/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
+/// 2. Some local type must appear in `Self`.
+#[instrument(level = "debug", skip(tcx), ret)]
+pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> {
+    // We only except this routine to be invoked on implementations
+    // of a trait, not inherent implementations.
+    let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
+    debug!(?trait_ref);
+
+    // If the *trait* is local to the crate, ok.
+    if trait_ref.def_id.is_local() {
+        debug!("trait {:?} is local to current crate", trait_ref.def_id);
+        return Ok(());
+    }
+
+    orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap()
+}
+
+/// Checks whether a trait-ref is potentially implementable by a crate.
+///
+/// The current rule is that a trait-ref orphan checks in a crate C:
+///
+/// 1. Order the parameters in the trait-ref in subst order - Self first,
+///    others linearly (e.g., `<U as Foo<V, W>>` is U < V < W).
+/// 2. Of these type parameters, there is at least one type parameter
+///    in which, walking the type as a tree, you can reach a type local
+///    to C where all types in-between are fundamental types. Call the
+///    first such parameter the "local key parameter".
+///     - e.g., `Box<LocalType>` is OK, because you can visit LocalType
+///       going through `Box`, which is fundamental.
+///     - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for
+///       the same reason.
+///     - but (knowing that `Vec<T>` is non-fundamental, and assuming it's
+///       not local), `Vec<LocalType>` is bad, because `Vec<->` is between
+///       the local type and the type parameter.
+/// 3. Before this local type, no generic type parameter of the impl must
+///    be reachable through fundamental types.
+///     - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental.
+///     - while `impl<T> Trait<LocalType> for Box<T>` results in an error, as `T` is
+///       reachable through the fundamental type `Box`.
+/// 4. Every type in the local key parameter not known in C, going
+///    through the parameter's type tree, must appear only as a subtree of
+///    a type local to C, with only fundamental types between the type
+///    local to C and the local key parameter.
+///     - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`)
+///     is bad, because the only local type with `T` as a subtree is
+///     `LocalType<T>`, and `Vec<->` is between it and the type parameter.
+///     - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because
+///     the second occurrence of `T` is not a subtree of *any* local type.
+///     - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of
+///     `LocalType<Vec<T>>`, which is local and has no types between it and
+///     the type parameter.
+///
+/// The orphan rules actually serve several different purposes:
+///
+/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where
+///    every type local to one crate is unknown in the other) can't implement
+///    the same trait-ref. This follows because it can be seen that no such
+///    type can orphan-check in 2 such crates.
+///
+///    To check that a local impl follows the orphan rules, we check it in
+///    InCrate::Local mode, using type parameters for the "generic" types.
+///
+/// 2. They ground negative reasoning for coherence. If a user wants to
+///    write both a conditional blanket impl and a specific impl, we need to
+///    make sure they do not overlap. For example, if we write
+///    ```ignore (illustrative)
+///    impl<T> IntoIterator for Vec<T>
+///    impl<T: Iterator> IntoIterator for T
+///    ```
+///    We need to be able to prove that `Vec<$0>: !Iterator` for every type $0.
+///    We can observe that this holds in the current crate, but we need to make
+///    sure this will also hold in all unknown crates (both "independent" crates,
+///    which we need for link-safety, and also child crates, because we don't want
+///    child crates to get error for impl conflicts in a *dependency*).
+///
+///    For that, we only allow negative reasoning if, for every assignment to the
+///    inference variables, every unknown crate would get an orphan error if they
+///    try to implement this trait-ref. To check for this, we use InCrate::Remote
+///    mode. That is sound because we already know all the impls from known crates.
+///
+/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can
+///    add "non-blanket" impls without breaking negative reasoning in dependent
+///    crates. This is the "rebalancing coherence" (RFC 1023) restriction.
+///
+///    For that, we only a allow crate to perform negative reasoning on
+///    non-local-non-`#[fundamental]` only if there's a local key parameter as per (2).
+///
+///    Because we never perform negative reasoning generically (coherence does
+///    not involve type parameters), this can be interpreted as doing the full
+///    orphan check (using InCrate::Local mode), substituting non-local known
+///    types for all inference variables.
+///
+///    This allows for crates to future-compatibly add impls as long as they
+///    can't apply to types with a key parameter in a child crate - applying
+///    the rules, this basically means that every type parameter in the impl
+///    must appear behind a non-fundamental type (because this is not a
+///    type-system requirement, crate owners might also go for "semantic
+///    future-compatibility" involving things such as sealed traits, but
+///    the above requirement is sufficient, and is necessary in "open world"
+///    cases).
+///
+/// Note that this function is never called for types that have both type
+/// parameters and inference variables.
+#[instrument(level = "trace", skip(lazily_normalize_ty), ret)]
+fn orphan_check_trait_ref<'tcx, E: Debug>(
+    trait_ref: ty::TraitRef<'tcx>,
+    in_crate: InCrate,
+    lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
+) -> Result<Result<(), OrphanCheckErr<'tcx>>, E> {
+    if trait_ref.has_infer() && trait_ref.has_param() {
+        bug!(
+            "can't orphan check a trait ref with both params and inference variables {:?}",
+            trait_ref
+        );
+    }
+
+    let mut checker = OrphanChecker::new(in_crate, lazily_normalize_ty);
+    Ok(match trait_ref.visit_with(&mut checker) {
+        ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
+        ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err),
+        ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
+            // Does there exist some local type after the `ParamTy`.
+            checker.search_first_local_ty = true;
+            if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) =
+                trait_ref.visit_with(&mut checker).break_value()
+            {
+                Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty)))
+            } else {
+                Err(OrphanCheckErr::UncoveredTy(ty, None))
+            }
+        }
+        ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()),
+    })
+}
+
+struct OrphanChecker<'tcx, F> {
+    in_crate: InCrate,
+    in_self_ty: bool,
+    lazily_normalize_ty: F,
+    /// Ignore orphan check failures and exclusively search for the first
+    /// local type.
+    search_first_local_ty: bool,
+    non_local_tys: Vec<(Ty<'tcx>, bool)>,
+}
+
+impl<'tcx, F, E> OrphanChecker<'tcx, F>
+where
+    F: FnOnce(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
+{
+    fn new(in_crate: InCrate, lazily_normalize_ty: F) -> Self {
+        OrphanChecker {
+            in_crate,
+            in_self_ty: true,
+            lazily_normalize_ty,
+            search_first_local_ty: false,
+            non_local_tys: Vec::new(),
+        }
+    }
+
+    fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
+        self.non_local_tys.push((t, self.in_self_ty));
+        ControlFlow::Continue(())
+    }
+
+    fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
+        if self.search_first_local_ty {
+            ControlFlow::Continue(())
+        } else {
+            ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
+        }
+    }
+
+    fn def_id_is_local(&mut self, def_id: DefId) -> bool {
+        match self.in_crate {
+            InCrate::Local => def_id.is_local(),
+            InCrate::Remote => false,
+        }
+    }
+}
+
+enum OrphanCheckEarlyExit<'tcx, E> {
+    NormalizationFailure(E),
+    ParamTy(Ty<'tcx>),
+    LocalTy(Ty<'tcx>),
+}
+
+impl<'tcx, F, E> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx, F>
+where
+    F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
+{
+    type BreakTy = OrphanCheckEarlyExit<'tcx, E>;
+    fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        ControlFlow::Continue(())
+    }
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        // Need to lazily normalize here in with `-Znext-solver=coherence`.
+        let ty = match (self.lazily_normalize_ty)(ty) {
+            Ok(ty) => ty,
+            Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)),
+        };
+
+        let result = match *ty.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(..)
+            | ty::Uint(..)
+            | ty::Float(..)
+            | ty::Str
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::Array(..)
+            | ty::Slice(..)
+            | ty::RawPtr(..)
+            | ty::Never
+            | ty::Tuple(..)
+            | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => {
+                self.found_non_local_ty(ty)
+            }
+
+            ty::Param(..) => self.found_param_ty(ty),
+
+            ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate {
+                InCrate::Local => self.found_non_local_ty(ty),
+                // The inference variable might be unified with a local
+                // type in that remote crate.
+                InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
+            },
+
+            // For fundamental types, we just look inside of them.
+            ty::Ref(_, ty, _) => ty.visit_with(self),
+            ty::Adt(def, args) => {
+                if self.def_id_is_local(def.did()) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else if def.is_fundamental() {
+                    args.visit_with(self)
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            ty::Foreign(def_id) => {
+                if self.def_id_is_local(def_id) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            ty::Dynamic(tt, ..) => {
+                let principal = tt.principal().map(|p| p.def_id());
+                if principal.is_some_and(|p| self.def_id_is_local(p)) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
+            ty::Closure(did, ..) | ty::Coroutine(did, ..) => {
+                if self.def_id_is_local(did) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            // This should only be created when checking whether we have to check whether some
+            // auto trait impl applies. There will never be multiple impls, so we can just
+            // act as if it were a local type here.
+            ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
+            ty::Alias(ty::Opaque, ..) => {
+                // This merits some explanation.
+                // Normally, opaque types are not involved when performing
+                // coherence checking, since it is illegal to directly
+                // implement a trait on an opaque type. However, we might
+                // end up looking at an opaque type during coherence checking
+                // if an opaque type gets used within another type (e.g. as
+                // the type of a field) when checking for auto trait or `Sized`
+                // impls. This requires us to decide whether or not an opaque
+                // type should be considered 'local' or not.
+                //
+                // We choose to treat all opaque types as non-local, even
+                // those that appear within the same crate. This seems
+                // somewhat surprising at first, but makes sense when
+                // you consider that opaque types are supposed to hide
+                // the underlying type *within the same crate*. When an
+                // opaque type is used from outside the module
+                // where it is declared, it should be impossible to observe
+                // anything about it other than the traits that it implements.
+                //
+                // The alternative would be to look at the underlying type
+                // to determine whether or not the opaque type itself should
+                // be considered local. However, this could make it a breaking change
+                // to switch the underlying ('defining') type from a local type
+                // to a remote type. This would violate the rule that opaque
+                // types should be completely opaque apart from the traits
+                // that they implement, so we don't use this behavior.
+                self.found_non_local_ty(ty)
+            }
+        };
+        // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so
+        // the first type we visit is always the self type.
+        self.in_self_ty = false;
+        result
+    }
+
+    /// All possible values for a constant parameter already exist
+    /// in the crate defining the trait, so they are always non-local[^1].
+    ///
+    /// Because there's no way to have an impl where the first local
+    /// generic argument is a constant, we also don't have to fail
+    /// the orphan check when encountering a parameter or a generic constant.
+    ///
+    /// This means that we can completely ignore constants during the orphan check.
+    ///
+    /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples.
+    ///
+    /// [^1]: This might not hold for function pointers or trait objects in the future.
+    /// As these should be quite rare as const arguments and especially rare as impl
+    /// parameters, allowing uncovered const parameters in impls seems more useful
+    /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile.
+    fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        ControlFlow::Continue(())
+    }
+}
+
+/// Compute the `intercrate_ambiguity_causes` for the new solver using
+/// "proof trees".
+///
+/// This is a bit scuffed but seems to be good enough, at least
+/// when looking at UI tests. Given that it is only used to improve
+/// diagnostics this is good enough. We can always improve it once there
+/// are test cases where it is currently not enough.
+fn compute_intercrate_ambiguity_causes<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    obligations: &[PredicateObligation<'tcx>],
+) -> FxIndexSet<IntercrateAmbiguityCause<'tcx>> {
+    let mut causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>> = Default::default();
+
+    for obligation in obligations {
+        search_ambiguity_causes(infcx, obligation.clone().into(), &mut causes);
+    }
+
+    causes
+}
+
+struct AmbiguityCausesVisitor<'a, 'tcx> {
+    causes: &'a mut FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
+}
+
+impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
+    type BreakTy = !;
+    fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> ControlFlow<Self::BreakTy> {
+        let infcx = goal.infcx();
+        for cand in goal.candidates() {
+            cand.visit_nested(self)?;
+        }
+        // When searching for intercrate ambiguity causes, we only need to look
+        // at ambiguous goals, as for others the coherence unknowable candidate
+        // was irrelevant.
+        match goal.result() {
+            Ok(Certainty::Maybe(_)) => {}
+            Ok(Certainty::Yes) | Err(NoSolution) => return ControlFlow::Continue(()),
+        }
+
+        let Goal { param_env, predicate } = goal.goal();
+
+        // For bound predicates we simply call `infcx.instantiate_binder_with_placeholders`
+        // 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,
+            Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)))
+                if matches!(
+                    infcx.tcx.def_kind(proj.projection_ty.def_id),
+                    DefKind::AssocTy | DefKind::AssocConst
+                ) =>
+            {
+                proj.projection_ty.trait_ref(infcx.tcx)
+            }
+            _ => return ControlFlow::Continue(()),
+        };
+
+        // Add ambiguity causes for reservation impls.
+        for cand in goal.candidates() {
+            if let inspect::ProbeKind::TraitCandidate {
+                source: CandidateSource::Impl(def_id),
+                result: Ok(_),
+            } = cand.kind()
+            {
+                if let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) {
+                    let message = infcx
+                        .tcx
+                        .get_attr(def_id, sym::rustc_reservation_impl)
+                        .and_then(|a| a.value_str());
+                    if let Some(message) = message {
+                        self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message });
+                    }
+                }
+            }
+        }
+
+        // Add ambiguity causes for unknowable goals.
+        let mut ambiguity_cause = None;
+        for cand in goal.candidates() {
+            // FIXME: boiiii, using string comparisions here sure is scuffed.
+            if let inspect::ProbeKind::MiscCandidate {
+                name: "coherence unknowable",
+                result: Ok(_),
+            } = cand.kind()
+            {
+                let lazily_normalize_ty = |ty: Ty<'tcx>| {
+                    let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(infcx);
+                    if matches!(ty.kind(), ty::Alias(..)) {
+                        // FIXME(-Znext-solver=coherence): we currently don't
+                        // normalize opaque types here, resulting in diverging behavior
+                        // for TAITs.
+                        match infcx
+                            .at(&ObligationCause::dummy(), param_env)
+                            .structurally_normalize(ty, &mut *fulfill_cx)
+                        {
+                            Ok(ty) => Ok(ty),
+                            Err(_errs) => Err(()),
+                        }
+                    } else {
+                        Ok(ty)
+                    }
+                };
+
+                infcx.probe(|_| {
+                    match trait_ref_is_knowable(infcx.tcx, trait_ref, lazily_normalize_ty) {
+                        Err(()) => {}
+                        Ok(Ok(())) => warn!("expected an unknowable trait ref: {trait_ref:?}"),
+                        Ok(Err(conflict)) => {
+                            if !trait_ref.references_error() {
+                                // Normalize the trait ref for diagnostics, ignoring any errors if this fails.
+                                let trait_ref =
+                                    deeply_normalize_for_diagnostics(infcx, param_env, trait_ref);
+
+                                let self_ty = trait_ref.self_ty();
+                                let self_ty = self_ty.has_concrete_skeleton().then(|| self_ty);
+                                ambiguity_cause = Some(match conflict {
+                                    Conflict::Upstream => {
+                                        IntercrateAmbiguityCause::UpstreamCrateUpdate {
+                                            trait_ref,
+                                            self_ty,
+                                        }
+                                    }
+                                    Conflict::Downstream => {
+                                        IntercrateAmbiguityCause::DownstreamCrate {
+                                            trait_ref,
+                                            self_ty,
+                                        }
+                                    }
+                                });
+                            }
+                        }
+                    }
+                })
+            } else {
+                match cand.result() {
+                    // We only add an ambiguity cause if the goal would otherwise
+                    // result in an error.
+                    //
+                    // FIXME: While this matches the behavior of the
+                    // old solver, it is not the only way in which the unknowable
+                    // candidates *weaken* coherence, they can also force otherwise
+                    // sucessful normalization to be ambiguous.
+                    Ok(Certainty::Maybe(_) | Certainty::Yes) => {
+                        ambiguity_cause = None;
+                        break;
+                    }
+                    Err(NoSolution) => continue,
+                }
+            }
+        }
+
+        if let Some(ambiguity_cause) = ambiguity_cause {
+            self.causes.insert(ambiguity_cause);
+        }
+
+        ControlFlow::Continue(())
+    }
+}
+
+fn search_ambiguity_causes<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    causes: &mut FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
+) {
+    infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { causes });
+}
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
new file mode 100644
index 00000000000..451e0823c25
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -0,0 +1,231 @@
+//! Checking that constant values used in types can be successfully evaluated.
+//!
+//! For concrete constants, this is fairly simple as we can just try and evaluate it.
+//!
+//! When dealing with polymorphic constants, for example `std::mem::size_of::<T>() - 1`,
+//! this is not as easy.
+//!
+//! In this case we try to build an abstract representation of this constant using
+//! `thir_abstract_const` which can then be checked for structural equality with other
+//! generic constants mentioned in the `caller_bounds` of the current environment.
+use rustc_hir::def::DefKind;
+use rustc_infer::infer::InferCtxt;
+use rustc_middle::mir::interpret::ErrorHandled;
+
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::abstract_const::NotConstEvaluatable;
+use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor};
+
+use rustc_span::Span;
+use std::ops::ControlFlow;
+
+use crate::traits::ObligationCtxt;
+
+/// Check if a given constant can be evaluated.
+#[instrument(skip(infcx), level = "debug")]
+pub fn is_const_evaluatable<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    unexpanded_ct: ty::Const<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    span: Span,
+) -> Result<(), NotConstEvaluatable> {
+    let tcx = infcx.tcx;
+    match tcx.expand_abstract_consts(unexpanded_ct).kind() {
+        ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) => (),
+        ty::ConstKind::Param(_)
+        | ty::ConstKind::Bound(_, _)
+        | ty::ConstKind::Placeholder(_)
+        | ty::ConstKind::Value(_)
+        | ty::ConstKind::Error(_) => return Ok(()),
+        ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer),
+    };
+
+    if tcx.features().generic_const_exprs {
+        let ct = tcx.expand_abstract_consts(unexpanded_ct);
+
+        let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
+            tcx.def_kind(uv.def) == DefKind::AnonConst
+        } else {
+            false
+        };
+
+        if !is_anon_ct {
+            if satisfied_from_param_env(tcx, infcx, ct, param_env) {
+                return Ok(());
+            }
+            if ct.has_non_region_infer() {
+                return Err(NotConstEvaluatable::MentionsInfer);
+            } else if ct.has_non_region_param() {
+                return Err(NotConstEvaluatable::MentionsParam);
+            }
+        }
+
+        match unexpanded_ct.kind() {
+            ty::ConstKind::Expr(_) => {
+                // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but
+                // currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it
+                // is evaluatable or not. For now we just ICE until this is implemented.
+                Err(NotConstEvaluatable::Error(tcx.dcx().span_delayed_bug(
+                    span,
+                    "evaluating `ConstKind::Expr` is not currently supported",
+                )))
+            }
+            ty::ConstKind::Unevaluated(uv) => {
+                let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
+                match concrete {
+                    Err(ErrorHandled::TooGeneric(_)) => {
+                        Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug(
+                            span,
+                            "Missing value for constant, but no error reported?",
+                        )))
+                    }
+                    Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())),
+                    Ok(_) => Ok(()),
+                }
+            }
+            _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
+        }
+    } else {
+        let uv = match unexpanded_ct.kind() {
+            ty::ConstKind::Unevaluated(uv) => uv,
+            ty::ConstKind::Expr(_) => {
+                bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled")
+            }
+            _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
+        };
+
+        // FIXME: We should only try to evaluate a given constant here if it is fully concrete
+        // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
+        //
+        // We previously did not check this, so we only emit a future compat warning if
+        // const evaluation succeeds and the given constant is still polymorphic for now
+        // and hopefully soon change this to an error.
+        //
+        // See #74595 for more details about this.
+        let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
+        match concrete {
+            // If we're evaluating a generic foreign constant, under a nightly compiler while
+            // the current crate does not enable `feature(generic_const_exprs)`, abort
+            // compilation with a useful error.
+            Err(_)
+                if tcx.sess.is_nightly_build()
+                    && satisfied_from_param_env(
+                        tcx,
+                        infcx,
+                        tcx.expand_abstract_consts(unexpanded_ct),
+                        param_env,
+                    ) =>
+            {
+                tcx.dcx()
+                    .struct_span_fatal(
+                        // Slightly better span than just using `span` alone
+                        if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def) } else { span },
+                        "failed to evaluate generic const expression",
+                    )
+                    .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
+                    .span_suggestion_verbose(
+                        rustc_span::DUMMY_SP,
+                        "consider enabling this feature",
+                        "#![feature(generic_const_exprs)]\n",
+                        rustc_errors::Applicability::MaybeIncorrect,
+                    )
+                    .emit()
+            }
+
+            Err(ErrorHandled::TooGeneric(_)) => {
+                let err = if uv.has_non_region_infer() {
+                    NotConstEvaluatable::MentionsInfer
+                } else if uv.has_non_region_param() {
+                    NotConstEvaluatable::MentionsParam
+                } else {
+                    let guar = infcx.dcx().span_delayed_bug(
+                        span,
+                        "Missing value for constant, but no error reported?",
+                    );
+                    NotConstEvaluatable::Error(guar)
+                };
+
+                Err(err)
+            }
+            Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())),
+            Ok(_) => Ok(()),
+        }
+    }
+}
+
+#[instrument(skip(infcx, tcx), level = "debug")]
+fn satisfied_from_param_env<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    infcx: &InferCtxt<'tcx>,
+    ct: ty::Const<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+) -> bool {
+    // Try to unify with each subtree in the AbstractConst to allow for
+    // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
+    // predicate for `(N + 1) * 2`
+    struct Visitor<'a, 'tcx> {
+        ct: ty::Const<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+
+        infcx: &'a InferCtxt<'tcx>,
+        single_match: Option<Result<ty::Const<'tcx>, ()>>,
+    }
+
+    impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for Visitor<'a, 'tcx> {
+        type BreakTy = ();
+        fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+            debug!("is_const_evaluatable: candidate={:?}", c);
+            if self.infcx.probe(|_| {
+                let ocx = ObligationCtxt::new(self.infcx);
+                ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()).is_ok()
+                    && ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok()
+                    && ocx.select_all_or_error().is_empty()
+            }) {
+                self.single_match = match self.single_match {
+                    None => Some(Ok(c)),
+                    Some(Ok(o)) if o == c => Some(Ok(c)),
+                    Some(_) => Some(Err(())),
+                };
+            }
+
+            if let ty::ConstKind::Expr(e) = c.kind() {
+                e.visit_with(self)
+            } else {
+                // FIXME(generic_const_exprs): This doesn't recurse into `<T as Trait<U>>::ASSOC`'s args.
+                // This is currently unobservable as `<T as Trait<{ U + 1 }>>::ASSOC` creates an anon const
+                // with its own `ConstEvaluatable` bound in the param env which we will visit separately.
+                //
+                // If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const
+                // this will be incorrect. It might be worth investigating making `predicates_of` elaborate
+                // all of the `ConstEvaluatable` bounds rather than having a visitor here.
+                ControlFlow::Continue(())
+            }
+        }
+    }
+
+    let mut single_match: Option<Result<ty::Const<'tcx>, ()>> = None;
+
+    for pred in param_env.caller_bounds() {
+        match pred.kind().skip_binder() {
+            ty::ClauseKind::ConstEvaluatable(ce) => {
+                let b_ct = tcx.expand_abstract_consts(ce);
+                let mut v = Visitor { ct, infcx, param_env, single_match };
+                let _ = b_ct.visit_with(&mut v);
+
+                single_match = v.single_match;
+            }
+            _ => {} // don't care
+        }
+    }
+
+    if let Some(Ok(c)) = single_match {
+        let ocx = ObligationCtxt::new(infcx);
+        assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok());
+        assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok());
+        assert!(ocx.select_all_or_error().is_empty());
+        return true;
+    }
+
+    debug!("is_const_evaluatable: no");
+    false
+}
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
new file mode 100644
index 00000000000..013a50f9fa1
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -0,0 +1,272 @@
+use std::cell::RefCell;
+use std::fmt::Debug;
+
+use super::FulfillmentContext;
+use super::TraitEngine;
+use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt;
+use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::NormalizeExt;
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_infer::infer::at::ToTrace;
+use rustc_infer::infer::canonical::{
+    Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse,
+};
+use rustc_infer::infer::outlives::env::OutlivesEnvironment;
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
+use rustc_infer::traits::{
+    FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _,
+};
+use rustc_middle::arena::ArenaAllocatable;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::ToPredicate;
+use rustc_middle::ty::TypeFoldable;
+use rustc_middle::ty::Variance;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+pub trait TraitEngineExt<'tcx> {
+    fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>;
+}
+
+impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
+    fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> {
+        if infcx.next_trait_solver() {
+            Box::new(NextFulfillmentCtxt::new(infcx))
+        } else {
+            let new_solver_globally =
+                infcx.tcx.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.globally);
+            assert!(
+                !new_solver_globally,
+                "using old solver even though new solver is enabled globally"
+            );
+            Box::new(FulfillmentContext::new(infcx))
+        }
+    }
+}
+
+/// Used if you want to have pleasant experience when dealing
+/// with obligations outside of hir or mir typeck.
+pub struct ObligationCtxt<'a, 'tcx> {
+    pub infcx: &'a InferCtxt<'tcx>,
+    engine: RefCell<Box<dyn TraitEngine<'tcx>>>,
+}
+
+impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
+        Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) }
+    }
+
+    pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
+        self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation);
+    }
+
+    pub fn register_obligations(
+        &self,
+        obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
+    ) {
+        // Can't use `register_predicate_obligations` because the iterator
+        // may also use this `ObligationCtxt`.
+        for obligation in obligations {
+            self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation)
+        }
+    }
+
+    pub fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
+        let InferOk { value, obligations } = infer_ok;
+        self.engine.borrow_mut().register_predicate_obligations(self.infcx, obligations);
+        value
+    }
+
+    /// Requires that `ty` must implement the trait with `def_id` in
+    /// the given environment. This trait must not have any type
+    /// parameters (except for `Self`).
+    pub fn register_bound(
+        &self,
+        cause: ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+        def_id: DefId,
+    ) {
+        let tcx = self.infcx.tcx;
+        let trait_ref = ty::TraitRef::new(tcx, def_id, [ty]);
+        self.register_obligation(Obligation {
+            cause,
+            recursion_depth: 0,
+            param_env,
+            predicate: ty::Binder::dummy(trait_ref).to_predicate(tcx),
+        });
+    }
+
+    pub fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        value: T,
+    ) -> T {
+        let infer_ok = self.infcx.at(cause, param_env).normalize(value);
+        self.register_infer_ok_obligations(infer_ok)
+    }
+
+    /// Makes `expected <: actual`.
+    pub fn eq_exp<T>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        a_is_expected: bool,
+        a: T,
+        b: T,
+    ) -> Result<(), TypeError<'tcx>>
+    where
+        T: ToTrace<'tcx>,
+    {
+        self.infcx
+            .at(cause, param_env)
+            .eq_exp(DefineOpaqueTypes::Yes, a_is_expected, a, b)
+            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
+    }
+
+    pub fn eq<T: ToTrace<'tcx>>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        expected: T,
+        actual: T,
+    ) -> Result<(), TypeError<'tcx>> {
+        self.infcx
+            .at(cause, param_env)
+            .eq(DefineOpaqueTypes::Yes, expected, actual)
+            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
+    }
+
+    /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`.
+    pub fn sub<T: ToTrace<'tcx>>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        expected: T,
+        actual: T,
+    ) -> Result<(), TypeError<'tcx>> {
+        self.infcx
+            .at(cause, param_env)
+            .sub(DefineOpaqueTypes::Yes, expected, actual)
+            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
+    }
+
+    pub fn relate<T: ToTrace<'tcx>>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        variance: Variance,
+        expected: T,
+        actual: T,
+    ) -> Result<(), TypeError<'tcx>> {
+        self.infcx
+            .at(cause, param_env)
+            .relate(DefineOpaqueTypes::Yes, expected, variance, actual)
+            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
+    }
+
+    /// Checks whether `expected` is a supertype of `actual`: `expected :> actual`.
+    pub fn sup<T: ToTrace<'tcx>>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        expected: T,
+        actual: T,
+    ) -> Result<(), TypeError<'tcx>> {
+        self.infcx
+            .at(cause, param_env)
+            .sup(DefineOpaqueTypes::Yes, expected, actual)
+            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
+    }
+
+    #[must_use]
+    pub fn select_where_possible(&self) -> Vec<FulfillmentError<'tcx>> {
+        self.engine.borrow_mut().select_where_possible(self.infcx)
+    }
+
+    #[must_use]
+    pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> {
+        self.engine.borrow_mut().select_all_or_error(self.infcx)
+    }
+
+    /// Resolves regions and reports errors.
+    ///
+    /// Takes ownership of the context as doing trait solving afterwards
+    /// will result in region constraints getting ignored.
+    pub fn resolve_regions_and_report_errors(
+        self,
+        generic_param_scope: LocalDefId,
+        outlives_env: &OutlivesEnvironment<'tcx>,
+    ) -> Result<(), ErrorGuaranteed> {
+        let errors = self.infcx.resolve_regions(outlives_env);
+        if errors.is_empty() {
+            Ok(())
+        } else {
+            Err(self.infcx.err_ctxt().report_region_errors(generic_param_scope, &errors))
+        }
+    }
+
+    pub fn assumed_wf_types_and_report_errors(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        def_id: LocalDefId,
+    ) -> Result<FxIndexSet<Ty<'tcx>>, ErrorGuaranteed> {
+        self.assumed_wf_types(param_env, def_id)
+            .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(errors))
+    }
+
+    pub fn assumed_wf_types(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        def_id: LocalDefId,
+    ) -> Result<FxIndexSet<Ty<'tcx>>, Vec<FulfillmentError<'tcx>>> {
+        let tcx = self.infcx.tcx;
+        let mut implied_bounds = FxIndexSet::default();
+        let mut errors = Vec::new();
+        for &(ty, span) in tcx.assumed_wf_types(def_id) {
+            // FIXME(@lcnr): rustc currently does not check wf for types
+            // pre-normalization, meaning that implied bounds are sometimes
+            // incorrect. See #100910 for more details.
+            //
+            // Not adding the unnormalized types here mostly fixes that, except
+            // that there are projections which are still ambiguous in the item definition
+            // but do normalize successfully when using the item, see #98543.
+            //
+            // Anyways, I will hopefully soon change implied bounds to make all of this
+            // sound and then uncomment this line again.
+
+            // implied_bounds.insert(ty);
+            let cause = ObligationCause::misc(span, def_id);
+            match self
+                .infcx
+                .at(&cause, param_env)
+                .deeply_normalize(ty, &mut **self.engine.borrow_mut())
+            {
+                // Insert well-formed types, ignoring duplicates.
+                Ok(normalized) => drop(implied_bounds.insert(normalized)),
+                Err(normalization_errors) => errors.extend(normalization_errors),
+            };
+        }
+
+        if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) }
+    }
+
+    pub fn make_canonicalized_query_response<T>(
+        &self,
+        inference_vars: CanonicalVarValues<'tcx>,
+        answer: T,
+    ) -> Result<CanonicalQueryResponse<'tcx, T>, NoSolution>
+    where
+        T: Debug + TypeFoldable<TyCtxt<'tcx>>,
+        Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
+    {
+        self.infcx.make_canonicalized_query_response(
+            inference_vars,
+            answer,
+            &mut **self.engine.borrow_mut(),
+        )
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
new file mode 100644
index 00000000000..b246e476bed
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
@@ -0,0 +1,101 @@
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
+use rustc_infer::traits::util::elaborate;
+use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation};
+use rustc_middle::ty;
+use rustc_span::{Span, DUMMY_SP};
+
+use crate::traits::ObligationCtxt;
+
+pub enum Ambiguity {
+    DefId(DefId),
+    ParamEnv(Span),
+}
+
+pub fn recompute_applicable_impls<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    obligation: &PolyTraitObligation<'tcx>,
+) -> Vec<Ambiguity> {
+    let tcx = infcx.tcx;
+    let param_env = obligation.param_env;
+
+    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;
+        }
+
+        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()
+    };
+
+    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;
+        }
+
+        ocx.select_where_possible().is_empty()
+    };
+
+    let mut ambiguities = Vec::new();
+
+    tcx.for_each_relevant_impl(
+        obligation.predicate.def_id(),
+        obligation.predicate.skip_binder().trait_ref.self_ty(),
+        |impl_def_id| {
+            if infcx.probe(|_| impl_may_apply(impl_def_id)) {
+                ambiguities.push(Ambiguity::DefId(impl_def_id))
+            }
+        },
+    );
+
+    let predicates =
+        tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
+    for (pred, span) in elaborate(tcx, predicates.into_iter()) {
+        let kind = pred.kind();
+        if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder()
+            && param_env_candidate_may_apply(kind.rebind(trait_pred))
+        {
+            if kind.rebind(trait_pred.trait_ref)
+                == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id()))
+            {
+                ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id())))
+            } else {
+                ambiguities.push(Ambiguity::ParamEnv(span))
+            }
+        }
+    }
+
+    ambiguities
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs
new file mode 100644
index 00000000000..90e2c1531b4
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs
@@ -0,0 +1,274 @@
+use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use crate::infer::InferCtxt;
+use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::Node;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{Span, DUMMY_SP};
+
+use super::ArgKind;
+
+pub use rustc_infer::traits::error_reporting::*;
+
+pub trait InferCtxtExt<'tcx> {
+    /// Given some node representing a fn-like thing in the HIR map,
+    /// returns a span and `ArgKind` information that describes the
+    /// arguments it expects. This can be supplied to
+    /// `report_arg_count_mismatch`.
+    fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)>;
+
+    /// Reports an error when the number of arguments needed by a
+    /// trait match doesn't match the number that the expression
+    /// provides.
+    fn report_arg_count_mismatch(
+        &self,
+        span: Span,
+        found_span: Option<Span>,
+        expected_args: Vec<ArgKind>,
+        found_args: Vec<ArgKind>,
+        is_closure: bool,
+        closure_pipe_span: Option<Span>,
+    ) -> DiagnosticBuilder<'tcx>;
+
+    /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
+    /// in that order, and returns the generic type corresponding to the
+    /// argument of that trait (corresponding to the closure arguments).
+    fn type_implements_fn_trait(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: ty::Binder<'tcx, Ty<'tcx>>,
+        polarity: ty::ImplPolarity,
+    ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>;
+}
+
+impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
+    /// Given some node representing a fn-like thing in the HIR map,
+    /// returns a span and `ArgKind` information that describes the
+    /// arguments it expects. This can be supplied to
+    /// `report_arg_count_mismatch`.
+    fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
+        let sm = self.tcx.sess.source_map();
+        let hir = self.tcx.hir();
+        Some(match node {
+            Node::Expr(&hir::Expr {
+                kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
+                ..
+            }) => (
+                fn_decl_span,
+                fn_arg_span,
+                hir.body(body)
+                    .params
+                    .iter()
+                    .map(|arg| {
+                        if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat
+                        {
+                            Some(ArgKind::Tuple(
+                                Some(span),
+                                args.iter()
+                                    .map(|pat| {
+                                        sm.span_to_snippet(pat.span)
+                                            .ok()
+                                            .map(|snippet| (snippet, "_".to_owned()))
+                                    })
+                                    .collect::<Option<Vec<_>>>()?,
+                            ))
+                        } else {
+                            let name = sm.span_to_snippet(arg.pat.span).ok()?;
+                            Some(ArgKind::Arg(name, "_".to_owned()))
+                        }
+                    })
+                    .collect::<Option<Vec<ArgKind>>>()?,
+            ),
+            Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. })
+            | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. })
+            | Node::TraitItem(&hir::TraitItem {
+                kind: hir::TraitItemKind::Fn(ref sig, _), ..
+            }) => (
+                sig.span,
+                None,
+                sig.decl
+                    .inputs
+                    .iter()
+                    .map(|arg| match arg.kind {
+                        hir::TyKind::Tup(tys) => ArgKind::Tuple(
+                            Some(arg.span),
+                            vec![("_".to_owned(), "_".to_owned()); tys.len()],
+                        ),
+                        _ => ArgKind::empty(),
+                    })
+                    .collect::<Vec<ArgKind>>(),
+            ),
+            Node::Ctor(variant_data) => {
+                let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
+                (span, None, vec![ArgKind::empty(); variant_data.fields().len()])
+            }
+            _ => panic!("non-FnLike node found: {node:?}"),
+        })
+    }
+
+    /// Reports an error when the number of arguments needed by a
+    /// trait match doesn't match the number that the expression
+    /// provides.
+    fn report_arg_count_mismatch(
+        &self,
+        span: Span,
+        found_span: Option<Span>,
+        expected_args: Vec<ArgKind>,
+        found_args: Vec<ArgKind>,
+        is_closure: bool,
+        closure_arg_span: Option<Span>,
+    ) -> DiagnosticBuilder<'tcx> {
+        let kind = if is_closure { "closure" } else { "function" };
+
+        let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
+            let arg_length = arguments.len();
+            let distinct = matches!(other, &[ArgKind::Tuple(..)]);
+            match (arg_length, arguments.get(0)) {
+                (1, Some(ArgKind::Tuple(_, fields))) => {
+                    format!("a single {}-tuple as argument", fields.len())
+                }
+                _ => format!(
+                    "{} {}argument{}",
+                    arg_length,
+                    if distinct && arg_length > 1 { "distinct " } else { "" },
+                    pluralize!(arg_length)
+                ),
+            }
+        };
+
+        let expected_str = args_str(&expected_args, &found_args);
+        let found_str = args_str(&found_args, &expected_args);
+
+        let mut err = struct_span_err!(
+            self.dcx(),
+            span,
+            E0593,
+            "{} is expected to take {}, but it takes {}",
+            kind,
+            expected_str,
+            found_str,
+        );
+
+        err.span_label(span, format!("expected {kind} that takes {expected_str}"));
+
+        if let Some(found_span) = found_span {
+            err.span_label(found_span, format!("takes {found_str}"));
+
+            // Suggest to take and ignore the arguments with expected_args_length `_`s if
+            // found arguments is empty (assume the user just wants to ignore args in this case).
+            // For example, if `expected_args_length` is 2, suggest `|_, _|`.
+            if found_args.is_empty() && is_closure {
+                let underscores = vec!["_"; expected_args.len()].join(", ");
+                err.span_suggestion_verbose(
+                    closure_arg_span.unwrap_or(found_span),
+                    format!(
+                        "consider changing the closure to take and ignore the expected argument{}",
+                        pluralize!(expected_args.len())
+                    ),
+                    format!("|{underscores}|"),
+                    Applicability::MachineApplicable,
+                );
+            }
+
+            if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
+                if fields.len() == expected_args.len() {
+                    let sugg = fields
+                        .iter()
+                        .map(|(name, _)| name.to_owned())
+                        .collect::<Vec<String>>()
+                        .join(", ");
+                    err.span_suggestion_verbose(
+                        found_span,
+                        "change the closure to take multiple arguments instead of a single tuple",
+                        format!("|{sugg}|"),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+            if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..]
+                && fields.len() == found_args.len()
+                && is_closure
+            {
+                let sugg = format!(
+                    "|({}){}|",
+                    found_args
+                        .iter()
+                        .map(|arg| match arg {
+                            ArgKind::Arg(name, _) => name.to_owned(),
+                            _ => "_".to_owned(),
+                        })
+                        .collect::<Vec<String>>()
+                        .join(", "),
+                    // add type annotations if available
+                    if found_args.iter().any(|arg| match arg {
+                        ArgKind::Arg(_, ty) => ty != "_",
+                        _ => false,
+                    }) {
+                        format!(
+                            ": ({})",
+                            fields
+                                .iter()
+                                .map(|(_, ty)| ty.to_owned())
+                                .collect::<Vec<String>>()
+                                .join(", ")
+                        )
+                    } else {
+                        String::new()
+                    },
+                );
+                err.span_suggestion_verbose(
+                    found_span,
+                    "change the closure to accept a tuple instead of individual arguments",
+                    sugg,
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+
+        err
+    }
+
+    fn type_implements_fn_trait(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: ty::Binder<'tcx, Ty<'tcx>>,
+        polarity: ty::ImplPolarity,
+    ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> {
+        self.commit_if_ok(|_| {
+            for trait_def_id in [
+                self.tcx.lang_items().fn_trait(),
+                self.tcx.lang_items().fn_mut_trait(),
+                self.tcx.lang_items().fn_once_trait(),
+            ] {
+                let Some(trait_def_id) = trait_def_id else { continue };
+                // Make a fresh inference variable so we can determine what the substitutions
+                // of the trait are.
+                let var = self.next_ty_var(TypeVariableOrigin {
+                    span: DUMMY_SP,
+                    kind: TypeVariableOriginKind::MiscVariable,
+                });
+                // FIXME(effects)
+                let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]);
+                let obligation = Obligation::new(
+                    self.tcx,
+                    ObligationCause::dummy(),
+                    param_env,
+                    ty.rebind(ty::TraitPredicate { trait_ref, polarity }),
+                );
+                let ocx = ObligationCtxt::new(self);
+                ocx.register_obligation(obligation);
+                if ocx.select_all_or_error().is_empty() {
+                    return Ok((
+                        self.tcx
+                            .fn_trait_kind_from_def_id(trait_def_id)
+                            .expect("expected to map DefId to ClosureKind"),
+                        ty.rebind(self.resolve_vars_if_possible(var)),
+                    ));
+                }
+            }
+
+            Err(())
+        })
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
new file mode 100644
index 00000000000..0796cb57d97
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -0,0 +1,183 @@
+// ignore-tidy-filelength :(
+
+mod ambiguity;
+mod infer_ctxt_ext;
+pub mod on_unimplemented;
+pub mod suggestions;
+mod type_err_ctxt_ext;
+
+use super::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
+use crate::infer::InferCtxt;
+use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
+use rustc_middle::traits::solve::Goal;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::Span;
+use std::io::Write;
+use std::ops::ControlFlow;
+
+pub use self::infer_ctxt_ext::*;
+pub use self::type_err_ctxt_ext::*;
+
+// When outputting impl candidates, prefer showing those that are more similar.
+//
+// We also compare candidates after skipping lifetimes, which has a lower
+// priority than exact matches.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum CandidateSimilarity {
+    Exact { ignoring_lifetimes: bool },
+    Fuzzy { ignoring_lifetimes: bool },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct ImplCandidate<'tcx> {
+    pub trait_ref: ty::TraitRef<'tcx>,
+    pub similarity: CandidateSimilarity,
+    impl_def_id: DefId,
+}
+
+enum GetSafeTransmuteErrorAndReason {
+    Silent,
+    Error { err_msg: String, safe_transmute_explanation: String },
+}
+
+struct UnsatisfiedConst(pub bool);
+
+/// Crude way of getting back an `Expr` from a `Span`.
+pub struct FindExprBySpan<'hir> {
+    pub span: Span,
+    pub result: Option<&'hir hir::Expr<'hir>>,
+    pub ty_result: Option<&'hir hir::Ty<'hir>>,
+}
+
+impl<'hir> FindExprBySpan<'hir> {
+    pub fn new(span: Span) -> Self {
+        Self { span, result: None, ty_result: None }
+    }
+}
+
+impl<'v> Visitor<'v> for FindExprBySpan<'v> {
+    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+        if self.span == ex.span {
+            self.result = Some(ex);
+        } else {
+            hir::intravisit::walk_expr(self, ex);
+        }
+    }
+    fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+        if self.span == ty.span {
+            self.ty_result = Some(ty);
+        } else {
+            hir::intravisit::walk_ty(self, ty);
+        }
+    }
+}
+
+/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
+/// `param: ?Sized` would be a valid constraint.
+struct FindTypeParam {
+    param: rustc_span::Symbol,
+    invalid_spans: Vec<Span>,
+    nested: bool,
+}
+
+impl<'v> Visitor<'v> for FindTypeParam {
+    fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) {
+        // Skip where-clauses, to avoid suggesting indirection for type parameters found there.
+    }
+
+    fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
+        // We collect the spans of all uses of the "bare" type param, like in `field: T` or
+        // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
+        // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
+        // obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
+        // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
+        // in that case should make what happened clear enough.
+        match ty.kind {
+            hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
+            hir::TyKind::Path(hir::QPath::Resolved(None, path))
+                if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
+            {
+                if !self.nested {
+                    debug!(?ty, "FindTypeParam::visit_ty");
+                    self.invalid_spans.push(ty.span);
+                }
+            }
+            hir::TyKind::Path(_) => {
+                let prev = self.nested;
+                self.nested = true;
+                hir::intravisit::walk_ty(self, ty);
+                self.nested = prev;
+            }
+            _ => {
+                hir::intravisit::walk_ty(self, ty);
+            }
+        }
+    }
+}
+
+/// Summarizes information
+#[derive(Clone)]
+pub enum ArgKind {
+    /// An argument of non-tuple type. Parameters are (name, ty)
+    Arg(String, String),
+
+    /// An argument of tuple type. For a "found" argument, the span is
+    /// the location in the source of the pattern. For an "expected"
+    /// argument, it will be None. The vector is a list of (name, ty)
+    /// strings for the components of the tuple.
+    Tuple(Option<Span>, Vec<(String, String)>),
+}
+
+impl ArgKind {
+    fn empty() -> ArgKind {
+        ArgKind::Arg("_".to_owned(), "_".to_owned())
+    }
+
+    /// Creates an `ArgKind` from the expected type of an
+    /// argument. It has no name (`_`) and an optional source span.
+    pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind {
+        match t.kind() {
+            ty::Tuple(tys) => ArgKind::Tuple(
+                span,
+                tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(),
+            ),
+            _ => ArgKind::Arg("_".to_owned(), t.to_string()),
+        }
+    }
+}
+
+struct HasNumericInferVisitor;
+
+impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
+    type BreakTy = ();
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+}
+
+#[derive(Copy, Clone)]
+pub enum DefIdOrName {
+    DefId(DefId),
+    Name(&'static str),
+}
+
+pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &InferCtxt<'tcx>) {
+    infcx.probe(|_| {
+        let goal = Goal { predicate: o.predicate, param_env: o.param_env };
+        let tree = infcx
+            .evaluate_root_goal(goal, GenerateProofTree::Yes)
+            .1
+            .expect("proof tree should have been generated");
+        let mut lock = std::io::stdout().lock();
+        let _ = lock.write_fmt(format_args!("{tree:?}\n"));
+        let _ = lock.flush();
+    });
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
new file mode 100644
index 00000000000..52f91d282f0
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -0,0 +1,910 @@
+use super::{ObligationCauseCode, PredicateObligation};
+use crate::infer::error_reporting::TypeErrCtxt;
+use rustc_ast::AttrArgs;
+use rustc_ast::AttrArgsEq;
+use rustc_ast::AttrKind;
+use rustc_ast::{Attribute, MetaItem, NestedMetaItem};
+use rustc_attr as attr;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{struct_span_err, ErrorGuaranteed};
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_middle::ty::GenericArgsRef;
+use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
+use rustc_parse_format::{ParseMode, Parser, Piece, Position};
+use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
+use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::{Span, DUMMY_SP};
+use std::iter;
+
+use crate::errors::{
+    EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
+};
+
+use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
+
+pub trait TypeErrCtxtExt<'tcx> {
+    /*private*/
+    fn impl_similar_to(
+        &self,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> Option<(DefId, GenericArgsRef<'tcx>)>;
+
+    /*private*/
+    fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str>;
+
+    fn on_unimplemented_note(
+        &self,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> OnUnimplementedNote;
+}
+
+/// The symbols which are always allowed in a format string
+static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
+    kw::SelfUpper,
+    sym::ItemContext,
+    sym::from_desugaring,
+    sym::direct,
+    sym::cause,
+    sym::integral,
+    sym::integer_,
+    sym::float,
+    sym::_Self,
+    sym::crate_local,
+    sym::Trait,
+];
+
+impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
+    fn impl_similar_to(
+        &self,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        obligation: &PredicateObligation<'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();
+
+        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);
+
+            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 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));
+                }
+            }
+        });
+
+        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)
+    }
+
+    /// Used to set on_unimplemented's `ItemContext`
+    /// to be the enclosing (async) block/function/closure
+    fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> {
+        match self.tcx.opt_hir_node_by_def_id(def_id)? {
+            hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) => Some("a function"),
+            hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
+                Some("a trait method")
+            }
+            hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
+                Some("a method")
+            }
+            hir::Node::Expr(hir::Expr {
+                kind: hir::ExprKind::Closure(hir::Closure { kind, .. }),
+                ..
+            }) => Some(self.describe_closure(*kind)),
+            _ => None,
+        }
+    }
+
+    fn on_unimplemented_note(
+        &self,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> OnUnimplementedNote {
+        let (def_id, args) = self
+            .impl_similar_to(trait_ref, obligation)
+            .unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().args));
+        let trait_ref = trait_ref.skip_binder();
+
+        let mut flags = vec![];
+        // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
+        // but I guess we could synthesize one here. We don't see any errors that rely on
+        // that yet, though.
+        let enclosure = self.describe_enclosure(obligation.cause.body_id).map(|t| t.to_owned());
+        flags.push((sym::ItemContext, enclosure));
+
+        match obligation.cause.code() {
+            ObligationCauseCode::BuiltinDerivedObligation(..)
+            | ObligationCauseCode::ImplDerivedObligation(..)
+            | ObligationCauseCode::DerivedObligation(..) => {}
+            _ => {
+                // this is a "direct", user-specified, rather than derived,
+                // obligation.
+                flags.push((sym::direct, None));
+            }
+        }
+
+        if let Some(k) = obligation.cause.span.desugaring_kind() {
+            flags.push((sym::from_desugaring, None));
+            flags.push((sym::from_desugaring, Some(format!("{k:?}"))));
+        }
+
+        if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
+            flags.push((sym::cause, Some("MainFunctionType".to_string())));
+        }
+
+        flags.push((sym::Trait, Some(trait_ref.print_trait_sugared().to_string())));
+
+        // Add all types without trimmed paths or visible paths, ensuring they end up with
+        // their "canonical" def path.
+        ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({
+            let generics = self.tcx.generics_of(def_id);
+            let self_ty = trait_ref.self_ty();
+            // This is also included through the generics list as `Self`,
+            // but the parser won't allow you to use it
+            flags.push((sym::_Self, Some(self_ty.to_string())));
+            if let Some(def) = self_ty.ty_adt_def() {
+                // We also want to be able to select self's original
+                // signature with no type arguments resolved
+                flags.push((
+                    sym::_Self,
+                    Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
+                ));
+            }
+
+            for param in generics.params.iter() {
+                let value = match param.kind {
+                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
+                        args[param.index as usize].to_string()
+                    }
+                    GenericParamDefKind::Lifetime => continue,
+                };
+                let name = param.name;
+                flags.push((name, Some(value)));
+
+                if let GenericParamDefKind::Type { .. } = param.kind {
+                    let param_ty = args[param.index as usize].expect_ty();
+                    if let Some(def) = param_ty.ty_adt_def() {
+                        // We also want to be able to select the parameter's
+                        // original signature with no type arguments resolved
+                        flags.push((
+                            name,
+                            Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
+                        ));
+                    }
+                }
+            }
+
+            if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
+                flags.push((sym::crate_local, None));
+            }
+
+            // Allow targeting all integers using `{integral}`, even if the exact type was resolved
+            if self_ty.is_integral() {
+                flags.push((sym::_Self, Some("{integral}".to_owned())));
+            }
+
+            if self_ty.is_array_slice() {
+                flags.push((sym::_Self, Some("&[]".to_owned())));
+            }
+
+            if self_ty.is_fn() {
+                let fn_sig = self_ty.fn_sig(self.tcx);
+                let shortname = match fn_sig.unsafety() {
+                    hir::Unsafety::Normal => "fn",
+                    hir::Unsafety::Unsafe => "unsafe fn",
+                };
+                flags.push((sym::_Self, Some(shortname.to_owned())));
+            }
+
+            // Slices give us `[]`, `[{ty}]`
+            if let ty::Slice(aty) = self_ty.kind() {
+                flags.push((sym::_Self, Some("[]".to_string())));
+                if let Some(def) = aty.ty_adt_def() {
+                    // We also want to be able to select the slice's type's original
+                    // signature with no type arguments resolved
+                    flags.push((
+                        sym::_Self,
+                        Some(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())),
+                    ));
+                }
+                if aty.is_integral() {
+                    flags.push((sym::_Self, Some("[{integral}]".to_string())));
+                }
+            }
+
+            // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
+            if let ty::Array(aty, len) = self_ty.kind() {
+                flags.push((sym::_Self, Some("[]".to_string())));
+                let len = len.try_to_valtree().and_then(|v| v.try_to_target_usize(self.tcx));
+                flags.push((sym::_Self, Some(format!("[{aty}; _]"))));
+                if let Some(n) = len {
+                    flags.push((sym::_Self, Some(format!("[{aty}; {n}]"))));
+                }
+                if let Some(def) = aty.ty_adt_def() {
+                    // We also want to be able to select the array's type's original
+                    // signature with no type arguments resolved
+                    let def_ty = self.tcx.type_of(def.did()).instantiate_identity();
+                    flags.push((sym::_Self, Some(format!("[{def_ty}; _]"))));
+                    if let Some(n) = len {
+                        flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]"))));
+                    }
+                }
+                if aty.is_integral() {
+                    flags.push((sym::_Self, Some("[{integral}; _]".to_string())));
+                    if let Some(n) = len {
+                        flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]"))));
+                    }
+                }
+            }
+            if let ty::Dynamic(traits, _, _) = self_ty.kind() {
+                for t in traits.iter() {
+                    if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
+                        flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
+                    }
+                }
+            }
+
+            // `&[{integral}]` - `FromIterator` needs that.
+            if let ty::Ref(_, ref_ty, rustc_ast::Mutability::Not) = self_ty.kind()
+                && let ty::Slice(sty) = ref_ty.kind()
+                && sty.is_integral()
+            {
+                flags.push((sym::_Self, Some("&[{integral}]".to_owned())));
+            }
+        }));
+
+        if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
+            command.evaluate(self.tcx, trait_ref, &flags)
+        } else {
+            OnUnimplementedNote::default()
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct OnUnimplementedFormatString {
+    symbol: Symbol,
+    span: Span,
+    is_diagnostic_namespace_variant: bool,
+}
+
+#[derive(Debug)]
+pub struct OnUnimplementedDirective {
+    pub condition: Option<MetaItem>,
+    pub subcommands: Vec<OnUnimplementedDirective>,
+    pub message: Option<OnUnimplementedFormatString>,
+    pub label: Option<OnUnimplementedFormatString>,
+    pub notes: Vec<OnUnimplementedFormatString>,
+    pub parent_label: Option<OnUnimplementedFormatString>,
+    pub append_const_msg: Option<AppendConstMessage>,
+}
+
+/// For the `#[rustc_on_unimplemented]` attribute
+#[derive(Default)]
+pub struct OnUnimplementedNote {
+    pub message: Option<String>,
+    pub label: Option<String>,
+    pub notes: Vec<String>,
+    pub parent_label: Option<String>,
+    // If none, should fall back to a generic message
+    pub append_const_msg: Option<AppendConstMessage>,
+}
+
+/// Append a message for `~const Trait` errors.
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
+pub enum AppendConstMessage {
+    #[default]
+    Default,
+    Custom(Symbol, Span),
+}
+
+#[derive(LintDiagnostic)]
+#[diag(trait_selection_malformed_on_unimplemented_attr)]
+#[help]
+pub struct MalformedOnUnimplementedAttrLint {
+    #[label]
+    pub span: Span,
+}
+
+impl MalformedOnUnimplementedAttrLint {
+    fn new(span: Span) -> Self {
+        Self { span }
+    }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
+#[help]
+pub struct MissingOptionsForOnUnimplementedAttr;
+
+#[derive(LintDiagnostic)]
+#[diag(trait_selection_ignored_diagnostic_option)]
+pub struct IgnoredDiagnosticOption {
+    pub option_name: &'static str,
+    #[label]
+    pub span: Span,
+    #[label(trait_selection_other_label)]
+    pub prev_span: Span,
+}
+
+impl IgnoredDiagnosticOption {
+    fn maybe_emit_warning<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        item_def_id: DefId,
+        new: Option<Span>,
+        old: Option<Span>,
+        option_name: &'static str,
+    ) {
+        if let (Some(new_item), Some(old_item)) = (new, old) {
+            tcx.emit_spanned_lint(
+                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                tcx.local_def_id_to_hir_id(item_def_id.expect_local()),
+                new_item,
+                IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name },
+            );
+        }
+    }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
+#[help]
+pub struct UnknownFormatParameterForOnUnimplementedAttr {
+    argument_name: Symbol,
+    trait_name: Symbol,
+}
+
+impl<'tcx> OnUnimplementedDirective {
+    fn parse(
+        tcx: TyCtxt<'tcx>,
+        item_def_id: DefId,
+        items: &[NestedMetaItem],
+        span: Span,
+        is_root: bool,
+        is_diagnostic_namespace_variant: bool,
+    ) -> Result<Option<Self>, ErrorGuaranteed> {
+        let mut errored = None;
+        let mut item_iter = items.iter();
+
+        let parse_value = |value_str, value_span| {
+            OnUnimplementedFormatString::try_parse(
+                tcx,
+                item_def_id,
+                value_str,
+                value_span,
+                is_diagnostic_namespace_variant,
+            )
+            .map(Some)
+        };
+
+        let condition = if is_root {
+            None
+        } else {
+            let cond = item_iter
+                .next()
+                .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))?
+                .meta_item()
+                .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
+            attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
+                if let Some(value) = cfg.value
+                    && let Err(guar) = parse_value(value, cfg.span)
+                {
+                    errored = Some(guar);
+                }
+                true
+            });
+            Some(cond.clone())
+        };
+
+        let mut message = None;
+        let mut label = None;
+        let mut notes = Vec::new();
+        let mut parent_label = None;
+        let mut subcommands = vec![];
+        let mut append_const_msg = None;
+
+        for item in item_iter {
+            if item.has_name(sym::message) && message.is_none() {
+                if let Some(message_) = item.value_str() {
+                    message = parse_value(message_, item.span())?;
+                    continue;
+                }
+            } else if item.has_name(sym::label) && label.is_none() {
+                if let Some(label_) = item.value_str() {
+                    label = parse_value(label_, item.span())?;
+                    continue;
+                }
+            } else if item.has_name(sym::note) {
+                if let Some(note_) = item.value_str() {
+                    if let Some(note) = parse_value(note_, item.span())? {
+                        notes.push(note);
+                        continue;
+                    }
+                }
+            } else if item.has_name(sym::parent_label)
+                && parent_label.is_none()
+                && !is_diagnostic_namespace_variant
+            {
+                if let Some(parent_label_) = item.value_str() {
+                    parent_label = parse_value(parent_label_, item.span())?;
+                    continue;
+                }
+            } else if item.has_name(sym::on)
+                && is_root
+                && message.is_none()
+                && label.is_none()
+                && notes.is_empty()
+                && !is_diagnostic_namespace_variant
+            // FIXME(diagnostic_namespace): disallow filters for now
+            {
+                if let Some(items) = item.meta_item_list() {
+                    match Self::parse(
+                        tcx,
+                        item_def_id,
+                        items,
+                        item.span(),
+                        false,
+                        is_diagnostic_namespace_variant,
+                    ) {
+                        Ok(Some(subcommand)) => subcommands.push(subcommand),
+                        Ok(None) => bug!(
+                            "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false"
+                        ),
+                        Err(reported) => errored = Some(reported),
+                    };
+                    continue;
+                }
+            } else if item.has_name(sym::append_const_msg)
+                && append_const_msg.is_none()
+                && !is_diagnostic_namespace_variant
+            {
+                if let Some(msg) = item.value_str() {
+                    append_const_msg = Some(AppendConstMessage::Custom(msg, item.span()));
+                    continue;
+                } else if item.is_word() {
+                    append_const_msg = Some(AppendConstMessage::Default);
+                    continue;
+                }
+            }
+
+            if is_diagnostic_namespace_variant {
+                tcx.emit_spanned_lint(
+                    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                    tcx.local_def_id_to_hir_id(item_def_id.expect_local()),
+                    vec![item.span()],
+                    MalformedOnUnimplementedAttrLint::new(item.span()),
+                );
+            } else {
+                // nothing found
+                tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() });
+            }
+        }
+
+        if let Some(reported) = errored {
+            if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) }
+        } else {
+            Ok(Some(OnUnimplementedDirective {
+                condition,
+                subcommands,
+                message,
+                label,
+                notes,
+                parent_label,
+                append_const_msg,
+            }))
+        }
+    }
+
+    pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
+        if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
+            return Self::parse_attribute(attr, false, tcx, item_def_id);
+        } else if tcx.features().diagnostic_namespace {
+            tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
+                .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
+                .try_fold(None, |aggr: Option<Self>, directive| {
+                    let directive = directive?;
+                    if let Some(aggr) = aggr {
+                        let mut subcommands = aggr.subcommands;
+                        subcommands.extend(directive.subcommands);
+                        let mut notes = aggr.notes;
+                        notes.extend(directive.notes);
+                        IgnoredDiagnosticOption::maybe_emit_warning(
+                            tcx,
+                            item_def_id,
+                            directive.message.as_ref().map(|f| f.span),
+                            aggr.message.as_ref().map(|f| f.span),
+                            "message",
+                        );
+                        IgnoredDiagnosticOption::maybe_emit_warning(
+                            tcx,
+                            item_def_id,
+                            directive.label.as_ref().map(|f| f.span),
+                            aggr.label.as_ref().map(|f| f.span),
+                            "label",
+                        );
+                        IgnoredDiagnosticOption::maybe_emit_warning(
+                            tcx,
+                            item_def_id,
+                            directive.condition.as_ref().map(|i| i.span),
+                            aggr.condition.as_ref().map(|i| i.span),
+                            "condition",
+                        );
+                        IgnoredDiagnosticOption::maybe_emit_warning(
+                            tcx,
+                            item_def_id,
+                            directive.parent_label.as_ref().map(|f| f.span),
+                            aggr.parent_label.as_ref().map(|f| f.span),
+                            "parent_label",
+                        );
+                        IgnoredDiagnosticOption::maybe_emit_warning(
+                            tcx,
+                            item_def_id,
+                            directive.append_const_msg.as_ref().and_then(|c| {
+                                if let AppendConstMessage::Custom(_, s) = c {
+                                    Some(*s)
+                                } else {
+                                    None
+                                }
+                            }),
+                            aggr.append_const_msg.as_ref().and_then(|c| {
+                                if let AppendConstMessage::Custom(_, s) = c {
+                                    Some(*s)
+                                } else {
+                                    None
+                                }
+                            }),
+                            "append_const_msg",
+                        );
+
+                        Ok(Some(Self {
+                            condition: aggr.condition.or(directive.condition),
+                            subcommands,
+                            message: aggr.message.or(directive.message),
+                            label: aggr.label.or(directive.label),
+                            notes,
+                            parent_label: aggr.parent_label.or(directive.parent_label),
+                            append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
+                        }))
+                    } else {
+                        Ok(Some(directive))
+                    }
+                })
+        } else {
+            Ok(None)
+        }
+    }
+
+    fn parse_attribute(
+        attr: &Attribute,
+        is_diagnostic_namespace_variant: bool,
+        tcx: TyCtxt<'tcx>,
+        item_def_id: DefId,
+    ) -> Result<Option<Self>, ErrorGuaranteed> {
+        let result = if let Some(items) = attr.meta_item_list() {
+            Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
+        } else if let Some(value) = attr.value_str() {
+            if !is_diagnostic_namespace_variant {
+                Ok(Some(OnUnimplementedDirective {
+                    condition: None,
+                    message: None,
+                    subcommands: vec![],
+                    label: Some(OnUnimplementedFormatString::try_parse(
+                        tcx,
+                        item_def_id,
+                        value,
+                        attr.span,
+                        is_diagnostic_namespace_variant,
+                    )?),
+                    notes: Vec::new(),
+                    parent_label: None,
+                    append_const_msg: None,
+                }))
+            } else {
+                let item = attr.get_normal_item();
+                let report_span = match &item.args {
+                    AttrArgs::Empty => item.path.span,
+                    AttrArgs::Delimited(args) => args.dspan.entire(),
+                    AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => eq_span.to(expr.span),
+                    AttrArgs::Eq(span, AttrArgsEq::Hir(expr)) => span.to(expr.span),
+                };
+
+                tcx.emit_spanned_lint(
+                    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                    tcx.local_def_id_to_hir_id(item_def_id.expect_local()),
+                    report_span,
+                    MalformedOnUnimplementedAttrLint::new(report_span),
+                );
+                Ok(None)
+            }
+        } else if is_diagnostic_namespace_variant {
+            match &attr.kind {
+                AttrKind::Normal(p) if !matches!(p.item.args, AttrArgs::Empty) => {
+                    tcx.emit_spanned_lint(
+                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                        tcx.local_def_id_to_hir_id(item_def_id.expect_local()),
+                        attr.span,
+                        MalformedOnUnimplementedAttrLint::new(attr.span),
+                    );
+                }
+                _ => tcx.emit_spanned_lint(
+                    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                    tcx.local_def_id_to_hir_id(item_def_id.expect_local()),
+                    attr.span,
+                    MissingOptionsForOnUnimplementedAttr,
+                ),
+            };
+
+            Ok(None)
+        } else {
+            let reported = tcx
+                .dcx()
+                .span_delayed_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
+            return Err(reported);
+        };
+        debug!("of_item({:?}) = {:?}", item_def_id, result);
+        result
+    }
+
+    pub fn evaluate(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        trait_ref: ty::TraitRef<'tcx>,
+        options: &[(Symbol, Option<String>)],
+    ) -> OnUnimplementedNote {
+        let mut message = None;
+        let mut label = None;
+        let mut notes = Vec::new();
+        let mut parent_label = None;
+        let mut append_const_msg = None;
+        info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
+
+        let options_map: FxHashMap<Symbol, String> =
+            options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect();
+
+        for command in self.subcommands.iter().chain(Some(self)).rev() {
+            if let Some(ref condition) = command.condition
+                && !attr::eval_condition(
+                    condition,
+                    &tcx.sess.parse_sess,
+                    Some(tcx.features()),
+                    &mut |cfg| {
+                        let value = cfg.value.map(|v| {
+                            // `with_no_visible_paths` is also used when generating the options,
+                            // so we need to match it here.
+                            ty::print::with_no_visible_paths!(
+                                OnUnimplementedFormatString {
+                                    symbol: v,
+                                    span: cfg.span,
+                                    is_diagnostic_namespace_variant: false
+                                }
+                                .format(
+                                    tcx,
+                                    trait_ref,
+                                    &options_map
+                                )
+                            )
+                        });
+
+                        options.contains(&(cfg.name, value))
+                    },
+                )
+            {
+                debug!("evaluate: skipping {:?} due to condition", command);
+                continue;
+            }
+            debug!("evaluate: {:?} succeeded", command);
+            if let Some(ref message_) = command.message {
+                message = Some(message_.clone());
+            }
+
+            if let Some(ref label_) = command.label {
+                label = Some(label_.clone());
+            }
+
+            notes.extend(command.notes.clone());
+
+            if let Some(ref parent_label_) = command.parent_label {
+                parent_label = Some(parent_label_.clone());
+            }
+
+            append_const_msg = command.append_const_msg;
+        }
+
+        OnUnimplementedNote {
+            label: label.map(|l| l.format(tcx, trait_ref, &options_map)),
+            message: message.map(|m| m.format(tcx, trait_ref, &options_map)),
+            notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, &options_map)).collect(),
+            parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)),
+            append_const_msg,
+        }
+    }
+}
+
+impl<'tcx> OnUnimplementedFormatString {
+    fn try_parse(
+        tcx: TyCtxt<'tcx>,
+        item_def_id: DefId,
+        from: Symbol,
+        value_span: Span,
+        is_diagnostic_namespace_variant: bool,
+    ) -> Result<Self, ErrorGuaranteed> {
+        let result = OnUnimplementedFormatString {
+            symbol: from,
+            span: value_span,
+            is_diagnostic_namespace_variant,
+        };
+        result.verify(tcx, item_def_id)?;
+        Ok(result)
+    }
+
+    fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
+        let trait_def_id = if tcx.is_trait(item_def_id) {
+            item_def_id
+        } else {
+            tcx.trait_id_of_impl(item_def_id)
+                .expect("expected `on_unimplemented` to correspond to a trait")
+        };
+        let trait_name = tcx.item_name(trait_def_id);
+        let generics = tcx.generics_of(item_def_id);
+        let s = self.symbol.as_str();
+        let parser = Parser::new(s, None, None, false, ParseMode::Format);
+        let mut result = Ok(());
+        for token in parser {
+            match token {
+                Piece::String(_) => (), // Normal string, no need to check it
+                Piece::NextArgument(a) => match a.position {
+                    Position::ArgumentNamed(s) => {
+                        match Symbol::intern(s) {
+                            // `{ThisTraitsName}` is allowed
+                            s if s == trait_name && !self.is_diagnostic_namespace_variant => (),
+                            s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
+                                && !self.is_diagnostic_namespace_variant =>
+                            {
+                                ()
+                            }
+                            // So is `{A}` if A is a type parameter
+                            s if generics.params.iter().any(|param| param.name == s) => (),
+                            s => {
+                                if self.is_diagnostic_namespace_variant {
+                                    tcx.emit_spanned_lint(
+                                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                                        tcx.local_def_id_to_hir_id(item_def_id.expect_local()),
+                                        self.span,
+                                        UnknownFormatParameterForOnUnimplementedAttr {
+                                            argument_name: s,
+                                            trait_name,
+                                        },
+                                    );
+                                } else {
+                                    result = Err(struct_span_err!(
+                                        tcx.dcx(),
+                                        self.span,
+                                        E0230,
+                                        "there is no parameter `{}` on {}",
+                                        s,
+                                        if trait_def_id == item_def_id {
+                                            format!("trait `{trait_name}`")
+                                        } else {
+                                            "impl".to_string()
+                                        }
+                                    )
+                                    .emit());
+                                }
+                            }
+                        }
+                    }
+                    // `{:1}` and `{}` are not to be used
+                    Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
+                        let reported = struct_span_err!(
+                            tcx.dcx(),
+                            self.span,
+                            E0231,
+                            "only named substitution parameters are allowed"
+                        )
+                        .emit();
+                        result = Err(reported);
+                    }
+                },
+            }
+        }
+
+        result
+    }
+
+    pub fn format(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        trait_ref: ty::TraitRef<'tcx>,
+        options: &FxHashMap<Symbol, String>,
+    ) -> String {
+        let name = tcx.item_name(trait_ref.def_id);
+        let trait_str = tcx.def_path_str(trait_ref.def_id);
+        let generics = tcx.generics_of(trait_ref.def_id);
+        let generic_map = generics
+            .params
+            .iter()
+            .filter_map(|param| {
+                let value = match param.kind {
+                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
+                        trait_ref.args[param.index as usize].to_string()
+                    }
+                    GenericParamDefKind::Lifetime => return None,
+                };
+                let name = param.name;
+                Some((name, value))
+            })
+            .collect::<FxHashMap<Symbol, String>>();
+        let empty_string = String::new();
+
+        let s = self.symbol.as_str();
+        let parser = Parser::new(s, None, None, false, ParseMode::Format);
+        let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
+        parser
+            .map(|p| match p {
+                Piece::String(s) => s.to_owned(),
+                Piece::NextArgument(a) => match a.position {
+                    Position::ArgumentNamed(arg) => {
+                        let s = Symbol::intern(arg);
+                        match generic_map.get(&s) {
+                            Some(val) => val.to_string(),
+                            None if self.is_diagnostic_namespace_variant => {
+                                format!("{{{arg}}}")
+                            }
+                            None if s == name => trait_str.clone(),
+                            None => {
+                                if let Some(val) = options.get(&s) {
+                                    val.clone()
+                                } else if s == sym::from_desugaring {
+                                    // don't break messages using these two arguments incorrectly
+                                    String::new()
+                                } else if s == sym::ItemContext
+                                    && !self.is_diagnostic_namespace_variant
+                                {
+                                    item_context.clone()
+                                } else if s == sym::integral {
+                                    String::from("{integral}")
+                                } else if s == sym::integer_ {
+                                    String::from("{integer}")
+                                } else if s == sym::float {
+                                    String::from("{float}")
+                                } else {
+                                    bug!(
+                                        "broken on_unimplemented {:?} for {:?}: \
+                                      no argument matching {:?}",
+                                        self.symbol,
+                                        trait_ref,
+                                        s
+                                    )
+                                }
+                            }
+                        }
+                    }
+                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
+                },
+            })
+            .collect()
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
new file mode 100644
index 00000000000..f63314081d6
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -0,0 +1,5020 @@
+// ignore-tidy-filelength
+
+use super::{
+    DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode,
+    PredicateObligation,
+};
+
+use crate::errors;
+use crate::infer::InferCtxt;
+use crate::traits::{NormalizeExt, ObligationCtxt};
+
+use hir::def::CtorOf;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_errors::{
+    error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
+    MultiSpan, Style, SuggestionStyle,
+};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::is_range_literal;
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Node};
+use rustc_hir::{Expr, HirId};
+use rustc_infer::infer::error_reporting::TypeErrCtxt;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
+use rustc_middle::hir::map;
+use rustc_middle::traits::IsConstable;
+use rustc_middle::ty::error::TypeError::{self, Sorts};
+use rustc_middle::ty::{
+    self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs,
+    InferTy, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder,
+    TypeSuperFoldable, TypeVisitableExt, TypeckResults,
+};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
+use rustc_target::spec::abi;
+use std::borrow::Cow;
+use std::iter;
+
+use crate::infer::InferCtxtExt as _;
+use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
+
+use itertools::EitherOrBoth;
+use itertools::Itertools;
+
+#[derive(Debug)]
+pub enum CoroutineInteriorOrUpvar {
+    // span of interior type
+    Interior(Span, Option<(Span, Option<Span>)>),
+    // span of upvar
+    Upvar(Span),
+}
+
+// This type provides a uniform interface to retrieve data on coroutines, whether it originated from
+// the local crate being compiled or from a foreign crate.
+#[derive(Debug)]
+struct CoroutineData<'tcx, 'a>(&'a TypeckResults<'tcx>);
+
+impl<'tcx, 'a> CoroutineData<'tcx, 'a> {
+    /// Try to get information about variables captured by the coroutine that matches a type we are
+    /// looking for with `ty_matches` function. We uses it to find upvar which causes a failure to
+    /// meet an obligation
+    fn try_get_upvar_span<F>(
+        &self,
+        infer_context: &InferCtxt<'tcx>,
+        coroutine_did: DefId,
+        ty_matches: F,
+    ) -> Option<CoroutineInteriorOrUpvar>
+    where
+        F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool,
+    {
+        infer_context.tcx.upvars_mentioned(coroutine_did).and_then(|upvars| {
+            upvars.iter().find_map(|(upvar_id, upvar)| {
+                let upvar_ty = self.0.node_type(*upvar_id);
+                let upvar_ty = infer_context.resolve_vars_if_possible(upvar_ty);
+                ty_matches(ty::Binder::dummy(upvar_ty))
+                    .then(|| CoroutineInteriorOrUpvar::Upvar(upvar.span))
+            })
+        })
+    }
+
+    /// Try to get the span of a type being awaited on that matches the type we are looking with the
+    /// `ty_matches` function. We uses it to find awaited type which causes a failure to meet an
+    /// obligation
+    fn get_from_await_ty<F>(
+        &self,
+        visitor: AwaitsVisitor,
+        hir: map::Map<'tcx>,
+        ty_matches: F,
+    ) -> Option<Span>
+    where
+        F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool,
+    {
+        visitor
+            .awaits
+            .into_iter()
+            .map(|id| hir.expect_expr(id))
+            .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(await_expr))))
+            .map(|expr| expr.span)
+    }
+}
+
+// This trait is public to expose the diagnostics methods to clippy.
+pub trait TypeErrCtxtExt<'tcx> {
+    fn suggest_restricting_param_bound(
+        &self,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+        associated_item: Option<(&'static str, Ty<'tcx>)>,
+        body_id: LocalDefId,
+    );
+
+    fn suggest_dereferences(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool;
+
+    fn get_closure_name(
+        &self,
+        def_id: DefId,
+        err: &mut Diagnostic,
+        msg: Cow<'static, str>,
+    ) -> Option<Symbol>;
+
+    fn suggest_fn_call(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool;
+
+    fn check_for_binding_assigned_block_without_tail_expression(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    );
+
+    fn suggest_add_clone_to_arg(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool;
+
+    fn extract_callable_info(
+        &self,
+        body_id: LocalDefId,
+        param_env: ty::ParamEnv<'tcx>,
+        found: Ty<'tcx>,
+    ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)>;
+
+    fn suggest_add_reference_to_arg(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+        has_custom_message: bool,
+    ) -> bool;
+
+    fn suggest_borrowing_for_object_cast(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+        self_ty: Ty<'tcx>,
+        object_ty: Ty<'tcx>,
+    );
+
+    fn suggest_remove_reference(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool;
+
+    fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic);
+
+    fn suggest_change_mut(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    );
+
+    fn suggest_semicolon_removal(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        span: Span,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool;
+
+    fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span>;
+
+    fn suggest_impl_trait(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool;
+
+    fn point_at_returns_when_relevant(
+        &self,
+        err: &mut DiagnosticBuilder<'tcx>,
+        obligation: &PredicateObligation<'tcx>,
+    );
+
+    fn report_closure_arg_mismatch(
+        &self,
+        span: Span,
+        found_span: Option<Span>,
+        found: ty::PolyTraitRef<'tcx>,
+        expected: ty::PolyTraitRef<'tcx>,
+        cause: &ObligationCauseCode<'tcx>,
+        found_node: Option<Node<'_>>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> DiagnosticBuilder<'tcx>;
+
+    fn note_conflicting_fn_args(
+        &self,
+        err: &mut Diagnostic,
+        cause: &ObligationCauseCode<'tcx>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    );
+
+    fn note_conflicting_closure_bounds(
+        &self,
+        cause: &ObligationCauseCode<'tcx>,
+        err: &mut DiagnosticBuilder<'tcx>,
+    );
+
+    fn suggest_fully_qualified_path(
+        &self,
+        err: &mut Diagnostic,
+        item_def_id: DefId,
+        span: Span,
+        trait_ref: DefId,
+    );
+
+    fn maybe_note_obligation_cause_for_async_await(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> bool;
+
+    fn note_obligation_cause_for_async_await(
+        &self,
+        err: &mut Diagnostic,
+        interior_or_upvar_span: CoroutineInteriorOrUpvar,
+        is_async: bool,
+        outer_coroutine: Option<DefId>,
+        trait_pred: ty::TraitPredicate<'tcx>,
+        target_ty: Ty<'tcx>,
+        obligation: &PredicateObligation<'tcx>,
+        next_code: Option<&ObligationCauseCode<'tcx>>,
+    );
+
+    fn note_obligation_cause_code<T>(
+        &self,
+        body_id: LocalDefId,
+        err: &mut Diagnostic,
+        predicate: T,
+        param_env: ty::ParamEnv<'tcx>,
+        cause_code: &ObligationCauseCode<'tcx>,
+        obligated_types: &mut Vec<Ty<'tcx>>,
+        seen_requirements: &mut FxHashSet<DefId>,
+    ) where
+        T: ToPredicate<'tcx>;
+
+    /// Suggest to await before try: future? => future.await?
+    fn suggest_await_before_try(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+        span: Span,
+    );
+
+    fn suggest_floating_point_literal(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_ref: &ty::PolyTraitRef<'tcx>,
+    );
+
+    fn suggest_derive(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    );
+
+    fn suggest_dereferencing_index(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    );
+
+    fn suggest_option_method_if_applicable(
+        &self,
+        failed_pred: ty::Predicate<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+    );
+
+    fn note_function_argument_obligation(
+        &self,
+        body_id: LocalDefId,
+        err: &mut Diagnostic,
+        arg_hir_id: HirId,
+        parent_code: &ObligationCauseCode<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        predicate: ty::Predicate<'tcx>,
+        call_hir_id: HirId,
+    );
+
+    fn look_for_iterator_item_mistakes(
+        &self,
+        assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
+        typeck_results: &TypeckResults<'tcx>,
+        type_diffs: &[TypeError<'tcx>],
+        param_env: ty::ParamEnv<'tcx>,
+        path_segment: &hir::PathSegment<'_>,
+        args: &[hir::Expr<'_>],
+        err: &mut Diagnostic,
+    );
+
+    fn point_at_chain(
+        &self,
+        expr: &hir::Expr<'_>,
+        typeck_results: &TypeckResults<'tcx>,
+        type_diffs: Vec<TypeError<'tcx>>,
+        param_env: ty::ParamEnv<'tcx>,
+        err: &mut Diagnostic,
+    );
+
+    fn probe_assoc_types_at_expr(
+        &self,
+        type_diffs: &[TypeError<'tcx>],
+        span: Span,
+        prev_ty: Ty<'tcx>,
+        body_id: hir::HirId,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>;
+
+    fn suggest_convert_to_slice(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        candidate_impls: &[ImplCandidate<'tcx>],
+        span: Span,
+    );
+
+    fn explain_hrtb_projection(
+        &self,
+        diag: &mut Diagnostic,
+        pred: ty::PolyTraitPredicate<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: &ObligationCause<'tcx>,
+    );
+
+    fn suggest_desugaring_async_fn_in_trait(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+    );
+}
+
+fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
+    (
+        generics.tail_span_for_predicate_suggestion(),
+        format!("{} {}", generics.add_where_or_trailing_comma(), pred),
+    )
+}
+
+/// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but
+/// it can also be an `impl Trait` param that needs to be decomposed to a type
+/// param for cleaner code.
+pub fn suggest_restriction<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    item_id: LocalDefId,
+    hir_generics: &hir::Generics<'tcx>,
+    msg: &str,
+    err: &mut Diagnostic,
+    fn_sig: Option<&hir::FnSig<'_>>,
+    projection: Option<&ty::AliasTy<'_>>,
+    trait_pred: ty::PolyTraitPredicate<'tcx>,
+    // When we are dealing with a trait, `super_traits` will be `Some`:
+    // Given `trait T: A + B + C {}`
+    //              -  ^^^^^^^^^ GenericBounds
+    //              |
+    //              &Ident
+    super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>,
+) {
+    if hir_generics.where_clause_span.from_expansion()
+        || hir_generics.where_clause_span.desugaring_kind().is_some()
+        || projection.is_some_and(|projection| tcx.is_impl_trait_in_trait(projection.def_id))
+    {
+        return;
+    }
+    let generics = tcx.generics_of(item_id);
+    // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...
+    if let Some((param, bound_str, fn_sig)) =
+        fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind() {
+            // Shenanigans to get the `Trait` from the `impl Trait`.
+            ty::Param(param) => {
+                let param_def = generics.type_param(param, tcx);
+                if param_def.kind.is_synthetic() {
+                    let bound_str =
+                        param_def.name.as_str().strip_prefix("impl ")?.trim_start().to_string();
+                    return Some((param_def, bound_str, sig));
+                }
+                None
+            }
+            _ => None,
+        })
+    {
+        let type_param_name = hir_generics.params.next_type_param_name(Some(&bound_str));
+        let trait_pred = trait_pred.fold_with(&mut ReplaceImplTraitFolder {
+            tcx,
+            param,
+            replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name))
+                .to_ty(tcx),
+        });
+        if !trait_pred.is_suggestable(tcx, false) {
+            return;
+        }
+        // We know we have an `impl Trait` that doesn't satisfy a required projection.
+
+        // Find all of the occurrences of `impl Trait` for `Trait` in the function arguments'
+        // types. There should be at least one, but there might be *more* than one. In that
+        // case we could just ignore it and try to identify which one needs the restriction,
+        // but instead we choose to suggest replacing all instances of `impl Trait` with `T`
+        // where `T: Trait`.
+        let mut ty_spans = vec![];
+        for input in fn_sig.decl.inputs {
+            ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id }
+                .visit_ty(input);
+        }
+        // The type param `T: Trait` we will suggest to introduce.
+        let type_param = format!("{type_param_name}: {bound_str}");
+
+        let mut sugg = vec![
+            if let Some(span) = hir_generics.span_for_param_suggestion() {
+                (span, format!(", {type_param}"))
+            } else {
+                (hir_generics.span, format!("<{type_param}>"))
+            },
+            // `fn foo(t: impl Trait)`
+            //                       ^ suggest `where <T as Trait>::A: Bound`
+            predicate_constraint(hir_generics, trait_pred.to_predicate(tcx)),
+        ];
+        sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string())));
+
+        // Suggest `fn foo<T: Trait>(t: T) where <T as Trait>::A: Bound`.
+        // FIXME: once `#![feature(associated_type_bounds)]` is stabilized, we should suggest
+        // `fn foo(t: impl Trait<A: Bound>)` instead.
+        err.multipart_suggestion(
+            "introduce a type parameter with a trait bound instead of using `impl Trait`",
+            sugg,
+            Applicability::MaybeIncorrect,
+        );
+    } else {
+        if !trait_pred.is_suggestable(tcx, false) {
+            return;
+        }
+        // Trivial case: `T` needs an extra bound: `T: Bound`.
+        let (sp, suggestion) = match (
+            hir_generics
+                .params
+                .iter()
+                .find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })),
+            super_traits,
+        ) {
+            (_, None) => predicate_constraint(hir_generics, trait_pred.to_predicate(tcx)),
+            (None, Some((ident, []))) => (
+                ident.span.shrink_to_hi(),
+                format!(": {}", trait_pred.print_modifiers_and_trait_path()),
+            ),
+            (_, Some((_, [.., bounds]))) => (
+                bounds.span().shrink_to_hi(),
+                format!(" + {}", trait_pred.print_modifiers_and_trait_path()),
+            ),
+            (Some(_), Some((_, []))) => (
+                hir_generics.span.shrink_to_hi(),
+                format!(": {}", trait_pred.print_modifiers_and_trait_path()),
+            ),
+        };
+
+        err.span_suggestion_verbose(
+            sp,
+            format!("consider further restricting {msg}"),
+            suggestion,
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
+impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
+    fn suggest_restricting_param_bound(
+        &self,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+        associated_ty: Option<(&'static str, Ty<'tcx>)>,
+        mut body_id: LocalDefId,
+    ) {
+        if trait_pred.skip_binder().polarity == ty::ImplPolarity::Negative {
+            return;
+        }
+
+        let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
+
+        let self_ty = trait_pred.skip_binder().self_ty();
+        let (param_ty, projection) = match self_ty.kind() {
+            ty::Param(_) => (true, None),
+            ty::Alias(ty::Projection, projection) => (false, Some(projection)),
+            _ => (false, None),
+        };
+
+        // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
+        //        don't suggest `T: Sized + ?Sized`.
+        while let Some(node) = self.tcx.opt_hir_node_by_def_id(body_id) {
+            match node {
+                hir::Node::Item(hir::Item {
+                    ident,
+                    kind: hir::ItemKind::Trait(_, _, generics, bounds, _),
+                    ..
+                }) if self_ty == self.tcx.types.self_param => {
+                    assert!(param_ty);
+                    // Restricting `Self` for a single method.
+                    suggest_restriction(
+                        self.tcx,
+                        body_id,
+                        generics,
+                        "`Self`",
+                        err,
+                        None,
+                        projection,
+                        trait_pred,
+                        Some((ident, bounds)),
+                    );
+                    return;
+                }
+
+                hir::Node::TraitItem(hir::TraitItem {
+                    generics,
+                    kind: hir::TraitItemKind::Fn(..),
+                    ..
+                }) if self_ty == self.tcx.types.self_param => {
+                    assert!(param_ty);
+                    // Restricting `Self` for a single method.
+                    suggest_restriction(
+                        self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred,
+                        None,
+                    );
+                    return;
+                }
+
+                hir::Node::TraitItem(hir::TraitItem {
+                    generics,
+                    kind: hir::TraitItemKind::Fn(fn_sig, ..),
+                    ..
+                })
+                | hir::Node::ImplItem(hir::ImplItem {
+                    generics,
+                    kind: hir::ImplItemKind::Fn(fn_sig, ..),
+                    ..
+                })
+                | hir::Node::Item(hir::Item {
+                    kind: hir::ItemKind::Fn(fn_sig, generics, _), ..
+                }) if projection.is_some() => {
+                    // Missing restriction on associated type of type parameter (unmet projection).
+                    suggest_restriction(
+                        self.tcx,
+                        body_id,
+                        generics,
+                        "the associated type",
+                        err,
+                        Some(fn_sig),
+                        projection,
+                        trait_pred,
+                        None,
+                    );
+                    return;
+                }
+                hir::Node::Item(hir::Item {
+                    kind:
+                        hir::ItemKind::Trait(_, _, generics, ..)
+                        | hir::ItemKind::Impl(hir::Impl { generics, .. }),
+                    ..
+                }) if projection.is_some() => {
+                    // Missing restriction on associated type of type parameter (unmet projection).
+                    suggest_restriction(
+                        self.tcx,
+                        body_id,
+                        generics,
+                        "the associated type",
+                        err,
+                        None,
+                        projection,
+                        trait_pred,
+                        None,
+                    );
+                    return;
+                }
+
+                hir::Node::Item(hir::Item {
+                    kind:
+                        hir::ItemKind::Struct(_, generics)
+                        | hir::ItemKind::Enum(_, generics)
+                        | hir::ItemKind::Union(_, generics)
+                        | hir::ItemKind::Trait(_, _, generics, ..)
+                        | hir::ItemKind::Impl(hir::Impl { generics, .. })
+                        | hir::ItemKind::Fn(_, generics, _)
+                        | hir::ItemKind::TyAlias(_, generics)
+                        | hir::ItemKind::Const(_, generics, _)
+                        | hir::ItemKind::TraitAlias(generics, _)
+                        | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
+                    ..
+                })
+                | hir::Node::TraitItem(hir::TraitItem { generics, .. })
+                | hir::Node::ImplItem(hir::ImplItem { generics, .. })
+                    if param_ty =>
+                {
+                    // We skip the 0'th subst (self) because we do not want
+                    // to consider the predicate as not suggestible if the
+                    // self type is an arg position `impl Trait` -- instead,
+                    // we handle that by adding ` + Bound` below.
+                    // FIXME(compiler-errors): It would be nice to do the same
+                    // this that we do in `suggest_restriction` and pull the
+                    // `impl Trait` into a new generic if it shows up somewhere
+                    // else in the predicate.
+                    if !trait_pred.skip_binder().trait_ref.args[1..]
+                        .iter()
+                        .all(|g| g.is_suggestable(self.tcx, false))
+                    {
+                        return;
+                    }
+                    // Missing generic type parameter bound.
+                    let param_name = self_ty.to_string();
+                    let mut constraint = with_no_trimmed_paths!(
+                        trait_pred.print_modifiers_and_trait_path().to_string()
+                    );
+
+                    if let Some((name, term)) = associated_ty {
+                        // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
+                        // That should be extracted into a helper function.
+                        if constraint.ends_with('>') {
+                            constraint = format!(
+                                "{}, {} = {}>",
+                                &constraint[..constraint.len() - 1],
+                                name,
+                                term
+                            );
+                        } else {
+                            constraint.push_str(&format!("<{name} = {term}>"));
+                        }
+                    }
+
+                    if suggest_constraining_type_param(
+                        self.tcx,
+                        generics,
+                        err,
+                        &param_name,
+                        &constraint,
+                        Some(trait_pred.def_id()),
+                        None,
+                    ) {
+                        return;
+                    }
+                }
+
+                hir::Node::Item(hir::Item {
+                    kind:
+                        hir::ItemKind::Struct(_, generics)
+                        | hir::ItemKind::Enum(_, generics)
+                        | hir::ItemKind::Union(_, generics)
+                        | hir::ItemKind::Trait(_, _, generics, ..)
+                        | hir::ItemKind::Impl(hir::Impl { generics, .. })
+                        | hir::ItemKind::Fn(_, generics, _)
+                        | hir::ItemKind::TyAlias(_, generics)
+                        | hir::ItemKind::Const(_, generics, _)
+                        | hir::ItemKind::TraitAlias(generics, _)
+                        | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
+                    ..
+                }) if !param_ty => {
+                    // Missing generic type parameter bound.
+                    if suggest_arbitrary_trait_bound(
+                        self.tcx,
+                        generics,
+                        err,
+                        trait_pred,
+                        associated_ty,
+                    ) {
+                        return;
+                    }
+                }
+                hir::Node::Crate(..) => return,
+
+                _ => {}
+            }
+            body_id = self.tcx.local_parent(body_id);
+        }
+    }
+
+    /// When after several dereferencing, the reference satisfies the trait
+    /// binding. This function provides dereference suggestion for this
+    /// specific situation.
+    fn suggest_dereferences(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool {
+        let mut code = obligation.cause.code();
+        if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, call_hir_id, .. } =
+            code
+            && let Some(typeck_results) = &self.typeck_results
+            && let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id)
+            && let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr)
+        {
+            // Suggest dereferencing the argument to a function/method call if possible
+
+            let mut real_trait_pred = trait_pred;
+            while let Some((parent_code, parent_trait_pred)) = code.parent() {
+                code = parent_code;
+                if let Some(parent_trait_pred) = parent_trait_pred {
+                    real_trait_pred = parent_trait_pred;
+                }
+
+                // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle
+                // `ReBound`, and we don't particularly care about the regions.
+                let real_ty =
+                    self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty());
+
+                if self.can_eq(obligation.param_env, real_ty, arg_ty)
+                    && let ty::Ref(region, base_ty, mutbl) = *real_ty.kind()
+                {
+                    let autoderef = (self.autoderef_steps)(base_ty);
+                    if let Some(steps) =
+                        autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| {
+                            // Re-add the `&`
+                            let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl });
+
+                            // Remapping bound vars here
+                            let real_trait_pred_and_ty = real_trait_pred
+                                .map_bound(|inner_trait_pred| (inner_trait_pred, ty));
+                            let obligation = self.mk_trait_obligation_with_new_self_ty(
+                                obligation.param_env,
+                                real_trait_pred_and_ty,
+                            );
+                            let may_hold = obligations
+                                .iter()
+                                .chain([&obligation])
+                                .all(|obligation| self.predicate_may_hold(obligation))
+                                .then_some(steps);
+
+                            may_hold
+                        })
+                    {
+                        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 {
+                                kind:
+                                    hir::ExprKind::AddrOf(
+                                        hir::BorrowKind::Ref,
+                                        hir::Mutability::Not,
+                                        expr,
+                                    ),
+                                ..
+                            })) = self.tcx.opt_hir_node(*arg_hir_id)
+                            {
+                                let derefs = "*".repeat(steps);
+                                err.span_suggestion_verbose(
+                                    expr.span.shrink_to_lo(),
+                                    "consider dereferencing here",
+                                    derefs,
+                                    Applicability::MachineApplicable,
+                                );
+                                return true;
+                            }
+                        }
+                    } else if real_trait_pred != trait_pred {
+                        // This branch addresses #87437.
+
+                        let span = obligation.cause.span;
+                        // Remapping bound vars here
+                        let real_trait_pred_and_base_ty = real_trait_pred
+                            .map_bound(|inner_trait_pred| (inner_trait_pred, base_ty));
+                        let obligation = self.mk_trait_obligation_with_new_self_ty(
+                            obligation.param_env,
+                            real_trait_pred_and_base_ty,
+                        );
+                        let sized_obligation = Obligation::new(
+                            self.tcx,
+                            obligation.cause.clone(),
+                            obligation.param_env,
+                            ty::TraitRef::from_lang_item(
+                                self.tcx,
+                                hir::LangItem::Sized,
+                                obligation.cause.span,
+                                [base_ty],
+                            ),
+                        );
+                        if self.predicate_may_hold(&obligation)
+                            && self.predicate_must_hold_modulo_regions(&sized_obligation)
+                        {
+                            let call_node = self.tcx.hir_node(*call_hir_id);
+                            let msg = "consider dereferencing here";
+                            let is_receiver = matches!(
+                                call_node,
+                                Node::Expr(hir::Expr {
+                                    kind: hir::ExprKind::MethodCall(_, receiver_expr, ..),
+                                    ..
+                                })
+                                if receiver_expr.hir_id == *arg_hir_id
+                            );
+                            if is_receiver {
+                                err.multipart_suggestion_verbose(
+                                    msg,
+                                    vec![
+                                        (span.shrink_to_lo(), "(*".to_string()),
+                                        (span.shrink_to_hi(), ")".to_string()),
+                                    ],
+                                    Applicability::MachineApplicable,
+                                )
+                            } else {
+                                err.span_suggestion_verbose(
+                                    span.shrink_to_lo(),
+                                    msg,
+                                    '*',
+                                    Applicability::MachineApplicable,
+                                )
+                            };
+                            return true;
+                        }
+                    }
+                }
+            }
+        } else if let (
+            ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id: Some(rhs_hir_id), .. },
+            predicate,
+        ) = code.peel_derives_with_predicate()
+            && let Some(typeck_results) = &self.typeck_results
+            && let hir::Node::Expr(lhs) = self.tcx.hir_node(*lhs_hir_id)
+            && let hir::Node::Expr(rhs) = self.tcx.hir_node(*rhs_hir_id)
+            && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs)
+        {
+            // Suggest dereferencing the LHS, RHS, or both terms of a binop if possible
+
+            let trait_pred = predicate.unwrap_or(trait_pred);
+            let lhs_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty());
+            let lhs_autoderef = (self.autoderef_steps)(lhs_ty);
+            let rhs_autoderef = (self.autoderef_steps)(rhs_ty);
+            let first_lhs = lhs_autoderef.first().unwrap().clone();
+            let first_rhs = rhs_autoderef.first().unwrap().clone();
+            let mut autoderefs = lhs_autoderef
+                .into_iter()
+                .enumerate()
+                .rev()
+                .zip_longest(rhs_autoderef.into_iter().enumerate().rev())
+                .map(|t| match t {
+                    EitherOrBoth::Both(a, b) => (a, b),
+                    EitherOrBoth::Left(a) => (a, (0, first_rhs.clone())),
+                    EitherOrBoth::Right(b) => ((0, first_lhs.clone()), b),
+                })
+                .rev();
+            if let Some((lsteps, rsteps)) =
+                autoderefs.find_map(|((lsteps, (l_ty, _)), (rsteps, (r_ty, _)))| {
+                    // Create a new predicate with the dereferenced LHS and RHS
+                    // We simultaneously dereference both sides rather than doing them
+                    // one at a time to account for cases such as &Box<T> == &&T
+                    let trait_pred_and_ty = trait_pred.map_bound(|inner| {
+                        (
+                            ty::TraitPredicate {
+                                trait_ref: ty::TraitRef::new(
+                                    self.tcx,
+                                    inner.trait_ref.def_id,
+                                    self.tcx.mk_args(
+                                        &[&[l_ty.into(), r_ty.into()], &inner.trait_ref.args[2..]]
+                                            .concat(),
+                                    ),
+                                ),
+                                ..inner
+                            },
+                            l_ty,
+                        )
+                    });
+                    let obligation = self.mk_trait_obligation_with_new_self_ty(
+                        obligation.param_env,
+                        trait_pred_and_ty,
+                    );
+                    self.predicate_may_hold(&obligation).then_some(match (lsteps, rsteps) {
+                        (_, 0) => (Some(lsteps), None),
+                        (0, _) => (None, Some(rsteps)),
+                        _ => (Some(lsteps), Some(rsteps)),
+                    })
+                })
+            {
+                let make_sugg = |mut expr: &Expr<'_>, mut steps| {
+                    let mut prefix_span = expr.span.shrink_to_lo();
+                    let mut msg = "consider dereferencing here";
+                    if let hir::ExprKind::AddrOf(_, _, inner) = expr.kind {
+                        msg = "consider removing the borrow and dereferencing instead";
+                        if let hir::ExprKind::AddrOf(..) = inner.kind {
+                            msg = "consider removing the borrows and dereferencing instead";
+                        }
+                    }
+                    while let hir::ExprKind::AddrOf(_, _, inner) = expr.kind
+                        && steps > 0
+                    {
+                        prefix_span = prefix_span.with_hi(inner.span.lo());
+                        expr = inner;
+                        steps -= 1;
+                    }
+                    // Empty suggestions with empty spans ICE with debug assertions
+                    if steps == 0 {
+                        return (
+                            msg.trim_end_matches(" and dereferencing instead"),
+                            vec![(prefix_span, String::new())],
+                        );
+                    }
+                    let derefs = "*".repeat(steps);
+                    let needs_parens = steps > 0
+                        && match expr.kind {
+                            hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
+                            _ if is_range_literal(expr) => true,
+                            _ => false,
+                        };
+                    let mut suggestion = if needs_parens {
+                        vec![
+                            (
+                                expr.span.with_lo(prefix_span.hi()).shrink_to_lo(),
+                                format!("{derefs}("),
+                            ),
+                            (expr.span.shrink_to_hi(), ")".to_string()),
+                        ]
+                    } else {
+                        vec![(
+                            expr.span.with_lo(prefix_span.hi()).shrink_to_lo(),
+                            format!("{derefs}"),
+                        )]
+                    };
+                    // Empty suggestions with empty spans ICE with debug assertions
+                    if !prefix_span.is_empty() {
+                        suggestion.push((prefix_span, String::new()));
+                    }
+                    (msg, suggestion)
+                };
+
+                if let Some(lsteps) = lsteps
+                    && let Some(rsteps) = rsteps
+                    && lsteps > 0
+                    && rsteps > 0
+                {
+                    let mut suggestion = make_sugg(lhs, lsteps).1;
+                    suggestion.append(&mut make_sugg(rhs, rsteps).1);
+                    err.multipart_suggestion_verbose(
+                        "consider dereferencing both sides of the expression",
+                        suggestion,
+                        Applicability::MachineApplicable,
+                    );
+                    return true;
+                } else if let Some(lsteps) = lsteps
+                    && lsteps > 0
+                {
+                    let (msg, suggestion) = make_sugg(lhs, lsteps);
+                    err.multipart_suggestion_verbose(
+                        msg,
+                        suggestion,
+                        Applicability::MachineApplicable,
+                    );
+                    return true;
+                } else if let Some(rsteps) = rsteps
+                    && rsteps > 0
+                {
+                    let (msg, suggestion) = make_sugg(rhs, rsteps);
+                    err.multipart_suggestion_verbose(
+                        msg,
+                        suggestion,
+                        Applicability::MachineApplicable,
+                    );
+                    return true;
+                }
+            }
+        }
+        false
+    }
+
+    /// Given a closure's `DefId`, return the given name of the closure.
+    ///
+    /// This doesn't account for reassignments, but it's only used for suggestions.
+    fn get_closure_name(
+        &self,
+        def_id: DefId,
+        err: &mut Diagnostic,
+        msg: Cow<'static, str>,
+    ) -> Option<Symbol> {
+        let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> {
+            // Get the local name of this closure. This can be inaccurate because
+            // of the possibility of reassignment, but this should be good enough.
+            match &kind {
+                hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => {
+                    Some(ident.name)
+                }
+                _ => {
+                    err.note(msg);
+                    None
+                }
+            }
+        };
+
+        let hir = self.tcx.hir();
+        let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?);
+        match hir.find_parent(hir_id) {
+            Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => {
+                get_name(err, &local.pat.kind)
+            }
+            // Different to previous arm because one is `&hir::Local` and the other
+            // is `P<hir::Local>`.
+            Some(hir::Node::Local(local)) => get_name(err, &local.pat.kind),
+            _ => None,
+        }
+    }
+
+    /// We tried to apply the bound to an `fn` or closure. Check whether calling it would
+    /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling
+    /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`.
+    fn suggest_fn_call(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool {
+        // It doesn't make sense to make this suggestion outside of typeck...
+        // (also autoderef will ICE...)
+        if self.typeck_results.is_none() {
+            return false;
+        }
+
+        if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) =
+            obligation.predicate.kind().skip_binder()
+            && Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
+        {
+            // Don't suggest calling to turn an unsized type into a sized type
+            return false;
+        }
+
+        let self_ty = self.instantiate_binder_with_fresh_vars(
+            DUMMY_SP,
+            BoundRegionConversionTime::FnCall,
+            trait_pred.self_ty(),
+        );
+
+        let Some((def_id_or_name, output, inputs)) =
+            self.extract_callable_info(obligation.cause.body_id, obligation.param_env, self_ty)
+        else {
+            return false;
+        };
+
+        // Remapping bound vars here
+        let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output));
+
+        let new_obligation =
+            self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);
+        if !self.predicate_must_hold_modulo_regions(&new_obligation) {
+            return false;
+        }
+
+        // Get the name of the callable and the arguments to be used in the suggestion.
+        let hir = self.tcx.hir();
+
+        let msg = match def_id_or_name {
+            DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
+                DefKind::Ctor(CtorOf::Struct, _) => {
+                    Cow::from("use parentheses to construct this tuple struct")
+                }
+                DefKind::Ctor(CtorOf::Variant, _) => {
+                    Cow::from("use parentheses to construct this tuple variant")
+                }
+                kind => Cow::from(format!(
+                    "use parentheses to call this {}",
+                    self.tcx.def_kind_descr(kind, def_id)
+                )),
+            },
+            DefIdOrName::Name(name) => Cow::from(format!("use parentheses to call this {name}")),
+        };
+
+        let args = inputs
+            .into_iter()
+            .map(|ty| {
+                if ty.is_suggestable(self.tcx, false) {
+                    format!("/* {ty} */")
+                } else {
+                    "/* value */".to_string()
+                }
+            })
+            .collect::<Vec<_>>()
+            .join(", ");
+
+        if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
+            && obligation.cause.span.can_be_used_for_suggestions()
+        {
+            // When the obligation error has been ensured to have been caused by
+            // an argument, the `obligation.cause.span` points at the expression
+            // of the argument, so we can provide a suggestion. Otherwise, we give
+            // a more general note.
+            err.span_suggestion_verbose(
+                obligation.cause.span.shrink_to_hi(),
+                msg,
+                format!("({args})"),
+                Applicability::HasPlaceholders,
+            );
+        } else if let DefIdOrName::DefId(def_id) = def_id_or_name {
+            let name = match hir.get_if_local(def_id) {
+                Some(hir::Node::Expr(hir::Expr {
+                    kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),
+                    ..
+                })) => {
+                    err.span_label(*fn_decl_span, "consider calling this closure");
+                    let Some(name) = self.get_closure_name(def_id, err, msg.clone()) else {
+                        return false;
+                    };
+                    name.to_string()
+                }
+                Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => {
+                    err.span_label(ident.span, "consider calling this function");
+                    ident.to_string()
+                }
+                Some(hir::Node::Ctor(..)) => {
+                    let name = self.tcx.def_path_str(def_id);
+                    err.span_label(
+                        self.tcx.def_span(def_id),
+                        format!("consider calling the constructor for `{name}`"),
+                    );
+                    name
+                }
+                _ => return false,
+            };
+            err.help(format!("{msg}: `{name}({args})`"));
+        }
+        true
+    }
+
+    fn check_for_binding_assigned_block_without_tail_expression(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) {
+        let mut span = obligation.cause.span;
+        while span.from_expansion() {
+            // Remove all the desugaring and macro contexts.
+            span.remove_mark();
+        }
+        let mut expr_finder = FindExprBySpan::new(span);
+        let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else {
+            return;
+        };
+        let body = self.tcx.hir().body(body_id);
+        expr_finder.visit_expr(body.value);
+        let Some(expr) = expr_finder.result else {
+            return;
+        };
+        let Some(typeck) = &self.typeck_results else {
+            return;
+        };
+        let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else {
+            return;
+        };
+        if !ty.is_unit() {
+            return;
+        };
+        let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {
+            return;
+        };
+        let Res::Local(hir_id) = path.res else {
+            return;
+        };
+        let Some(hir::Node::Pat(pat)) = self.tcx.opt_hir_node(hir_id) else {
+            return;
+        };
+        let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) =
+            self.tcx.hir().find_parent(pat.hir_id)
+        else {
+            return;
+        };
+        let hir::ExprKind::Block(block, None) = init.kind else {
+            return;
+        };
+        if block.expr.is_some() {
+            return;
+        }
+        let [.., stmt] = block.stmts else {
+            err.span_label(block.span, "this empty block is missing a tail expression");
+            return;
+        };
+        let hir::StmtKind::Semi(tail_expr) = stmt.kind else {
+            return;
+        };
+        let Some(ty) = typeck.expr_ty_opt(tail_expr) else {
+            err.span_label(block.span, "this block is missing a tail expression");
+            return;
+        };
+        let ty = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(ty));
+        let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, ty));
+
+        let new_obligation =
+            self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);
+        if self.predicate_must_hold_modulo_regions(&new_obligation) {
+            err.span_suggestion_short(
+                stmt.span.with_lo(tail_expr.span.hi()),
+                "remove this semicolon",
+                "",
+                Applicability::MachineApplicable,
+            );
+        } else {
+            err.span_label(block.span, "this block is missing a tail expression");
+        }
+    }
+
+    fn suggest_add_clone_to_arg(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        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 };
+
+        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)),
+        );
+
+        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;
+        }
+        false
+    }
+
+    /// Extracts information about a callable type for diagnostics. This is a
+    /// heuristic -- it doesn't necessarily mean that a type is always callable,
+    /// because the callable type must also be well-formed to be called.
+    fn extract_callable_info(
+        &self,
+        body_id: LocalDefId,
+        param_env: ty::ParamEnv<'tcx>,
+        found: Ty<'tcx>,
+    ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
+        // Autoderef is useful here because sometimes we box callables, etc.
+        let Some((def_id_or_name, output, inputs)) =
+            (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| {
+                match *found.kind() {
+                    ty::FnPtr(fn_sig) => Some((
+                        DefIdOrName::Name("function pointer"),
+                        fn_sig.output(),
+                        fn_sig.inputs(),
+                    )),
+                    ty::FnDef(def_id, _) => {
+                        let fn_sig = found.fn_sig(self.tcx);
+                        Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
+                    }
+                    ty::Closure(def_id, args) => {
+                        let fn_sig = args.as_closure().sig();
+                        Some((
+                            DefIdOrName::DefId(def_id),
+                            fn_sig.output(),
+                            fn_sig.inputs().map_bound(|inputs| &inputs[1..]),
+                        ))
+                    }
+                    ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
+                        self.tcx.item_bounds(def_id).instantiate(self.tcx, args).iter().find_map(
+                            |pred| {
+                                if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
+                        && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
+                        // args tuple will always be args[1]
+                        && let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind()
+                                {
+                                    Some((
+                                        DefIdOrName::DefId(def_id),
+                                        pred.kind().rebind(proj.term.ty().unwrap()),
+                                        pred.kind().rebind(args.as_slice()),
+                                    ))
+                                } else {
+                                    None
+                                }
+                            },
+                        )
+                    }
+                    ty::Dynamic(data, _, ty::Dyn) => {
+                        data.iter().find_map(|pred| {
+                            if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
+                        && Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
+                        // for existential projection, args are shifted over by 1
+                        && let ty::Tuple(args) = proj.args.type_at(0).kind()
+                            {
+                                Some((
+                                    DefIdOrName::Name("trait object"),
+                                    pred.rebind(proj.term.ty().unwrap()),
+                                    pred.rebind(args.as_slice()),
+                                ))
+                            } else {
+                                None
+                            }
+                        })
+                    }
+                    ty::Param(param) => {
+                        let generics = self.tcx.generics_of(body_id);
+                        let name = if generics.count() > param.index as usize
+                            && let def = generics.param_at(param.index as usize, self.tcx)
+                            && matches!(def.kind, ty::GenericParamDefKind::Type { .. })
+                            && def.name == param.name
+                        {
+                            DefIdOrName::DefId(def.def_id)
+                        } else {
+                            DefIdOrName::Name("type parameter")
+                        };
+                        param_env.caller_bounds().iter().find_map(|pred| {
+                            if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
+                        && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
+                        && proj.projection_ty.self_ty() == found
+                        // args tuple will always be args[1]
+                        && let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind()
+                            {
+                                Some((
+                                    name,
+                                    pred.kind().rebind(proj.term.ty().unwrap()),
+                                    pred.kind().rebind(args.as_slice()),
+                                ))
+                            } else {
+                                None
+                            }
+                        })
+                    }
+                    _ => None,
+                }
+            })
+        else {
+            return None;
+        };
+
+        let output = self.instantiate_binder_with_fresh_vars(
+            DUMMY_SP,
+            BoundRegionConversionTime::FnCall,
+            output,
+        );
+        let inputs = inputs
+            .skip_binder()
+            .iter()
+            .map(|ty| {
+                self.instantiate_binder_with_fresh_vars(
+                    DUMMY_SP,
+                    BoundRegionConversionTime::FnCall,
+                    inputs.rebind(*ty),
+                )
+            })
+            .collect();
+
+        // We don't want to register any extra obligations, which should be
+        // implied by wf, but also because that would possibly result in
+        // erroneous errors later on.
+        let InferOk { value: output, obligations: _ } =
+            self.at(&ObligationCause::dummy(), param_env).normalize(output);
+
+        if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+    }
+
+    fn suggest_add_reference_to_arg(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        poly_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        has_custom_message: bool,
+    ) -> bool {
+        let span = obligation.cause.span;
+
+        let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } =
+            obligation.cause.code()
+        {
+            parent_code
+        } else if let ObligationCauseCode::ItemObligation(_)
+        | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code()
+        {
+            obligation.cause.code()
+        } else if let ExpnKind::Desugaring(DesugaringKind::ForLoop) =
+            span.ctxt().outer_expn_data().kind
+        {
+            obligation.cause.code()
+        } else {
+            return false;
+        };
+
+        // List of traits for which it would be nonsensical to suggest borrowing.
+        // For instance, immutable references are always Copy, so suggesting to
+        // borrow would always succeed, but it's probably not what the user wanted.
+        let mut never_suggest_borrow: Vec<_> =
+            [LangItem::Copy, LangItem::Clone, LangItem::Unpin, LangItem::Sized]
+                .iter()
+                .filter_map(|lang_item| self.tcx.lang_items().get(*lang_item))
+                .collect();
+
+        if let Some(def_id) = self.tcx.get_diagnostic_item(sym::Send) {
+            never_suggest_borrow.push(def_id);
+        }
+
+        let param_env = obligation.param_env;
+
+        // Try to apply the original trait binding obligation by borrowing.
+        let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>,
+                                 blacklist: &[DefId]|
+         -> bool {
+            if blacklist.contains(&old_pred.def_id()) {
+                return false;
+            }
+            // We map bounds to `&T` and `&mut T`
+            let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
+                (
+                    trait_pred,
+                    Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+                )
+            });
+            let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
+                (
+                    trait_pred,
+                    Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+                )
+            });
+
+            let mk_result = |trait_pred_and_new_ty| {
+                let obligation =
+                    self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
+                self.predicate_must_hold_modulo_regions(&obligation)
+            };
+            let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);
+            let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);
+
+            let (ref_inner_ty_satisfies_pred, ref_inner_ty_mut) =
+                if let ObligationCauseCode::ItemObligation(_)
+                | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code()
+                    && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind()
+                {
+                    (
+                        mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))),
+                        mutability.is_mut(),
+                    )
+                } else {
+                    (false, false)
+                };
+
+            if imm_ref_self_ty_satisfies_pred
+                || mut_ref_self_ty_satisfies_pred
+                || ref_inner_ty_satisfies_pred
+            {
+                if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                    // We don't want a borrowing suggestion on the fields in structs,
+                    // ```
+                    // struct Foo {
+                    //  the_foos: Vec<Foo>
+                    // }
+                    // ```
+                    if !matches!(
+                        span.ctxt().outer_expn_data().kind,
+                        ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
+                    ) {
+                        return false;
+                    }
+                    if snippet.starts_with('&') {
+                        // This is already a literal borrow and the obligation is failing
+                        // somewhere else in the obligation chain. Do not suggest non-sense.
+                        return false;
+                    }
+                    // We have a very specific type of error, where just borrowing this argument
+                    // might solve the problem. In cases like this, the important part is the
+                    // original type obligation, not the last one that failed, which is arbitrary.
+                    // Because of this, we modify the error to refer to the original obligation and
+                    // return early in the caller.
+
+                    let msg = format!("the trait bound `{old_pred}` is not satisfied");
+                    if has_custom_message {
+                        err.note(msg);
+                    } else {
+                        err.messages =
+                            vec![(rustc_errors::DiagnosticMessage::from(msg), Style::NoStyle)];
+                    }
+                    let mut file = None;
+                    err.span_label(
+                        span,
+                        format!(
+                            "the trait `{}` is not implemented for `{}`",
+                            old_pred.print_modifiers_and_trait_path(),
+                            self.tcx.short_ty_string(old_pred.self_ty().skip_binder(), &mut file),
+                        ),
+                    );
+                    if let Some(file) = file {
+                        err.note(format!(
+                            "the full type name has been written to '{}'",
+                            file.display()
+                        ));
+                    }
+
+                    if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred {
+                        err.span_suggestions(
+                            span.shrink_to_lo(),
+                            "consider borrowing here",
+                            ["&".to_string(), "&mut ".to_string()],
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut;
+                        let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" });
+                        let sugg_msg = format!(
+                            "consider{} borrowing here",
+                            if is_mut { " mutably" } else { "" }
+                        );
+
+                        // Issue #109436, we need to add parentheses properly for method calls
+                        // for example, `foo.into()` should be `(&foo).into()`
+                        if let Some(_) =
+                            self.tcx.sess.source_map().span_look_ahead(span, ".", Some(50))
+                        {
+                            err.multipart_suggestion_verbose(
+                                sugg_msg,
+                                vec![
+                                    (span.shrink_to_lo(), format!("({sugg_prefix}")),
+                                    (span.shrink_to_hi(), ")".to_string()),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+                            return true;
+                        }
+
+                        // Issue #104961, we need to add parentheses properly for compound expressions
+                        // for example, `x.starts_with("hi".to_string() + "you")`
+                        // should be `x.starts_with(&("hi".to_string() + "you"))`
+                        let Some(body_id) =
+                            self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
+                        else {
+                            return false;
+                        };
+                        let body = self.tcx.hir().body(body_id);
+                        let mut expr_finder = FindExprBySpan::new(span);
+                        expr_finder.visit_expr(body.value);
+                        let Some(expr) = expr_finder.result else {
+                            return false;
+                        };
+                        let needs_parens = match expr.kind {
+                            // parenthesize if needed (Issue #46756)
+                            hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
+                            // parenthesize borrows of range literals (Issue #54505)
+                            _ if is_range_literal(expr) => true,
+                            _ => false,
+                        };
+
+                        let span = if needs_parens { span } else { span.shrink_to_lo() };
+                        let suggestions = if !needs_parens {
+                            vec![(span.shrink_to_lo(), sugg_prefix)]
+                        } else {
+                            vec![
+                                (span.shrink_to_lo(), format!("{sugg_prefix}(")),
+                                (span.shrink_to_hi(), ")".to_string()),
+                            ]
+                        };
+                        err.multipart_suggestion_verbose(
+                            sugg_msg,
+                            suggestions,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                    return true;
+                }
+            }
+            return false;
+        };
+
+        if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code {
+            try_borrowing(cause.derived.parent_trait_pred, &[])
+        } else if let ObligationCauseCode::BindingObligation(_, _)
+        | ObligationCauseCode::ItemObligation(_)
+        | ObligationCauseCode::ExprItemObligation(..)
+        | ObligationCauseCode::ExprBindingObligation(..) = code
+        {
+            try_borrowing(poly_trait_pred, &never_suggest_borrow)
+        } else {
+            false
+        }
+    }
+
+    // Suggest borrowing the type
+    fn suggest_borrowing_for_object_cast(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+        self_ty: Ty<'tcx>,
+        target_ty: Ty<'tcx>,
+    ) {
+        let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else {
+            return;
+        };
+        let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else {
+            return;
+        };
+        let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty);
+
+        for predicate in predicates.iter() {
+            if !self.predicate_must_hold_modulo_regions(
+                &obligation.with(self.tcx, predicate.with_self_ty(self.tcx, self_ref_ty)),
+            ) {
+                return;
+            }
+        }
+
+        err.span_suggestion(
+            obligation.cause.span.shrink_to_lo(),
+            format!(
+                "consider borrowing the value, since `&{self_ty}` can be coerced into `{target_ty}`"
+            ),
+            "&",
+            Applicability::MaybeIncorrect,
+        );
+    }
+
+    /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
+    /// suggest removing these references until we reach a type that implements the trait.
+    fn suggest_remove_reference(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool {
+        let mut span = obligation.cause.span;
+        let mut trait_pred = trait_pred;
+        let mut code = obligation.cause.code();
+        while let Some((c, Some(parent_trait_pred))) = code.parent() {
+            // We want the root obligation, in order to detect properly handle
+            // `for _ in &mut &mut vec![] {}`.
+            code = c;
+            trait_pred = parent_trait_pred;
+        }
+        while span.desugaring_kind().is_some() {
+            // Remove all the hir desugaring contexts while maintaining the macro contexts.
+            span.remove_mark();
+        }
+        let mut expr_finder = super::FindExprBySpan::new(span);
+        let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else {
+            return false;
+        };
+        let body = self.tcx.hir().body(body_id);
+        expr_finder.visit_expr(body.value);
+        let mut maybe_suggest = |suggested_ty, count, suggestions| {
+            // Remapping bound vars here
+            let trait_pred_and_suggested_ty =
+                trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
+
+            let new_obligation = self.mk_trait_obligation_with_new_self_ty(
+                obligation.param_env,
+                trait_pred_and_suggested_ty,
+            );
+
+            if self.predicate_may_hold(&new_obligation) {
+                let msg = if count == 1 {
+                    "consider removing the leading `&`-reference".to_string()
+                } else {
+                    format!("consider removing {count} leading `&`-references")
+                };
+
+                err.multipart_suggestion_verbose(
+                    msg,
+                    suggestions,
+                    Applicability::MachineApplicable,
+                );
+                true
+            } else {
+                false
+            }
+        };
+
+        // Maybe suggest removal of borrows from types in type parameters, like in
+        // `src/test/ui/not-panic/not-panic-safe.rs`.
+        let mut count = 0;
+        let mut suggestions = vec![];
+        // Skipping binder here, remapping below
+        let mut suggested_ty = trait_pred.self_ty().skip_binder();
+        if let Some(mut hir_ty) = expr_finder.ty_result {
+            while let hir::TyKind::Ref(_, mut_ty) = &hir_ty.kind {
+                count += 1;
+                let span = hir_ty.span.until(mut_ty.ty.span);
+                suggestions.push((span, String::new()));
+
+                let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
+                    break;
+                };
+                suggested_ty = *inner_ty;
+
+                hir_ty = mut_ty.ty;
+
+                if maybe_suggest(suggested_ty, count, suggestions.clone()) {
+                    return true;
+                }
+            }
+        }
+
+        // Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
+        let Some(mut expr) = expr_finder.result else {
+            return false;
+        };
+        let mut count = 0;
+        let mut suggestions = vec![];
+        // Skipping binder here, remapping below
+        let mut suggested_ty = trait_pred.self_ty().skip_binder();
+        'outer: loop {
+            while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind {
+                count += 1;
+                let span = if expr.span.eq_ctxt(borrowed.span) {
+                    expr.span.until(borrowed.span)
+                } else {
+                    expr.span.with_hi(expr.span.lo() + BytePos(1))
+                };
+                suggestions.push((span, String::new()));
+
+                let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
+                    break 'outer;
+                };
+                suggested_ty = *inner_ty;
+
+                expr = borrowed;
+
+                if maybe_suggest(suggested_ty, count, suggestions.clone()) {
+                    return true;
+                }
+            }
+            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 Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id)
+                && let None = local.ty
+                && let Some(binding_expr) = local.init
+            {
+                expr = binding_expr;
+            } else {
+                break 'outer;
+            }
+        }
+        false
+    }
+
+    fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
+        let hir = self.tcx.hir();
+        if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives()
+            && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id)
+        {
+            // FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()`
+            // and if not maybe suggest doing something else? If we kept the expression around we
+            // could also check if it is an fn call (very likely) and suggest changing *that*, if
+            // it is from the local crate.
+
+            // use nth(1) to skip one layer of desugaring from `IntoIter::into_iter`
+            if let Some((_, hir::Node::Expr(await_expr))) = hir.parent_iter(*hir_id).nth(1)
+                && let Some(expr_span) = expr.span.find_ancestor_inside_same_ctxt(await_expr.span)
+            {
+                let removal_span = self
+                    .tcx
+                    .sess
+                    .source_map()
+                    .span_extend_while(expr_span, char::is_whitespace)
+                    .unwrap_or(expr_span)
+                    .shrink_to_hi()
+                    .to(await_expr.span.shrink_to_hi());
+                err.span_suggestion(
+                    removal_span,
+                    "remove the `.await`",
+                    "",
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                err.span_label(obligation.cause.span, "remove the `.await`");
+            }
+            // FIXME: account for associated `async fn`s.
+            if let hir::Expr { span, kind: hir::ExprKind::Call(base, _), .. } = expr {
+                if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
+                    obligation.predicate.kind().skip_binder()
+                {
+                    err.span_label(*span, format!("this call returns `{}`", pred.self_ty()));
+                }
+                if let Some(typeck_results) = &self.typeck_results
+                    && let ty = typeck_results.expr_ty_adjusted(base)
+                    && let ty::FnDef(def_id, _args) = ty.kind()
+                    && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) =
+                        hir.get_if_local(*def_id)
+                {
+                    let msg = format!("alternatively, consider making `fn {ident}` asynchronous");
+                    if vis_span.is_empty() {
+                        err.span_suggestion_verbose(
+                            span.shrink_to_lo(),
+                            msg,
+                            "async ",
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        err.span_suggestion_verbose(
+                            vis_span.shrink_to_hi(),
+                            msg,
+                            " async",
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    /// Check if the trait bound is implemented for a different mutability and note it in the
+    /// final error.
+    fn suggest_change_mut(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) {
+        let points_at_arg = matches!(
+            obligation.cause.code(),
+            ObligationCauseCode::FunctionArgumentObligation { .. },
+        );
+
+        let span = obligation.cause.span;
+        if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+            let refs_number =
+                snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
+            if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
+                // Do not suggest removal of borrow from type arguments.
+                return;
+            }
+            let trait_pred = self.resolve_vars_if_possible(trait_pred);
+            if trait_pred.has_non_region_infer() {
+                // Do not ICE while trying to find if a reborrow would succeed on a trait with
+                // unresolved bindings.
+                return;
+            }
+
+            // Skipping binder here, remapping below
+            if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind()
+            {
+                let suggested_ty = match mutability {
+                    hir::Mutability::Mut => Ty::new_imm_ref(self.tcx, region, t_type),
+                    hir::Mutability::Not => Ty::new_mut_ref(self.tcx, region, t_type),
+                };
+
+                // Remapping bound vars here
+                let trait_pred_and_suggested_ty =
+                    trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
+
+                let new_obligation = self.mk_trait_obligation_with_new_self_ty(
+                    obligation.param_env,
+                    trait_pred_and_suggested_ty,
+                );
+                let suggested_ty_would_satisfy_obligation = self
+                    .evaluate_obligation_no_overflow(&new_obligation)
+                    .must_apply_modulo_regions();
+                if suggested_ty_would_satisfy_obligation {
+                    let sp = self
+                        .tcx
+                        .sess
+                        .source_map()
+                        .span_take_while(span, |c| c.is_whitespace() || *c == '&');
+                    if points_at_arg && mutability.is_not() && refs_number > 0 {
+                        // If we have a call like foo(&mut buf), then don't suggest foo(&mut mut buf)
+                        if snippet
+                            .trim_start_matches(|c: char| c.is_whitespace() || c == '&')
+                            .starts_with("mut")
+                        {
+                            return;
+                        }
+                        err.span_suggestion_verbose(
+                            sp,
+                            "consider changing this borrow's mutability",
+                            "&mut ",
+                            Applicability::MachineApplicable,
+                        );
+                    } else {
+                        err.note(format!(
+                            "`{}` is implemented for `{}`, but not for `{}`",
+                            trait_pred.print_modifiers_and_trait_path(),
+                            suggested_ty,
+                            trait_pred.skip_binder().self_ty(),
+                        ));
+                    }
+                }
+            }
+        }
+    }
+
+    fn suggest_semicolon_removal(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        span: Span,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool {
+        let hir = self.tcx.hir();
+        let node = self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id);
+        if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node
+            && let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind
+            && sig.decl.output.span().overlaps(span)
+            && blk.expr.is_none()
+            && trait_pred.self_ty().skip_binder().is_unit()
+            && let Some(stmt) = blk.stmts.last()
+            && let hir::StmtKind::Semi(expr) = stmt.kind
+            // Only suggest this if the expression behind the semicolon implements the predicate
+            && let Some(typeck_results) = &self.typeck_results
+            && let Some(ty) = typeck_results.expr_ty_opt(expr)
+            && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty(
+                obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, ty))
+            ))
+        {
+            err.span_label(
+                expr.span,
+                format!(
+                    "this expression has type `{}`, which implements `{}`",
+                    ty,
+                    trait_pred.print_modifiers_and_trait_path()
+                ),
+            );
+            err.span_suggestion(
+                self.tcx.sess.source_map().end_point(stmt.span),
+                "remove this semicolon",
+                "",
+                Applicability::MachineApplicable,
+            );
+            return true;
+        }
+        false
+    }
+
+    fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
+        let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) =
+            self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id)
+        else {
+            return None;
+        };
+
+        if let hir::FnRetTy::Return(ret_ty) = sig.decl.output { Some(ret_ty.span) } else { None }
+    }
+
+    /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if
+    /// applicable and signal that the error has been expanded appropriately and needs to be
+    /// emitted.
+    fn suggest_impl_trait(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool {
+        let ObligationCauseCode::SizedReturnType = obligation.cause.code() else {
+            return false;
+        };
+        let ty::Dynamic(_, _, ty::Dyn) = trait_pred.self_ty().skip_binder().kind() else {
+            return false;
+        };
+
+        err.code(error_code!(E0746));
+        err.set_primary_message("return type cannot have an unboxed trait object");
+        err.children.clear();
+
+        let span = obligation.cause.span;
+        if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span)
+            && snip.starts_with("dyn ")
+        {
+            err.span_suggestion(
+                span.with_hi(span.lo() + BytePos(4)),
+                "return an `impl Trait` instead of a `dyn Trait`, \
+                if all returned values are the same type",
+                "impl ",
+                Applicability::MaybeIncorrect,
+            );
+        }
+
+        let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(obligation.cause.body_id));
+
+        let mut visitor = ReturnsVisitor::default();
+        visitor.visit_body(body);
+
+        let mut sugg =
+            vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())];
+        sugg.extend(visitor.returns.into_iter().flat_map(|expr| {
+            let span =
+                expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span);
+            if !span.can_be_used_for_suggestions() {
+                vec![]
+            } else if let hir::ExprKind::Call(path, ..) = expr.kind
+                && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, method)) = path.kind
+                && method.ident.name == sym::new
+                && let hir::TyKind::Path(hir::QPath::Resolved(.., box_path)) = ty.kind
+                && box_path
+                    .res
+                    .opt_def_id()
+                    .is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box())
+            {
+                // Don't box `Box::new`
+                vec![]
+            } else {
+                vec![
+                    (span.shrink_to_lo(), "Box::new(".to_string()),
+                    (span.shrink_to_hi(), ")".to_string()),
+                ]
+            }
+        }));
+
+        err.multipart_suggestion(
+            "box the return type, and wrap all of the returned values in `Box::new`",
+            sugg,
+            Applicability::MaybeIncorrect,
+        );
+
+        true
+    }
+
+    fn point_at_returns_when_relevant(
+        &self,
+        err: &mut DiagnosticBuilder<'tcx>,
+        obligation: &PredicateObligation<'tcx>,
+    ) {
+        match obligation.cause.code().peel_derives() {
+            ObligationCauseCode::SizedReturnType => {}
+            _ => return,
+        }
+
+        let hir = self.tcx.hir();
+        let node = self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id);
+        if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) =
+            node
+        {
+            let body = hir.body(*body_id);
+            // Point at all the `return`s in the function as they have failed trait bounds.
+            let mut visitor = ReturnsVisitor::default();
+            visitor.visit_body(body);
+            let typeck_results = self.typeck_results.as_ref().unwrap();
+            for expr in &visitor.returns {
+                if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) {
+                    let ty = self.resolve_vars_if_possible(returned_ty);
+                    if ty.references_error() {
+                        // don't print out the [type error] here
+                        err.delay_as_bug();
+                    } else {
+                        err.span_label(expr.span, format!("this returned value is of type `{ty}`"));
+                    }
+                }
+            }
+        }
+    }
+
+    fn report_closure_arg_mismatch(
+        &self,
+        span: Span,
+        found_span: Option<Span>,
+        found: ty::PolyTraitRef<'tcx>,
+        expected: ty::PolyTraitRef<'tcx>,
+        cause: &ObligationCauseCode<'tcx>,
+        found_node: Option<Node<'_>>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        pub(crate) fn build_fn_sig_ty<'tcx>(
+            infcx: &InferCtxt<'tcx>,
+            trait_ref: ty::PolyTraitRef<'tcx>,
+        ) -> Ty<'tcx> {
+            let inputs = trait_ref.skip_binder().args.type_at(1);
+            let sig = match inputs.kind() {
+                ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id()) => {
+                    infcx.tcx.mk_fn_sig(
+                        *inputs,
+                        infcx.next_ty_var(TypeVariableOrigin {
+                            span: DUMMY_SP,
+                            kind: TypeVariableOriginKind::MiscVariable,
+                        }),
+                        false,
+                        hir::Unsafety::Normal,
+                        abi::Abi::Rust,
+                    )
+                }
+                _ => infcx.tcx.mk_fn_sig(
+                    [inputs],
+                    infcx.next_ty_var(TypeVariableOrigin {
+                        span: DUMMY_SP,
+                        kind: TypeVariableOriginKind::MiscVariable,
+                    }),
+                    false,
+                    hir::Unsafety::Normal,
+                    abi::Abi::Rust,
+                ),
+            };
+
+            Ty::new_fn_ptr(infcx.tcx, trait_ref.rebind(sig))
+        }
+
+        let argument_kind = match expected.skip_binder().self_ty().kind() {
+            ty::Closure(..) => "closure",
+            ty::Coroutine(..) => "coroutine",
+            _ => "function",
+        };
+        let mut err = struct_span_err!(
+            self.dcx(),
+            span,
+            E0631,
+            "type mismatch in {argument_kind} arguments",
+        );
+
+        err.span_label(span, "expected due to this");
+
+        let found_span = found_span.unwrap_or(span);
+        err.span_label(found_span, "found signature defined here");
+
+        let expected = build_fn_sig_ty(self, expected);
+        let found = build_fn_sig_ty(self, found);
+
+        let (expected_str, found_str) = self.cmp(expected, found);
+
+        let signature_kind = format!("{argument_kind} signature");
+        err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str);
+
+        self.note_conflicting_fn_args(&mut err, cause, expected, found, param_env);
+        self.note_conflicting_closure_bounds(cause, &mut err);
+
+        if let Some(found_node) = found_node {
+            hint_missing_borrow(self, param_env, span, found, expected, found_node, &mut err);
+        }
+
+        err
+    }
+
+    fn note_conflicting_fn_args(
+        &self,
+        err: &mut Diagnostic,
+        cause: &ObligationCauseCode<'tcx>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) {
+        let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = cause else {
+            return;
+        };
+        let ty::FnPtr(expected) = expected.kind() else {
+            return;
+        };
+        let ty::FnPtr(found) = found.kind() else {
+            return;
+        };
+        let Some(Node::Expr(arg)) = self.tcx.opt_hir_node(*arg_hir_id) else {
+            return;
+        };
+        let hir::ExprKind::Path(path) = arg.kind else {
+            return;
+        };
+        let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(*expected).inputs();
+        let found_inputs = self.tcx.instantiate_bound_regions_with_erased(*found).inputs();
+        let both_tys = expected_inputs.iter().copied().zip(found_inputs.iter().copied());
+
+        let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| {
+            let (expected_ty, expected_refs) = get_deref_type_and_refs(expected);
+            let (found_ty, found_refs) = get_deref_type_and_refs(found);
+
+            if infcx.can_eq(param_env, found_ty, expected_ty) {
+                if found_refs.len() == expected_refs.len()
+                    && found_refs.iter().eq(expected_refs.iter())
+                {
+                    name
+                } else if found_refs.len() > expected_refs.len() {
+                    let refs = &found_refs[..found_refs.len() - expected_refs.len()];
+                    if found_refs[..expected_refs.len()].iter().eq(expected_refs.iter()) {
+                        format!(
+                            "{}{name}",
+                            refs.iter()
+                                .map(|mutbl| format!("&{}", mutbl.prefix_str()))
+                                .collect::<Vec<_>>()
+                                .join(""),
+                        )
+                    } else {
+                        // The refs have different mutability.
+                        format!(
+                            "{}*{name}",
+                            refs.iter()
+                                .map(|mutbl| format!("&{}", mutbl.prefix_str()))
+                                .collect::<Vec<_>>()
+                                .join(""),
+                        )
+                    }
+                } else if expected_refs.len() > found_refs.len() {
+                    format!(
+                        "{}{name}",
+                        (0..(expected_refs.len() - found_refs.len()))
+                            .map(|_| "*")
+                            .collect::<Vec<_>>()
+                            .join(""),
+                    )
+                } else {
+                    format!(
+                        "{}{name}",
+                        found_refs
+                            .iter()
+                            .map(|mutbl| format!("&{}", mutbl.prefix_str()))
+                            .chain(found_refs.iter().map(|_| "*".to_string()))
+                            .collect::<Vec<_>>()
+                            .join(""),
+                    )
+                }
+            } else {
+                format!("/* {found} */")
+            }
+        };
+        let args_have_same_underlying_type = both_tys.clone().all(|(expected, found)| {
+            let (expected_ty, _) = get_deref_type_and_refs(expected);
+            let (found_ty, _) = get_deref_type_and_refs(found);
+            self.can_eq(param_env, found_ty, expected_ty)
+        });
+        let (closure_names, call_names): (Vec<_>, Vec<_>) = if args_have_same_underlying_type
+            && !expected_inputs.is_empty()
+            && expected_inputs.len() == found_inputs.len()
+            && let Some(typeck) = &self.typeck_results
+            && let Res::Def(res_kind, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id)
+            && res_kind.is_fn_like()
+        {
+            let closure: Vec<_> = self
+                .tcx
+                .fn_arg_names(fn_def_id)
+                .iter()
+                .enumerate()
+                .map(|(i, ident)| {
+                    if ident.name.is_empty() || ident.name == kw::SelfLower {
+                        format!("arg{i}")
+                    } else {
+                        format!("{ident}")
+                    }
+                })
+                .collect();
+            let args = closure
+                .iter()
+                .zip(both_tys)
+                .map(|(name, (expected, found))| {
+                    arg_expr(self.infcx, name.to_owned(), expected, found)
+                })
+                .collect();
+            (closure, args)
+        } else {
+            let closure_args = expected_inputs
+                .iter()
+                .enumerate()
+                .map(|(i, _)| format!("arg{i}"))
+                .collect::<Vec<_>>();
+            let call_args = both_tys
+                .enumerate()
+                .map(|(i, (expected, found))| {
+                    arg_expr(self.infcx, format!("arg{i}"), expected, found)
+                })
+                .collect::<Vec<_>>();
+            (closure_args, call_args)
+        };
+        let closure_names: Vec<_> = closure_names
+            .into_iter()
+            .zip(expected_inputs.iter())
+            .map(|(name, ty)| {
+                format!(
+                    "{name}{}",
+                    if ty.has_infer_types() {
+                        String::new()
+                    } else if ty.references_error() {
+                        ": /* type */".to_string()
+                    } else {
+                        format!(": {ty}")
+                    }
+                )
+            })
+            .collect();
+        err.multipart_suggestion(
+            "consider wrapping the function in a closure",
+            vec![
+                (arg.span.shrink_to_lo(), format!("|{}| ", closure_names.join(", "))),
+                (arg.span.shrink_to_hi(), format!("({})", call_names.join(", "))),
+            ],
+            Applicability::MaybeIncorrect,
+        );
+    }
+
+    // Add a note if there are two `Fn`-family bounds that have conflicting argument
+    // requirements, which will always cause a closure to have a type error.
+    fn note_conflicting_closure_bounds(
+        &self,
+        cause: &ObligationCauseCode<'tcx>,
+        err: &mut DiagnosticBuilder<'tcx>,
+    ) {
+        // First, look for an `ExprBindingObligation`, which means we can get
+        // the unsubstituted predicate list of the called function. And check
+        // that the predicate that we failed to satisfy is a `Fn`-like trait.
+        if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = cause
+            && let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
+            && let Some(pred) = predicates.predicates.get(*idx)
+            && let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder()
+            && self.tcx.is_fn_trait(trait_pred.def_id())
+        {
+            let expected_self =
+                self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.self_ty()));
+            let expected_args =
+                self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.args));
+
+            // Find another predicate whose self-type is equal to the expected self type,
+            // but whose args don't match.
+            let other_pred = predicates.into_iter().enumerate().find(|(other_idx, (pred, _))| {
+                match pred.kind().skip_binder() {
+                    ty::ClauseKind::Trait(trait_pred)
+                        if self.tcx.is_fn_trait(trait_pred.def_id())
+                            && other_idx != idx
+                            // Make sure that the self type matches
+                            // (i.e. constraining this closure)
+                            && expected_self
+                                == self.tcx.anonymize_bound_vars(
+                                    pred.kind().rebind(trait_pred.self_ty()),
+                                )
+                            // But the args don't match (i.e. incompatible args)
+                            && expected_args
+                                != self.tcx.anonymize_bound_vars(
+                                    pred.kind().rebind(trait_pred.trait_ref.args),
+                                ) =>
+                    {
+                        true
+                    }
+                    _ => false,
+                }
+            });
+            // If we found one, then it's very likely the cause of the error.
+            if let Some((_, (_, other_pred_span))) = other_pred {
+                err.span_note(
+                    other_pred_span,
+                    "closure inferred to have a different signature due to this bound",
+                );
+            }
+        }
+    }
+
+    fn suggest_fully_qualified_path(
+        &self,
+        err: &mut Diagnostic,
+        item_def_id: DefId,
+        span: Span,
+        trait_ref: DefId,
+    ) {
+        if let Some(assoc_item) = self.tcx.opt_associated_item(item_def_id) {
+            if let ty::AssocKind::Const | ty::AssocKind::Type = assoc_item.kind {
+                err.note(format!(
+                    "{}s cannot be accessed directly on a `trait`, they can only be \
+                        accessed through a specific `impl`",
+                    self.tcx.def_kind_descr(assoc_item.kind.as_def_kind(), item_def_id)
+                ));
+                err.span_suggestion(
+                    span,
+                    "use the fully qualified path to an implementation",
+                    format!("<Type as {}>::{}", self.tcx.def_path_str(trait_ref), assoc_item.name),
+                    Applicability::HasPlaceholders,
+                );
+            }
+        }
+    }
+
+    /// Adds an async-await specific note to the diagnostic when the future does not implement
+    /// an auto trait because of a captured type.
+    ///
+    /// ```text
+    /// note: future does not implement `Qux` as this value is used across an await
+    ///   --> $DIR/issue-64130-3-other.rs:17:5
+    ///    |
+    /// LL |     let x = Foo;
+    ///    |         - has type `Foo`
+    /// LL |     baz().await;
+    ///    |     ^^^^^^^^^^^ await occurs here, with `x` maybe used later
+    /// LL | }
+    ///    | - `x` is later dropped here
+    /// ```
+    ///
+    /// When the diagnostic does not implement `Send` or `Sync` specifically, then the diagnostic
+    /// is "replaced" with a different message and a more specific error.
+    ///
+    /// ```text
+    /// error: future cannot be sent between threads safely
+    ///   --> $DIR/issue-64130-2-send.rs:21:5
+    ///    |
+    /// LL | fn is_send<T: Send>(t: T) { }
+    ///    |               ---- required by this bound in `is_send`
+    /// ...
+    /// LL |     is_send(bar());
+    ///    |     ^^^^^^^ future returned by `bar` is not send
+    ///    |
+    ///    = help: within `impl std::future::Future`, the trait `std::marker::Send` is not
+    ///            implemented for `Foo`
+    /// note: future is not send as this value is used across an await
+    ///   --> $DIR/issue-64130-2-send.rs:15:5
+    ///    |
+    /// LL |     let x = Foo;
+    ///    |         - has type `Foo`
+    /// LL |     baz().await;
+    ///    |     ^^^^^^^^^^^ await occurs here, with `x` maybe used later
+    /// LL | }
+    ///    | - `x` is later dropped here
+    /// ```
+    ///
+    /// Returns `true` if an async-await specific note was added to the diagnostic.
+    #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))]
+    fn maybe_note_obligation_cause_for_async_await(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> bool {
+        let hir = self.tcx.hir();
+
+        // Attempt to detect an async-await error by looking at the obligation causes, looking
+        // for a coroutine to be present.
+        //
+        // When a future does not implement a trait because of a captured type in one of the
+        // coroutines somewhere in the call stack, then the result is a chain of obligations.
+        //
+        // Given an `async fn` A that calls an `async fn` B which captures a non-send type and that
+        // future is passed as an argument to a function C which requires a `Send` type, then the
+        // chain looks something like this:
+        //
+        // - `BuiltinDerivedObligation` with a coroutine witness (B)
+        // - `BuiltinDerivedObligation` with a coroutine (B)
+        // - `BuiltinDerivedObligation` with `impl std::future::Future` (B)
+        // - `BuiltinDerivedObligation` with a coroutine witness (A)
+        // - `BuiltinDerivedObligation` with a coroutine (A)
+        // - `BuiltinDerivedObligation` with `impl std::future::Future` (A)
+        // - `BindingObligation` with `impl_send` (Send requirement)
+        //
+        // The first obligation in the chain is the most useful and has the coroutine that captured
+        // the type. The last coroutine (`outer_coroutine` below) has information about where the
+        // bound was introduced. At least one coroutine should be present for this diagnostic to be
+        // modified.
+        let (mut trait_ref, mut target_ty) = match obligation.predicate.kind().skip_binder() {
+            ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => (Some(p), Some(p.self_ty())),
+            _ => (None, None),
+        };
+        let mut coroutine = None;
+        let mut outer_coroutine = None;
+        let mut next_code = Some(obligation.cause.code());
+
+        let mut seen_upvar_tys_infer_tuple = false;
+
+        while let Some(code) = next_code {
+            debug!(?code);
+            match code {
+                ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => {
+                    next_code = Some(parent_code);
+                }
+                ObligationCauseCode::ImplDerivedObligation(cause) => {
+                    let ty = cause.derived.parent_trait_pred.skip_binder().self_ty();
+                    debug!(
+                        parent_trait_ref = ?cause.derived.parent_trait_pred,
+                        self_ty.kind = ?ty.kind(),
+                        "ImplDerived",
+                    );
+
+                    match *ty.kind() {
+                        ty::Coroutine(did, ..) | ty::CoroutineWitness(did, _) => {
+                            coroutine = coroutine.or(Some(did));
+                            outer_coroutine = Some(did);
+                        }
+                        ty::Tuple(_) if !seen_upvar_tys_infer_tuple => {
+                            // By introducing a tuple of upvar types into the chain of obligations
+                            // of a coroutine, the first non-coroutine item is now the tuple itself,
+                            // we shall ignore this.
+
+                            seen_upvar_tys_infer_tuple = true;
+                        }
+                        _ if coroutine.is_none() => {
+                            trait_ref = Some(cause.derived.parent_trait_pred.skip_binder());
+                            target_ty = Some(ty);
+                        }
+                        _ => {}
+                    }
+
+                    next_code = Some(&cause.derived.parent_code);
+                }
+                ObligationCauseCode::DerivedObligation(derived_obligation)
+                | ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) => {
+                    let ty = derived_obligation.parent_trait_pred.skip_binder().self_ty();
+                    debug!(
+                        parent_trait_ref = ?derived_obligation.parent_trait_pred,
+                        self_ty.kind = ?ty.kind(),
+                    );
+
+                    match *ty.kind() {
+                        ty::Coroutine(did, ..) | ty::CoroutineWitness(did, ..) => {
+                            coroutine = coroutine.or(Some(did));
+                            outer_coroutine = Some(did);
+                        }
+                        ty::Tuple(_) if !seen_upvar_tys_infer_tuple => {
+                            // By introducing a tuple of upvar types into the chain of obligations
+                            // of a coroutine, the first non-coroutine item is now the tuple itself,
+                            // we shall ignore this.
+
+                            seen_upvar_tys_infer_tuple = true;
+                        }
+                        _ if coroutine.is_none() => {
+                            trait_ref = Some(derived_obligation.parent_trait_pred.skip_binder());
+                            target_ty = Some(ty);
+                        }
+                        _ => {}
+                    }
+
+                    next_code = Some(&derived_obligation.parent_code);
+                }
+                _ => break,
+            }
+        }
+
+        // Only continue if a coroutine was found.
+        debug!(?coroutine, ?trait_ref, ?target_ty);
+        let (Some(coroutine_did), Some(trait_ref), Some(target_ty)) =
+            (coroutine, trait_ref, target_ty)
+        else {
+            return false;
+        };
+
+        let span = self.tcx.def_span(coroutine_did);
+
+        let coroutine_did_root = self.tcx.typeck_root_def_id(coroutine_did);
+        debug!(
+            ?coroutine_did,
+            ?coroutine_did_root,
+            typeck_results.hir_owner = ?self.typeck_results.as_ref().map(|t| t.hir_owner),
+            ?span,
+        );
+
+        let coroutine_body = coroutine_did
+            .as_local()
+            .and_then(|def_id| hir.maybe_body_owned_by(def_id))
+            .map(|body_id| hir.body(body_id));
+        let mut visitor = AwaitsVisitor::default();
+        if let Some(body) = coroutine_body {
+            visitor.visit_body(body);
+        }
+        debug!(awaits = ?visitor.awaits);
+
+        // Look for a type inside the coroutine interior that matches the target type to get
+        // a span.
+        let target_ty_erased = self.tcx.erase_regions(target_ty);
+        let ty_matches = |ty| -> bool {
+            // Careful: the regions for types that appear in the
+            // coroutine interior are not generally known, so we
+            // want to erase them when comparing (and anyway,
+            // `Send` and other bounds are generally unaffected by
+            // the choice of region). When erasing regions, we
+            // also have to erase late-bound regions. This is
+            // because the types that appear in the coroutine
+            // interior generally contain "bound regions" to
+            // represent regions that are part of the suspended
+            // coroutine frame. Bound regions are preserved by
+            // `erase_regions` and so we must also call
+            // `instantiate_bound_regions_with_erased`.
+            let ty_erased = self.tcx.instantiate_bound_regions_with_erased(ty);
+            let ty_erased = self.tcx.erase_regions(ty_erased);
+            let eq = ty_erased == target_ty_erased;
+            debug!(?ty_erased, ?target_ty_erased, ?eq);
+            eq
+        };
+
+        // Get the typeck results from the infcx if the coroutine is the function we are currently
+        // type-checking; otherwise, get them by performing a query. This is needed to avoid
+        // cycles. If we can't use resolved types because the coroutine comes from another crate,
+        // we still provide a targeted error but without all the relevant spans.
+        let coroutine_data = match &self.typeck_results {
+            Some(t) if t.hir_owner.to_def_id() == coroutine_did_root => CoroutineData(t),
+            _ if coroutine_did.is_local() => {
+                CoroutineData(self.tcx.typeck(coroutine_did.expect_local()))
+            }
+            _ => return false,
+        };
+
+        let coroutine_within_in_progress_typeck = match &self.typeck_results {
+            Some(t) => t.hir_owner.to_def_id() == coroutine_did_root,
+            _ => false,
+        };
+
+        let mut interior_or_upvar_span = None;
+
+        let from_awaited_ty = coroutine_data.get_from_await_ty(visitor, hir, ty_matches);
+        debug!(?from_awaited_ty);
+
+        // Avoid disclosing internal information to downstream crates.
+        if coroutine_did.is_local()
+            // Try to avoid cycles.
+            && !coroutine_within_in_progress_typeck
+            && let Some(coroutine_info) = self.tcx.mir_coroutine_witnesses(coroutine_did)
+        {
+            debug!(?coroutine_info);
+            'find_source: for (variant, source_info) in
+                coroutine_info.variant_fields.iter().zip(&coroutine_info.variant_source_info)
+            {
+                debug!(?variant);
+                for &local in variant {
+                    let decl = &coroutine_info.field_tys[local];
+                    debug!(?decl);
+                    if ty_matches(ty::Binder::dummy(decl.ty)) && !decl.ignore_for_traits {
+                        interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior(
+                            decl.source_info.span,
+                            Some((source_info.span, from_awaited_ty)),
+                        ));
+                        break 'find_source;
+                    }
+                }
+            }
+        }
+
+        if interior_or_upvar_span.is_none() {
+            interior_or_upvar_span =
+                coroutine_data.try_get_upvar_span(self, coroutine_did, ty_matches);
+        }
+
+        if interior_or_upvar_span.is_none() && !coroutine_did.is_local() {
+            interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior(span, None));
+        }
+
+        debug!(?interior_or_upvar_span);
+        if let Some(interior_or_upvar_span) = interior_or_upvar_span {
+            let is_async = self.tcx.coroutine_is_async(coroutine_did);
+            self.note_obligation_cause_for_async_await(
+                err,
+                interior_or_upvar_span,
+                is_async,
+                outer_coroutine,
+                trait_ref,
+                target_ty,
+                obligation,
+                next_code,
+            );
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Unconditionally adds the diagnostic note described in
+    /// `maybe_note_obligation_cause_for_async_await`'s documentation comment.
+    #[instrument(level = "debug", skip_all)]
+    fn note_obligation_cause_for_async_await(
+        &self,
+        err: &mut Diagnostic,
+        interior_or_upvar_span: CoroutineInteriorOrUpvar,
+        is_async: bool,
+        outer_coroutine: Option<DefId>,
+        trait_pred: ty::TraitPredicate<'tcx>,
+        target_ty: Ty<'tcx>,
+        obligation: &PredicateObligation<'tcx>,
+        next_code: Option<&ObligationCauseCode<'tcx>>,
+    ) {
+        let source_map = self.tcx.sess.source_map();
+
+        let (await_or_yield, an_await_or_yield) =
+            if is_async { ("await", "an await") } else { ("yield", "a yield") };
+        let future_or_coroutine = if is_async { "future" } else { "coroutine" };
+
+        // Special case the primary error message when send or sync is the trait that was
+        // not implemented.
+        let hir = self.tcx.hir();
+        let trait_explanation = if let Some(name @ (sym::Send | sym::Sync)) =
+            self.tcx.get_diagnostic_name(trait_pred.def_id())
+        {
+            let (trait_name, trait_verb) =
+                if name == sym::Send { ("`Send`", "sent") } else { ("`Sync`", "shared") };
+
+            err.clear_code();
+            err.set_primary_message(format!(
+                "{future_or_coroutine} cannot be {trait_verb} between threads safely"
+            ));
+
+            let original_span = err.span.primary_span().unwrap();
+            let mut span = MultiSpan::from_span(original_span);
+
+            let message = outer_coroutine
+                .and_then(|coroutine_did| {
+                    Some(match self.tcx.coroutine_kind(coroutine_did).unwrap() {
+                        CoroutineKind::Coroutine(_) => format!("coroutine is not {trait_name}"),
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Async,
+                            CoroutineSource::Fn,
+                        ) => self
+                            .tcx
+                            .parent(coroutine_did)
+                            .as_local()
+                            .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did))
+                            .and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
+                            .map(|name| {
+                                format!("future returned by `{name}` is not {trait_name}")
+                            })?,
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Async,
+                            CoroutineSource::Block,
+                        ) => {
+                            format!("future created by async block is not {trait_name}")
+                        }
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Async,
+                            CoroutineSource::Closure,
+                        ) => {
+                            format!("future created by async closure is not {trait_name}")
+                        }
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::AsyncGen,
+                            CoroutineSource::Fn,
+                        ) => self
+                            .tcx
+                            .parent(coroutine_did)
+                            .as_local()
+                            .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did))
+                            .and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
+                            .map(|name| {
+                                format!("async iterator returned by `{name}` is not {trait_name}")
+                            })?,
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::AsyncGen,
+                            CoroutineSource::Block,
+                        ) => {
+                            format!("async iterator created by async gen block is not {trait_name}")
+                        }
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::AsyncGen,
+                            CoroutineSource::Closure,
+                        ) => {
+                            format!(
+                                "async iterator created by async gen closure is not {trait_name}"
+                            )
+                        }
+                        CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Fn) => {
+                            self.tcx
+                                .parent(coroutine_did)
+                                .as_local()
+                                .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did))
+                                .and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
+                                .map(|name| {
+                                    format!("iterator returned by `{name}` is not {trait_name}")
+                                })?
+                        }
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Gen,
+                            CoroutineSource::Block,
+                        ) => {
+                            format!("iterator created by gen block is not {trait_name}")
+                        }
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Gen,
+                            CoroutineSource::Closure,
+                        ) => {
+                            format!("iterator created by gen closure is not {trait_name}")
+                        }
+                    })
+                })
+                .unwrap_or_else(|| format!("{future_or_coroutine} is not {trait_name}"));
+
+            span.push_span_label(original_span, message);
+            err.set_span(span);
+
+            format!("is not {trait_name}")
+        } else {
+            format!("does not implement `{}`", trait_pred.print_modifiers_and_trait_path())
+        };
+
+        let mut explain_yield = |interior_span: Span, yield_span: Span| {
+            let mut span = MultiSpan::from_span(yield_span);
+            let snippet = match source_map.span_to_snippet(interior_span) {
+                // #70935: If snippet contains newlines, display "the value" instead
+                // so that we do not emit complex diagnostics.
+                Ok(snippet) if !snippet.contains('\n') => format!("`{snippet}`"),
+                _ => "the value".to_string(),
+            };
+            // note: future is not `Send` as this value is used across an await
+            //   --> $DIR/issue-70935-complex-spans.rs:13:9
+            //    |
+            // LL |            baz(|| async {
+            //    |  ______________-
+            //    | |
+            //    | |
+            // LL | |              foo(tx.clone());
+            // LL | |          }).await;
+            //    | |          - ^^^^^^ await occurs here, with value maybe used later
+            //    | |__________|
+            //    |            has type `closure` which is not `Send`
+            // note: value is later dropped here
+            // LL | |          }).await;
+            //    | |                  ^
+            //
+            span.push_span_label(
+                yield_span,
+                format!("{await_or_yield} occurs here, with {snippet} maybe used later"),
+            );
+            span.push_span_label(
+                interior_span,
+                format!("has type `{target_ty}` which {trait_explanation}"),
+            );
+            err.span_note(
+                span,
+                format!("{future_or_coroutine} {trait_explanation} as this value is used across {an_await_or_yield}"),
+            );
+        };
+        match interior_or_upvar_span {
+            CoroutineInteriorOrUpvar::Interior(interior_span, interior_extra_info) => {
+                if let Some((yield_span, from_awaited_ty)) = interior_extra_info {
+                    if let Some(await_span) = from_awaited_ty {
+                        // The type causing this obligation is one being awaited at await_span.
+                        let mut span = MultiSpan::from_span(await_span);
+                        span.push_span_label(
+                            await_span,
+                            format!(
+                                "await occurs here on type `{target_ty}`, which {trait_explanation}"
+                            ),
+                        );
+                        err.span_note(
+                            span,
+                            format!(
+                                "future {trait_explanation} as it awaits another future which {trait_explanation}"
+                            ),
+                        );
+                    } else {
+                        // Look at the last interior type to get a span for the `.await`.
+                        explain_yield(interior_span, yield_span);
+                    }
+                }
+            }
+            CoroutineInteriorOrUpvar::Upvar(upvar_span) => {
+                // `Some((ref_ty, is_mut))` if `target_ty` is `&T` or `&mut T` and fails to impl `Send`
+                let non_send = match target_ty.kind() {
+                    ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(obligation) {
+                        Ok(eval) if !eval.may_apply() => Some((ref_ty, mutability.is_mut())),
+                        _ => None,
+                    },
+                    _ => None,
+                };
+
+                let (span_label, span_note) = match non_send {
+                    // if `target_ty` is `&T` or `&mut T` and fails to impl `Send`,
+                    // include suggestions to make `T: Sync` so that `&T: Send`,
+                    // or to make `T: Send` so that `&mut T: Send`
+                    Some((ref_ty, is_mut)) => {
+                        let ref_ty_trait = if is_mut { "Send" } else { "Sync" };
+                        let ref_kind = if is_mut { "&mut" } else { "&" };
+                        (
+                            format!(
+                                "has type `{target_ty}` which {trait_explanation}, because `{ref_ty}` is not `{ref_ty_trait}`"
+                            ),
+                            format!(
+                                "captured value {trait_explanation} because `{ref_kind}` references cannot be sent unless their referent is `{ref_ty_trait}`"
+                            ),
+                        )
+                    }
+                    None => (
+                        format!("has type `{target_ty}` which {trait_explanation}"),
+                        format!("captured value {trait_explanation}"),
+                    ),
+                };
+
+                let mut span = MultiSpan::from_span(upvar_span);
+                span.push_span_label(upvar_span, span_label);
+                err.span_note(span, span_note);
+            }
+        }
+
+        // Add a note for the item obligation that remains - normally a note pointing to the
+        // bound that introduced the obligation (e.g. `T: Send`).
+        debug!(?next_code);
+        self.note_obligation_cause_code(
+            obligation.cause.body_id,
+            err,
+            obligation.predicate,
+            obligation.param_env,
+            next_code.unwrap(),
+            &mut Vec::new(),
+            &mut Default::default(),
+        );
+    }
+
+    fn note_obligation_cause_code<T>(
+        &self,
+        body_id: LocalDefId,
+        err: &mut Diagnostic,
+        predicate: T,
+        param_env: ty::ParamEnv<'tcx>,
+        cause_code: &ObligationCauseCode<'tcx>,
+        obligated_types: &mut Vec<Ty<'tcx>>,
+        seen_requirements: &mut FxHashSet<DefId>,
+    ) where
+        T: ToPredicate<'tcx>,
+    {
+        let tcx = self.tcx;
+        let predicate = predicate.to_predicate(tcx);
+        match *cause_code {
+            ObligationCauseCode::ExprAssignable
+            | ObligationCauseCode::MatchExpressionArm { .. }
+            | ObligationCauseCode::Pattern { .. }
+            | ObligationCauseCode::IfExpression { .. }
+            | ObligationCauseCode::IfExpressionWithNoElse
+            | ObligationCauseCode::MainFunctionType
+            | ObligationCauseCode::StartFunctionType
+            | ObligationCauseCode::LangFunctionType(_)
+            | ObligationCauseCode::IntrinsicType
+            | ObligationCauseCode::MethodReceiver
+            | ObligationCauseCode::ReturnNoExpression
+            | ObligationCauseCode::UnifyReceiver(..)
+            | ObligationCauseCode::MiscObligation
+            | ObligationCauseCode::WellFormed(..)
+            | ObligationCauseCode::MatchImpl(..)
+            | ObligationCauseCode::ReturnValue(_)
+            | ObligationCauseCode::BlockTailExpression(..)
+            | ObligationCauseCode::AwaitableExpr(_)
+            | ObligationCauseCode::ForLoopIterator
+            | ObligationCauseCode::QuestionMark
+            | ObligationCauseCode::CheckAssociatedTypeBounds { .. }
+            | ObligationCauseCode::LetElse
+            | ObligationCauseCode::BinOp { .. }
+            | ObligationCauseCode::AscribeUserTypeProvePredicate(..)
+            | ObligationCauseCode::DropImpl
+            | ObligationCauseCode::ConstParam(_)
+            | ObligationCauseCode::ReferenceOutlivesReferent(..)
+            | ObligationCauseCode::ObjectTypeBound(..) => {}
+            ObligationCauseCode::RustCall => {
+                if let Some(pred) = predicate.to_opt_poly_trait_pred()
+                    && Some(pred.def_id()) == self.tcx.lang_items().sized_trait()
+                {
+                    err.note("argument required to be sized due to `extern \"rust-call\"` ABI");
+                }
+            }
+            ObligationCauseCode::SliceOrArrayElem => {
+                err.note("slice and array elements must have `Sized` type");
+            }
+            ObligationCauseCode::TupleElem => {
+                err.note("only the last element of a tuple may have a dynamically sized type");
+            }
+            ObligationCauseCode::ItemObligation(_)
+            | ObligationCauseCode::ExprItemObligation(..) => {
+                // We hold the `DefId` of the item introducing the obligation, but displaying it
+                // doesn't add user usable information. It always point at an associated item.
+            }
+            ObligationCauseCode::BindingObligation(item_def_id, span)
+            | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..) => {
+                let item_name = tcx.def_path_str(item_def_id);
+                let short_item_name = with_forced_trimmed_paths!(tcx.def_path_str(item_def_id));
+                let mut multispan = MultiSpan::from(span);
+                let sm = tcx.sess.source_map();
+                if let Some(ident) = tcx.opt_item_ident(item_def_id) {
+                    let same_line =
+                        match (sm.lookup_line(ident.span.hi()), sm.lookup_line(span.lo())) {
+                            (Ok(l), Ok(r)) => l.line == r.line,
+                            _ => true,
+                        };
+                    if ident.span.is_visible(sm) && !ident.span.overlaps(span) && !same_line {
+                        multispan.push_span_label(
+                            ident.span,
+                            format!(
+                                "required by a bound in this {}",
+                                tcx.def_kind(item_def_id).descr(item_def_id)
+                            ),
+                        );
+                    }
+                }
+                let descr = format!("required by a bound in `{item_name}`");
+                if span.is_visible(sm) {
+                    let msg = format!("required by this bound in `{short_item_name}`");
+                    multispan.push_span_label(span, msg);
+                    err.span_note(multispan, descr);
+                    if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
+                        && let ty::ClauseKind::Trait(trait_pred) = clause
+                    {
+                        let def_id = trait_pred.def_id();
+                        let visible_item = if let Some(local) = def_id.as_local() {
+                            // Check for local traits being reachable.
+                            let vis = &self.tcx.resolutions(()).effective_visibilities;
+                            // Account for non-`pub` traits in the root of the local crate.
+                            let is_locally_reachable = self.tcx.parent(def_id).is_crate_root();
+                            vis.is_reachable(local) || is_locally_reachable
+                        } else {
+                            // Check for foreign traits being reachable.
+                            self.tcx.visible_parent_map(()).get(&def_id).is_some()
+                        };
+                        if Some(def_id) == self.tcx.lang_items().sized_trait()
+                            && let Some(hir::Node::TraitItem(hir::TraitItem {
+                                ident,
+                                kind: hir::TraitItemKind::Type(bounds, None),
+                                ..
+                            })) = tcx.hir().get_if_local(item_def_id)
+                            // Do not suggest relaxing if there is an explicit `Sized` obligation.
+                            && !bounds.iter()
+                                .filter_map(|bound| bound.trait_ref())
+                                .any(|tr| tr.trait_def_id() == self.tcx.lang_items().sized_trait())
+                        {
+                            let (span, separator) = if let [.., last] = bounds {
+                                (last.span().shrink_to_hi(), " +")
+                            } else {
+                                (ident.span.shrink_to_hi(), ":")
+                            };
+                            err.span_suggestion_verbose(
+                                span,
+                                "consider relaxing the implicit `Sized` restriction",
+                                format!("{separator} ?Sized"),
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                        if let DefKind::Trait = tcx.def_kind(item_def_id)
+                            && !visible_item
+                        {
+                            err.note(format!(
+                                "`{short_item_name}` is a \"sealed trait\", because to implement \
+                                 it you also need to implement `{}`, which is not accessible; \
+                                 this is usually done to force you to use one of the provided \
+                                 types that already implement it",
+                                with_no_trimmed_paths!(tcx.def_path_str(def_id)),
+                            ));
+                            let impls_of = tcx.trait_impls_of(def_id);
+                            let impls = impls_of
+                                .non_blanket_impls()
+                                .values()
+                                .flatten()
+                                .chain(impls_of.blanket_impls().iter())
+                                .collect::<Vec<_>>();
+                            if !impls.is_empty() {
+                                let len = impls.len();
+                                let mut types = impls
+                                    .iter()
+                                    .map(|t| {
+                                        with_no_trimmed_paths!(format!(
+                                            "  {}",
+                                            tcx.type_of(*t).instantiate_identity(),
+                                        ))
+                                    })
+                                    .collect::<Vec<_>>();
+                                let post = if types.len() > 9 {
+                                    types.truncate(8);
+                                    format!("\nand {} others", len - 8)
+                                } else {
+                                    String::new()
+                                };
+                                err.help(format!(
+                                    "the following type{} implement{} the trait:\n{}{post}",
+                                    pluralize!(len),
+                                    if len == 1 { "s" } else { "" },
+                                    types.join("\n"),
+                                ));
+                            }
+                        }
+                    }
+                } else {
+                    err.span_note(tcx.def_span(item_def_id), descr);
+                }
+            }
+            ObligationCauseCode::Coercion { source, target } => {
+                let mut file = None;
+                let source =
+                    self.tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file);
+                let target =
+                    self.tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file);
+                err.note(with_forced_trimmed_paths!(format!(
+                    "required for the cast from `{source}` to `{target}`",
+                )));
+                if let Some(file) = file {
+                    err.note(format!(
+                        "the full name for the type has been written to '{}'",
+                        file.display(),
+                    ));
+                }
+            }
+            ObligationCauseCode::RepeatElementCopy {
+                is_constable,
+                elt_type,
+                elt_span,
+                elt_stmt_span,
+            } => {
+                err.note(
+                    "the `Copy` trait is required because this value will be copied for each element of the array",
+                );
+                let value_kind = match is_constable {
+                    IsConstable::Fn => Some("the result of the function call"),
+                    IsConstable::Ctor => Some("the result of the constructor"),
+                    _ => None,
+                };
+                let sm = tcx.sess.source_map();
+                if let Some(value_kind) = value_kind
+                    && let Ok(snip) = sm.span_to_snippet(elt_span)
+                {
+                    let help_msg = format!(
+                        "consider creating a new `const` item and initializing it with {value_kind} \
+                        to be used in the repeat position"
+                    );
+                    let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default();
+                    err.multipart_suggestion(
+                        help_msg,
+                        vec![
+                            (
+                                elt_stmt_span.shrink_to_lo(),
+                                format!(
+                                    "const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}"
+                                ),
+                            ),
+                            (elt_span, "ARRAY_REPEAT_VALUE".to_string()),
+                        ],
+                        Applicability::MachineApplicable,
+                    );
+                }
+
+                if self.tcx.sess.is_nightly_build()
+                    && matches!(is_constable, IsConstable::Fn | IsConstable::Ctor)
+                {
+                    err.help(
+                        "create an inline `const` block, see RFC #2920 \
+                         <https://github.com/rust-lang/rfcs/pull/2920> for more information",
+                    );
+                }
+            }
+            ObligationCauseCode::VariableType(hir_id) => {
+                let parent_node = self.tcx.hir().parent_id(hir_id);
+                match self.tcx.opt_hir_node(parent_node) {
+                    Some(Node::Local(hir::Local { ty: Some(ty), .. })) => {
+                        err.span_suggestion_verbose(
+                            ty.span.shrink_to_lo(),
+                            "consider borrowing here",
+                            "&",
+                            Applicability::MachineApplicable,
+                        );
+                        err.note("all local variables must have a statically known size");
+                    }
+                    Some(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.
+                        err.span_suggestion_verbose(
+                            span.shrink_to_lo(),
+                            "consider borrowing here",
+                            "&",
+                            Applicability::MachineApplicable,
+                        );
+                        err.note("all local variables must have a statically known size");
+                    }
+                    Some(Node::Param(param)) => {
+                        err.span_suggestion_verbose(
+                            param.ty_span.shrink_to_lo(),
+                            "function arguments must have a statically known size, borrowed types \
+                            always have a known size",
+                            "&",
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                    _ => {
+                        err.note("all local variables must have a statically known size");
+                    }
+                }
+                if !self.tcx.features().unsized_locals {
+                    err.help("unsized locals are gated as an unstable feature");
+                }
+            }
+            ObligationCauseCode::SizedArgumentType(ty_span) => {
+                if let Some(span) = ty_span {
+                    if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
+                        && let ty::ClauseKind::Trait(trait_pred) = clause
+                        && let ty::Dynamic(..) = trait_pred.self_ty().kind()
+                    {
+                        let span = if let Ok(snippet) =
+                            self.tcx.sess.source_map().span_to_snippet(span)
+                            && snippet.starts_with("dyn ")
+                        {
+                            let pos = snippet.len() - snippet[3..].trim_start().len();
+                            span.with_hi(span.lo() + BytePos(pos as u32))
+                        } else {
+                            span.shrink_to_lo()
+                        };
+                        err.span_suggestion_verbose(
+                            span,
+                            "you can use `impl Trait` as the argument type",
+                            "impl ".to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                    err.span_suggestion_verbose(
+                        span.shrink_to_lo(),
+                        "function arguments must have a statically known size, borrowed types \
+                         always have a known size",
+                        "&",
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    err.note("all function arguments must have a statically known size");
+                }
+                if tcx.sess.opts.unstable_features.is_nightly_build()
+                    && !self.tcx.features().unsized_fn_params
+                {
+                    err.help("unsized fn params are gated as an unstable feature");
+                }
+            }
+            ObligationCauseCode::SizedReturnType => {
+                err.note("the return type of a function must have a statically known size");
+            }
+            ObligationCauseCode::SizedYieldType => {
+                err.note("the yield type of a coroutine must have a statically known size");
+            }
+            ObligationCauseCode::AssignmentLhsSized => {
+                err.note("the left-hand-side of an assignment must have a statically known size");
+            }
+            ObligationCauseCode::TupleInitializerSized => {
+                err.note("tuples must have a statically known size to be initialized");
+            }
+            ObligationCauseCode::StructInitializerSized => {
+                err.note("structs must have a statically known size to be initialized");
+            }
+            ObligationCauseCode::FieldSized { adt_kind: ref item, last, span } => {
+                match *item {
+                    AdtKind::Struct => {
+                        if last {
+                            err.note(
+                                "the last field of a packed struct may only have a \
+                                dynamically sized type if it does not need drop to be run",
+                            );
+                        } else {
+                            err.note(
+                                "only the last field of a struct may have a dynamically sized type",
+                            );
+                        }
+                    }
+                    AdtKind::Union => {
+                        err.note("no field of a union may have a dynamically sized type");
+                    }
+                    AdtKind::Enum => {
+                        err.note("no field of an enum variant may have a dynamically sized type");
+                    }
+                }
+                err.help("change the field's type to have a statically known size");
+                err.span_suggestion(
+                    span.shrink_to_lo(),
+                    "borrowed types always have a statically known size",
+                    "&",
+                    Applicability::MachineApplicable,
+                );
+                err.multipart_suggestion(
+                    "the `Box` type always has a statically known size and allocates its contents \
+                     in the heap",
+                    vec![
+                        (span.shrink_to_lo(), "Box<".to_string()),
+                        (span.shrink_to_hi(), ">".to_string()),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+            }
+            ObligationCauseCode::ConstSized => {
+                err.note("constant expressions must have a statically known size");
+            }
+            ObligationCauseCode::InlineAsmSized => {
+                err.note("all inline asm arguments must have a statically known size");
+            }
+            ObligationCauseCode::SizedClosureCapture(closure_def_id) => {
+                err.note(
+                    "all values captured by value by a closure must have a statically known size",
+                );
+                let hir::ExprKind::Closure(closure) =
+                    self.tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind
+                else {
+                    bug!("expected closure in SizedClosureCapture obligation");
+                };
+                if let hir::CaptureBy::Value { .. } = closure.capture_clause
+                    && let Some(span) = closure.fn_arg_span
+                {
+                    err.span_label(span, "this closure captures all values by move");
+                }
+            }
+            ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => {
+                let what = match self.tcx.coroutine_kind(coroutine_def_id) {
+                    None
+                    | Some(hir::CoroutineKind::Coroutine(_))
+                    | Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => {
+                        "yield"
+                    }
+                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
+                        "await"
+                    }
+                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => {
+                        "yield`/`await"
+                    }
+                };
+                err.note(format!(
+                    "all values live across `{what}` must have a statically known size"
+                ));
+            }
+            ObligationCauseCode::SharedStatic => {
+                err.note("shared static variables must have a type that implements `Sync`");
+            }
+            ObligationCauseCode::BuiltinDerivedObligation(ref data) => {
+                let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred);
+                let ty = parent_trait_ref.skip_binder().self_ty();
+                if parent_trait_ref.references_error() {
+                    // NOTE(eddyb) this was `.cancel()`, but `err`
+                    // is borrowed, so we can't fully defuse it.
+                    err.downgrade_to_delayed_bug();
+                    return;
+                }
+
+                // If the obligation for a tuple is set directly by a Coroutine or Closure,
+                // then the tuple must be the one containing capture types.
+                let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) {
+                    false
+                } else {
+                    if let ObligationCauseCode::BuiltinDerivedObligation(data) = &*data.parent_code
+                    {
+                        let parent_trait_ref =
+                            self.resolve_vars_if_possible(data.parent_trait_pred);
+                        let nested_ty = parent_trait_ref.skip_binder().self_ty();
+                        matches!(nested_ty.kind(), ty::Coroutine(..))
+                            || matches!(nested_ty.kind(), ty::Closure(..))
+                    } else {
+                        false
+                    }
+                };
+
+                // Don't print the tuple of capture types
+                'print: {
+                    if !is_upvar_tys_infer_tuple {
+                        let mut file = None;
+                        let ty_str = self.tcx.short_ty_string(ty, &mut file);
+                        let msg = format!("required because it appears within the type `{ty_str}`");
+                        match ty.kind() {
+                            ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) {
+                                Some(ident) => err.span_note(ident.span, msg),
+                                None => err.note(msg),
+                            },
+                            ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
+                                // If the previous type is async fn, this is the future generated by the body of an async function.
+                                // Avoid printing it twice (it was already printed in the `ty::Coroutine` arm below).
+                                let is_future = tcx.ty_is_opaque_future(ty);
+                                debug!(
+                                    ?obligated_types,
+                                    ?is_future,
+                                    "note_obligation_cause_code: check for async fn"
+                                );
+                                if is_future
+                                    && obligated_types.last().is_some_and(|ty| match ty.kind() {
+                                        ty::Coroutine(last_def_id, ..) => {
+                                            tcx.coroutine_is_async(*last_def_id)
+                                        }
+                                        _ => false,
+                                    })
+                                {
+                                    break 'print;
+                                }
+                                err.span_note(self.tcx.def_span(def_id), msg)
+                            }
+                            ty::CoroutineWitness(def_id, args) => {
+                                use std::fmt::Write;
+
+                                // FIXME: this is kind of an unusual format for rustc, can we make it more clear?
+                                // Maybe we should just remove this note altogether?
+                                // FIXME: only print types which don't meet the trait requirement
+                                let mut msg =
+                                    "required because it captures the following types: ".to_owned();
+                                for bty in tcx.coroutine_hidden_types(*def_id) {
+                                    let ty = bty.instantiate(tcx, args);
+                                    write!(msg, "`{ty}`, ").unwrap();
+                                }
+                                err.note(msg.trim_end_matches(", ").to_string())
+                            }
+                            ty::Coroutine(def_id, _) => {
+                                let sp = self.tcx.def_span(def_id);
+
+                                // Special-case this to say "async block" instead of `[static coroutine]`.
+                                let kind = tcx.coroutine_kind(def_id).unwrap();
+                                err.span_note(
+                                    sp,
+                                    with_forced_trimmed_paths!(format!(
+                                        "required because it's used within this {kind:#}",
+                                    )),
+                                )
+                            }
+                            ty::Closure(def_id, _) => err.span_note(
+                                self.tcx.def_span(def_id),
+                                "required because it's used within this closure",
+                            ),
+                            ty::Str => err.note("`str` is considered to contain a `[u8]` slice for auto trait purposes"),
+                            _ => err.note(msg),
+                        };
+                    }
+                }
+
+                obligated_types.push(ty);
+
+                let parent_predicate = parent_trait_ref;
+                if !self.is_recursive_obligation(obligated_types, &data.parent_code) {
+                    // #74711: avoid a stack overflow
+                    ensure_sufficient_stack(|| {
+                        self.note_obligation_cause_code(
+                            body_id,
+                            err,
+                            parent_predicate,
+                            param_env,
+                            &data.parent_code,
+                            obligated_types,
+                            seen_requirements,
+                        )
+                    });
+                } else {
+                    ensure_sufficient_stack(|| {
+                        self.note_obligation_cause_code(
+                            body_id,
+                            err,
+                            parent_predicate,
+                            param_env,
+                            cause_code.peel_derives(),
+                            obligated_types,
+                            seen_requirements,
+                        )
+                    });
+                }
+            }
+            ObligationCauseCode::ImplDerivedObligation(ref data) => {
+                let mut parent_trait_pred =
+                    self.resolve_vars_if_possible(data.derived.parent_trait_pred);
+                let parent_def_id = parent_trait_pred.def_id();
+                let mut file = None;
+                let self_ty =
+                    self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file);
+                let msg = format!(
+                    "required for `{self_ty}` to implement `{}`",
+                    parent_trait_pred.print_modifiers_and_trait_path()
+                );
+                let mut is_auto_trait = false;
+                match self.tcx.hir().get_if_local(data.impl_or_alias_def_id) {
+                    Some(Node::Item(hir::Item {
+                        kind: hir::ItemKind::Trait(is_auto, ..),
+                        ident,
+                        ..
+                    })) => {
+                        // FIXME: we should do something else so that it works even on crate foreign
+                        // auto traits.
+                        is_auto_trait = matches!(is_auto, hir::IsAuto::Yes);
+                        err.span_note(ident.span, msg);
+                    }
+                    Some(Node::Item(hir::Item {
+                        kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
+                        ..
+                    })) => {
+                        let mut spans = Vec::with_capacity(2);
+                        if let Some(trait_ref) = of_trait {
+                            spans.push(trait_ref.path.span);
+                        }
+                        spans.push(self_ty.span);
+                        let mut spans: MultiSpan = spans.into();
+                        if matches!(
+                            self_ty.span.ctxt().outer_expn_data().kind,
+                            ExpnKind::Macro(MacroKind::Derive, _)
+                        ) || matches!(
+                            of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind),
+                            Some(ExpnKind::Macro(MacroKind::Derive, _))
+                        ) {
+                            spans.push_span_label(
+                                data.span,
+                                "unsatisfied trait bound introduced in this `derive` macro",
+                            );
+                        } else if !data.span.is_dummy() && !data.span.overlaps(self_ty.span) {
+                            spans.push_span_label(
+                                data.span,
+                                "unsatisfied trait bound introduced here",
+                            );
+                        }
+                        err.span_note(spans, msg);
+                    }
+                    _ => {
+                        err.note(msg);
+                    }
+                };
+
+                if let Some(file) = file {
+                    err.note(format!(
+                        "the full type name has been written to '{}'",
+                        file.display(),
+                    ));
+                }
+                let mut parent_predicate = parent_trait_pred;
+                let mut data = &data.derived;
+                let mut count = 0;
+                seen_requirements.insert(parent_def_id);
+                if is_auto_trait {
+                    // We don't want to point at the ADT saying "required because it appears within
+                    // the type `X`", like we would otherwise do in test `supertrait-auto-trait.rs`.
+                    while let ObligationCauseCode::BuiltinDerivedObligation(derived) =
+                        &*data.parent_code
+                    {
+                        let child_trait_ref =
+                            self.resolve_vars_if_possible(derived.parent_trait_pred);
+                        let child_def_id = child_trait_ref.def_id();
+                        if seen_requirements.insert(child_def_id) {
+                            break;
+                        }
+                        data = derived;
+                        parent_predicate = child_trait_ref.to_predicate(tcx);
+                        parent_trait_pred = child_trait_ref;
+                    }
+                }
+                while let ObligationCauseCode::ImplDerivedObligation(child) = &*data.parent_code {
+                    // Skip redundant recursive obligation notes. See `ui/issue-20413.rs`.
+                    let child_trait_pred =
+                        self.resolve_vars_if_possible(child.derived.parent_trait_pred);
+                    let child_def_id = child_trait_pred.def_id();
+                    if seen_requirements.insert(child_def_id) {
+                        break;
+                    }
+                    count += 1;
+                    data = &child.derived;
+                    parent_predicate = child_trait_pred.to_predicate(tcx);
+                    parent_trait_pred = child_trait_pred;
+                }
+                if count > 0 {
+                    err.note(format!(
+                        "{} redundant requirement{} hidden",
+                        count,
+                        pluralize!(count)
+                    ));
+                    let mut file = None;
+                    let self_ty = self
+                        .tcx
+                        .short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file);
+                    err.note(format!(
+                        "required for `{self_ty}` to implement `{}`",
+                        parent_trait_pred.print_modifiers_and_trait_path()
+                    ));
+                    if let Some(file) = file {
+                        err.note(format!(
+                            "the full type name has been written to '{}'",
+                            file.display(),
+                        ));
+                    }
+                }
+                // #74711: avoid a stack overflow
+                ensure_sufficient_stack(|| {
+                    self.note_obligation_cause_code(
+                        body_id,
+                        err,
+                        parent_predicate,
+                        param_env,
+                        &data.parent_code,
+                        obligated_types,
+                        seen_requirements,
+                    )
+                });
+            }
+            ObligationCauseCode::DerivedObligation(ref data) => {
+                let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred);
+                let parent_predicate = parent_trait_ref;
+                // #74711: avoid a stack overflow
+                ensure_sufficient_stack(|| {
+                    self.note_obligation_cause_code(
+                        body_id,
+                        err,
+                        parent_predicate,
+                        param_env,
+                        &data.parent_code,
+                        obligated_types,
+                        seen_requirements,
+                    )
+                });
+            }
+            ObligationCauseCode::TypeAlias(ref nested, span, def_id) => {
+                // #74711: avoid a stack overflow
+                ensure_sufficient_stack(|| {
+                    self.note_obligation_cause_code(
+                        body_id,
+                        err,
+                        predicate,
+                        param_env,
+                        nested,
+                        obligated_types,
+                        seen_requirements,
+                    )
+                });
+                let mut multispan = MultiSpan::from(span);
+                multispan.push_span_label(span, "required by this bound");
+                err.span_note(
+                    multispan,
+                    format!(
+                        "required by a bound on the type alias `{}`",
+                        self.infcx.tcx.item_name(def_id)
+                    ),
+                );
+            }
+            ObligationCauseCode::FunctionArgumentObligation {
+                arg_hir_id,
+                call_hir_id,
+                ref parent_code,
+                ..
+            } => {
+                self.note_function_argument_obligation(
+                    body_id,
+                    err,
+                    arg_hir_id,
+                    parent_code,
+                    param_env,
+                    predicate,
+                    call_hir_id,
+                );
+                ensure_sufficient_stack(|| {
+                    self.note_obligation_cause_code(
+                        body_id,
+                        err,
+                        predicate,
+                        param_env,
+                        parent_code,
+                        obligated_types,
+                        seen_requirements,
+                    )
+                });
+            }
+            ObligationCauseCode::CompareImplItemObligation { trait_item_def_id, kind, .. } => {
+                let item_name = self.tcx.item_name(trait_item_def_id);
+                let msg = format!(
+                    "the requirement `{predicate}` appears on the `impl`'s {kind} \
+                     `{item_name}` but not on the corresponding trait's {kind}",
+                );
+                let sp = self
+                    .tcx
+                    .opt_item_ident(trait_item_def_id)
+                    .map(|i| i.span)
+                    .unwrap_or_else(|| self.tcx.def_span(trait_item_def_id));
+                let mut assoc_span: MultiSpan = sp.into();
+                assoc_span.push_span_label(
+                    sp,
+                    format!("this trait's {kind} doesn't have the requirement `{predicate}`"),
+                );
+                if let Some(ident) = self
+                    .tcx
+                    .opt_associated_item(trait_item_def_id)
+                    .and_then(|i| self.tcx.opt_item_ident(i.container_id(self.tcx)))
+                {
+                    assoc_span.push_span_label(ident.span, "in this trait");
+                }
+                err.span_note(assoc_span, msg);
+            }
+            ObligationCauseCode::TrivialBound => {
+                err.help("see issue #48214");
+                if tcx.sess.opts.unstable_features.is_nightly_build() {
+                    err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable");
+                }
+            }
+            ObligationCauseCode::OpaqueReturnType(expr_info) => {
+                if let Some((expr_ty, expr_span)) = expr_info {
+                    let expr_ty = with_forced_trimmed_paths!(self.ty_to_string(expr_ty));
+                    err.span_label(
+                        expr_span,
+                        with_forced_trimmed_paths!(format!(
+                            "return type was inferred to be `{expr_ty}` here",
+                        )),
+                    );
+                }
+            }
+        }
+    }
+
+    #[instrument(
+        level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty())
+    )]
+    fn suggest_await_before_try(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+        span: Span,
+    ) {
+        if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) =
+            self.tcx.coroutine_kind(obligation.cause.body_id)
+        {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+
+            let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
+            let impls_future = self.type_implements_trait(
+                future_trait,
+                [self.tcx.instantiate_bound_regions_with_erased(self_ty)],
+                obligation.param_env,
+            );
+            if !impls_future.must_apply_modulo_regions() {
+                return;
+            }
+
+            let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
+            // `<T as Future>::Output`
+            let projection_ty = trait_pred.map_bound(|trait_pred| {
+                Ty::new_projection(
+                    self.tcx,
+                    item_def_id,
+                    // Future::Output has no args
+                    [trait_pred.self_ty()],
+                )
+            });
+            let InferOk { value: projection_ty, .. } =
+                self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
+
+            debug!(
+                normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
+            );
+            let try_obligation = self.mk_trait_obligation_with_new_self_ty(
+                obligation.param_env,
+                trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
+            );
+            debug!(try_trait_obligation = ?try_obligation);
+            if self.predicate_may_hold(&try_obligation)
+                && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+                && snippet.ends_with('?')
+            {
+                err.span_suggestion_verbose(
+                    span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
+                    "consider `await`ing on the `Future`",
+                    ".await",
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+
+    fn suggest_floating_point_literal(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_ref: &ty::PolyTraitRef<'tcx>,
+    ) {
+        let rhs_span = match obligation.cause.code() {
+            ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => {
+                span
+            }
+            _ => return,
+        };
+        if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind()
+            && let ty::Infer(InferTy::IntVar(_)) = trait_ref.skip_binder().args.type_at(1).kind()
+        {
+            err.span_suggestion_verbose(
+                rhs_span.shrink_to_hi(),
+                "consider using a floating-point literal by writing it with `.0`",
+                ".0",
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+
+    fn suggest_derive(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) {
+        let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
+            return;
+        };
+        let (adt, args) = match trait_pred.skip_binder().self_ty().kind() {
+            ty::Adt(adt, args) if adt.did().is_local() => (adt, args),
+            _ => return,
+        };
+        let can_derive = {
+            let is_derivable_trait = match diagnostic_name {
+                sym::Default => !adt.is_enum(),
+                sym::PartialEq | sym::PartialOrd => {
+                    let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1);
+                    trait_pred.skip_binder().self_ty() == rhs_ty
+                }
+                sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
+                _ => false,
+            };
+            is_derivable_trait &&
+                // Ensure all fields impl the trait.
+                adt.all_fields().all(|field| {
+                    let field_ty = ty::GenericArg::from(field.ty(self.tcx, args));
+                    let trait_args = match diagnostic_name {
+                        sym::PartialEq | sym::PartialOrd => {
+                            Some(field_ty)
+                        }
+                        _ => None,
+                    };
+                    // Also add host param, if present
+                    let host = self.tcx.generics_of(trait_pred.def_id()).host_effect_index.map(|idx| trait_pred.skip_binder().trait_ref.args[idx]);
+                    let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
+                        trait_ref: ty::TraitRef::new(self.tcx,
+                            trait_pred.def_id(),
+                            [field_ty].into_iter().chain(trait_args).chain(host),
+                        ),
+                        ..*tr
+                    });
+                    let field_obl = Obligation::new(
+                        self.tcx,
+                        obligation.cause.clone(),
+                        obligation.param_env,
+                        trait_pred,
+                    );
+                    self.predicate_must_hold_modulo_regions(&field_obl)
+                })
+        };
+        if can_derive {
+            err.span_suggestion_verbose(
+                self.tcx.def_span(adt.did()).shrink_to_lo(),
+                format!(
+                    "consider annotating `{}` with `#[derive({})]`",
+                    trait_pred.skip_binder().self_ty(),
+                    diagnostic_name,
+                ),
+                // FIXME(effects, const_trait_impl) derive_const as suggestion?
+                format!("#[derive({diagnostic_name})]\n"),
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+
+    fn suggest_dereferencing_index(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) {
+        if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code()
+            && self
+                .tcx
+                .is_diagnostic_item(sym::SliceIndex, trait_pred.skip_binder().trait_ref.def_id)
+            && let ty::Slice(_) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind()
+            && let ty::Ref(_, inner_ty, _) = trait_pred.skip_binder().self_ty().kind()
+            && let ty::Uint(ty::UintTy::Usize) = inner_ty.kind()
+        {
+            err.span_suggestion_verbose(
+                obligation.cause.span.shrink_to_lo(),
+                "dereference this index",
+                '*',
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+
+    fn note_function_argument_obligation(
+        &self,
+        body_id: LocalDefId,
+        err: &mut Diagnostic,
+        arg_hir_id: HirId,
+        parent_code: &ObligationCauseCode<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        failed_pred: ty::Predicate<'tcx>,
+        call_hir_id: HirId,
+    ) {
+        let tcx = self.tcx;
+        if let Some(Node::Expr(expr)) = tcx.opt_hir_node(arg_hir_id)
+            && let Some(typeck_results) = &self.typeck_results
+        {
+            if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
+                let inner_expr = expr.peel_blocks();
+                let ty = typeck_results
+                    .expr_ty_adjusted_opt(inner_expr)
+                    .unwrap_or(Ty::new_misc_error(tcx));
+                let span = inner_expr.span;
+                if Some(span) != err.span.primary_span() {
+                    err.span_label(
+                        span,
+                        if ty.references_error() {
+                            String::new()
+                        } else {
+                            let ty = with_forced_trimmed_paths!(self.ty_to_string(ty));
+                            format!("this tail expression is of type `{ty}`")
+                        },
+                    );
+                    if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder()
+                        && let ty::ClauseKind::Trait(pred) = clause
+                        && [
+                            tcx.lang_items().fn_once_trait(),
+                            tcx.lang_items().fn_mut_trait(),
+                            tcx.lang_items().fn_trait(),
+                        ]
+                        .contains(&Some(pred.def_id()))
+                    {
+                        if let [stmt, ..] = block.stmts
+                            && let hir::StmtKind::Semi(value) = stmt.kind
+                            && let hir::ExprKind::Closure(hir::Closure {
+                                body, fn_decl_span, ..
+                            }) = value.kind
+                            && let body = tcx.hir().body(*body)
+                            && !matches!(body.value.kind, hir::ExprKind::Block(..))
+                        {
+                            // Check if the failed predicate was an expectation of a closure type
+                            // and if there might have been a `{ |args|` typo instead of `|args| {`.
+                            err.multipart_suggestion(
+                                "you might have meant to open the closure body instead of placing \
+                                 a closure within a block",
+                                vec![
+                                    (expr.span.with_hi(value.span.lo()), String::new()),
+                                    (fn_decl_span.shrink_to_hi(), " {".to_string()),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+                        } else {
+                            // Maybe the bare block was meant to be a closure.
+                            err.span_suggestion_verbose(
+                                expr.span.shrink_to_lo(),
+                                "you might have meant to create the closure instead of a block",
+                                format!(
+                                    "|{}| ",
+                                    (0..pred.trait_ref.args.len() - 1)
+                                        .map(|_| "_")
+                                        .collect::<Vec<_>>()
+                                        .join(", ")
+                                ),
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
+                }
+            }
+
+            // FIXME: visit the ty to see if there's any closure involved, and if there is,
+            // check whether its evaluated return type is the same as the one corresponding
+            // to an associated type (as seen from `trait_pred`) in the predicate. Like in
+            // trait_pred `S: Sum<<Self as Iterator>::Item>` and predicate `i32: Sum<&()>`
+            let mut type_diffs = vec![];
+            if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code
+                && let Some(node_args) = typeck_results.node_args_opt(call_hir_id)
+                && let where_clauses =
+                    self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args)
+                && let Some(where_pred) = where_clauses.predicates.get(*idx)
+            {
+                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,
+                    );
+
+                    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()
+                {
+                    type_diffs = vec![Sorts(ty::error::ExpectedFound {
+                        expected: Ty::new_alias(
+                            self.tcx,
+                            ty::Projection,
+                            where_pred.skip_binder().projection_ty,
+                        ),
+                        found,
+                    })];
+                }
+            }
+            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 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 Some(binding_expr) = local.init
+            {
+                // If the expression we're calling on is a binding, we want to point at the
+                // `let` when talking about the type. Otherwise we'll point at every part
+                // of the method chain with the type.
+                self.point_at_chain(binding_expr, typeck_results, type_diffs, param_env, err);
+            } else {
+                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
+        {
+            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 hir::ExprKind::Call(hir::Expr { span, .. }, _)
+            | hir::ExprKind::MethodCall(
+                hir::PathSegment { ident: Ident { span, .. }, .. },
+                ..,
+            ) = expr.kind
+            {
+                if Some(*span) != err.span.primary_span() {
+                    err.span_label(*span, "required by a bound introduced by this call");
+                }
+            }
+
+            if let hir::ExprKind::MethodCall(_, expr, ..) = expr.kind {
+                self.suggest_option_method_if_applicable(failed_pred, param_env, err, expr);
+            }
+        }
+    }
+
+    fn suggest_option_method_if_applicable(
+        &self,
+        failed_pred: ty::Predicate<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+    ) {
+        let tcx = self.tcx;
+        let infcx = self.infcx;
+        let Some(typeck_results) = self.typeck_results.as_ref() else { return };
+
+        // Make sure we're dealing with the `Option` type.
+        let Some(option_ty_adt) = typeck_results.expr_ty_adjusted(expr).ty_adt_def() else {
+            return;
+        };
+        if !tcx.is_diagnostic_item(sym::Option, option_ty_adt.did()) {
+            return;
+        }
+
+        // Given the predicate `fn(&T): FnOnce<(U,)>`, extract `fn(&T)` and `(U,)`,
+        // then suggest `Option::as_deref(_mut)` if `U` can deref to `T`
+        if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, .. }))
+            = failed_pred.kind().skip_binder()
+            && tcx.is_fn_trait(trait_ref.def_id)
+            && let [self_ty, found_ty] = trait_ref.args.as_slice()
+            && let Some(fn_ty) = self_ty.as_type().filter(|ty| ty.is_fn())
+            && let fn_sig @ ty::FnSig {
+                abi: abi::Abi::Rust,
+                c_variadic: false,
+                unsafety: hir::Unsafety::Normal,
+                ..
+            } = fn_ty.fn_sig(tcx).skip_binder()
+
+            // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T`
+            && let Some(&ty::Ref(_, target_ty, needs_mut)) = fn_sig.inputs().first().map(|t| t.kind())
+            && !target_ty.has_escaping_bound_vars()
+
+            // Extract first tuple element out of fn trait, e.g. `FnOnce<(U,)>` -> `U`
+            && let Some(ty::Tuple(tys)) = found_ty.as_type().map(Ty::kind)
+            && let &[found_ty] = tys.as_slice()
+            && !found_ty.has_escaping_bound_vars()
+
+            // Extract `<U as Deref>::Target` assoc type and check that it is `T`
+            && let Some(deref_target_did) = tcx.lang_items().deref_target()
+            && let projection = Ty::new_projection(tcx,deref_target_did, tcx.mk_args(&[ty::GenericArg::from(found_ty)]))
+            && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection)
+            && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation))
+            && infcx.can_eq(param_env, deref_target, target_ty)
+        {
+            let help = if let hir::Mutability::Mut = needs_mut
+                && let Some(deref_mut_did) = tcx.lang_items().deref_mut_trait()
+                && infcx
+                    .type_implements_trait(deref_mut_did, iter::once(found_ty), param_env)
+                    .must_apply_modulo_regions()
+            {
+                Some(("call `Option::as_deref_mut()` first", ".as_deref_mut()"))
+            } else if let hir::Mutability::Not = needs_mut {
+                Some(("call `Option::as_deref()` first", ".as_deref()"))
+            } else {
+                None
+            };
+
+            if let Some((msg, sugg)) = help {
+                err.span_suggestion_with_style(
+                    expr.span.shrink_to_hi(),
+                    msg,
+                    sugg,
+                    Applicability::MaybeIncorrect,
+                    SuggestionStyle::ShowAlways,
+                );
+            }
+        }
+    }
+
+    fn look_for_iterator_item_mistakes(
+        &self,
+        assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
+        typeck_results: &TypeckResults<'tcx>,
+        type_diffs: &[TypeError<'tcx>],
+        param_env: ty::ParamEnv<'tcx>,
+        path_segment: &hir::PathSegment<'_>,
+        args: &[hir::Expr<'_>],
+        err: &mut Diagnostic,
+    ) {
+        let tcx = self.tcx;
+        // Special case for iterator chains, we look at potential failures of `Iterator::Item`
+        // not being `: Clone` and `Iterator::map` calls with spurious trailing `;`.
+        for entry in assocs_in_this_method {
+            let Some((_span, (def_id, ty))) = entry else {
+                continue;
+            };
+            for diff in type_diffs {
+                let Sorts(expected_found) = diff else {
+                    continue;
+                };
+                if tcx.is_diagnostic_item(sym::IteratorItem, *def_id)
+                    && path_segment.ident.name == sym::map
+                    && self.can_eq(param_env, expected_found.found, *ty)
+                    && let [arg] = args
+                    && let hir::ExprKind::Closure(closure) = arg.kind
+                {
+                    let body = tcx.hir().body(closure.body);
+                    if let hir::ExprKind::Block(block, None) = body.value.kind
+                        && let None = block.expr
+                        && let [.., stmt] = block.stmts
+                        && let hir::StmtKind::Semi(expr) = stmt.kind
+                        // FIXME: actually check the expected vs found types, but right now
+                        // the expected is a projection that we need to resolve.
+                        // && let Some(tail_ty) = typeck_results.expr_ty_opt(expr)
+                        && expected_found.found.is_unit()
+                    {
+                        err.span_suggestion_verbose(
+                            expr.span.shrink_to_hi().with_hi(stmt.span.hi()),
+                            "consider removing this semicolon",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                    let expr = if let hir::ExprKind::Block(block, None) = body.value.kind
+                        && let Some(expr) = block.expr
+                    {
+                        expr
+                    } else {
+                        body.value
+                    };
+                    if let hir::ExprKind::MethodCall(path_segment, rcvr, [], span) = expr.kind
+                        && path_segment.ident.name == sym::clone
+                        && let Some(expr_ty) = typeck_results.expr_ty_opt(expr)
+                        && let Some(rcvr_ty) = typeck_results.expr_ty_opt(rcvr)
+                        && self.can_eq(param_env, expr_ty, rcvr_ty)
+                        && let ty::Ref(_, ty, _) = expr_ty.kind()
+                    {
+                        err.span_label(
+                            span,
+                            format!(
+                                "this method call is cloning the reference `{expr_ty}`, not \
+                                 `{ty}` which doesn't implement `Clone`",
+                            ),
+                        );
+                        let ty::Param(..) = ty.kind() else {
+                            continue;
+                        };
+                        let hir = tcx.hir();
+                        let node = tcx.hir_node_by_def_id(hir.get_parent_item(expr.hir_id).def_id);
+
+                        let pred = ty::Binder::dummy(ty::TraitPredicate {
+                            trait_ref: ty::TraitRef::from_lang_item(
+                                tcx,
+                                LangItem::Clone,
+                                span,
+                                [*ty],
+                            ),
+                            polarity: ty::ImplPolarity::Positive,
+                        });
+                        let Some(generics) = node.generics() else {
+                            continue;
+                        };
+                        let Some(body_id) = node.body_id() else {
+                            continue;
+                        };
+                        suggest_restriction(
+                            tcx,
+                            hir.body_owner_def_id(body_id),
+                            generics,
+                            &format!("type parameter `{ty}`"),
+                            err,
+                            node.fn_sig(),
+                            None,
+                            pred,
+                            None,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn point_at_chain(
+        &self,
+        expr: &hir::Expr<'_>,
+        typeck_results: &TypeckResults<'tcx>,
+        type_diffs: Vec<TypeError<'tcx>>,
+        param_env: ty::ParamEnv<'tcx>,
+        err: &mut Diagnostic,
+    ) {
+        let mut primary_spans = vec![];
+        let mut span_labels = vec![];
+
+        let tcx = self.tcx;
+
+        let mut print_root_expr = true;
+        let mut assocs = vec![];
+        let mut expr = expr;
+        let mut prev_ty = self.resolve_vars_if_possible(
+            typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
+        );
+        while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind {
+            // Point at every method call in the chain with the resulting type.
+            // vec![1, 2, 3].iter().map(mapper).sum<i32>()
+            //               ^^^^^^ ^^^^^^^^^^^
+            expr = rcvr_expr;
+            let assocs_in_this_method =
+                self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
+            self.look_for_iterator_item_mistakes(
+                &assocs_in_this_method,
+                typeck_results,
+                &type_diffs,
+                param_env,
+                path_segment,
+                args,
+                err,
+            );
+            assocs.push(assocs_in_this_method);
+            prev_ty = self.resolve_vars_if_possible(
+                typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(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 Some(parent) = self.tcx.hir().find_parent(binding.hir_id)
+            {
+                // We've reached the root of the method call chain...
+                if let hir::Node::Local(local) = parent
+                    && let Some(binding_expr) = local.init
+                {
+                    // ...and it is a binding. Get the binding creation and continue the chain.
+                    expr = binding_expr;
+                }
+                if let hir::Node::Param(param) = parent {
+                    // ...and it is a an fn argument.
+                    let prev_ty = self.resolve_vars_if_possible(
+                        typeck_results
+                            .node_type_opt(param.hir_id)
+                            .unwrap_or(Ty::new_misc_error(tcx)),
+                    );
+                    let assocs_in_this_method = self.probe_assoc_types_at_expr(
+                        &type_diffs,
+                        param.ty_span,
+                        prev_ty,
+                        param.hir_id,
+                        param_env,
+                    );
+                    if assocs_in_this_method.iter().any(|a| a.is_some()) {
+                        assocs.push(assocs_in_this_method);
+                        print_root_expr = false;
+                    }
+                    break;
+                }
+            }
+        }
+        // We want the type before deref coercions, otherwise we talk about `&[_]`
+        // instead of `Vec<_>`.
+        if let Some(ty) = typeck_results.expr_ty_opt(expr)
+            && print_root_expr
+        {
+            let ty = with_forced_trimmed_paths!(self.ty_to_string(ty));
+            // Point at the root expression
+            // vec![1, 2, 3].iter().map(mapper).sum<i32>()
+            // ^^^^^^^^^^^^^
+            span_labels.push((expr.span, format!("this expression has type `{ty}`")));
+        };
+        // Only show this if it is not a "trivial" expression (not a method
+        // chain) and there are associated types to talk about.
+        let mut assocs = assocs.into_iter().peekable();
+        while let Some(assocs_in_method) = assocs.next() {
+            let Some(prev_assoc_in_method) = assocs.peek() else {
+                for entry in assocs_in_method {
+                    let Some((span, (assoc, ty))) = entry else {
+                        continue;
+                    };
+                    if primary_spans.is_empty()
+                        || type_diffs.iter().any(|diff| {
+                            let Sorts(expected_found) = diff else {
+                                return false;
+                            };
+                            self.can_eq(param_env, expected_found.found, ty)
+                        })
+                    {
+                        // FIXME: this doesn't quite work for `Iterator::collect`
+                        // because we have `Vec<i32>` and `()`, but we'd want `i32`
+                        // to point at the `.into_iter()` call, but as long as we
+                        // still point at the other method calls that might have
+                        // introduced the issue, this is fine for now.
+                        primary_spans.push(span);
+                    }
+                    span_labels.push((
+                        span,
+                        with_forced_trimmed_paths!(format!(
+                            "`{}` is `{ty}` here",
+                            self.tcx.def_path_str(assoc),
+                        )),
+                    ));
+                }
+                break;
+            };
+            for (entry, prev_entry) in
+                assocs_in_method.into_iter().zip(prev_assoc_in_method.into_iter())
+            {
+                match (entry, prev_entry) {
+                    (Some((span, (assoc, ty))), Some((_, (_, prev_ty)))) => {
+                        let ty_str = with_forced_trimmed_paths!(self.ty_to_string(ty));
+
+                        let assoc = with_forced_trimmed_paths!(self.tcx.def_path_str(assoc));
+                        if !self.can_eq(param_env, ty, *prev_ty) {
+                            if type_diffs.iter().any(|diff| {
+                                let Sorts(expected_found) = diff else {
+                                    return false;
+                                };
+                                self.can_eq(param_env, expected_found.found, ty)
+                            }) {
+                                primary_spans.push(span);
+                            }
+                            span_labels
+                                .push((span, format!("`{assoc}` changed to `{ty_str}` here")));
+                        } else {
+                            span_labels.push((span, format!("`{assoc}` remains `{ty_str}` here")));
+                        }
+                    }
+                    (Some((span, (assoc, ty))), None) => {
+                        span_labels.push((
+                            span,
+                            with_forced_trimmed_paths!(format!(
+                                "`{}` is `{}` here",
+                                self.tcx.def_path_str(assoc),
+                                self.ty_to_string(ty),
+                            )),
+                        ));
+                    }
+                    (None, Some(_)) | (None, None) => {}
+                }
+            }
+        }
+        if !primary_spans.is_empty() {
+            let mut multi_span: MultiSpan = primary_spans.into();
+            for (span, label) in span_labels {
+                multi_span.push_span_label(span, label);
+            }
+            err.span_note(
+                multi_span,
+                "the method call chain might not have had the expected associated types",
+            );
+        }
+    }
+
+    fn probe_assoc_types_at_expr(
+        &self,
+        type_diffs: &[TypeError<'tcx>],
+        span: Span,
+        prev_ty: Ty<'tcx>,
+        body_id: hir::HirId,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> {
+        let ocx = ObligationCtxt::new(self.infcx);
+        let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
+        for diff in type_diffs {
+            let Sorts(expected_found) = diff else {
+                continue;
+            };
+            let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else {
+                continue;
+            };
+
+            let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
+            let trait_def_id = proj.trait_def_id(self.tcx);
+            // Make `Self` be equivalent to the type of the call chain
+            // expression we're looking at now, so that we can tell what
+            // for example `Iterator::Item` is at this point in the chain.
+            let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| {
+                match param.kind {
+                    ty::GenericParamDefKind::Type { .. } => {
+                        if param.index == 0 {
+                            return prev_ty.into();
+                        }
+                    }
+                    ty::GenericParamDefKind::Lifetime | ty::GenericParamDefKind::Const { .. } => {}
+                }
+                self.var_for_def(span, param)
+            });
+            // This will hold the resolved type of the associated type, if the
+            // current expression implements the trait that associated type is
+            // in. For example, this would be what `Iterator::Item` is here.
+            let ty_var = self.infcx.next_ty_var(origin);
+            // This corresponds to `<ExprTy as Iterator>::Item = _`.
+            let projection = ty::Binder::dummy(ty::PredicateKind::Clause(
+                ty::ClauseKind::Projection(ty::ProjectionPredicate {
+                    projection_ty: ty::AliasTy::new(self.tcx, proj.def_id, args),
+                    term: ty_var.into(),
+                }),
+            ));
+            let body_def_id = self.tcx.hir().enclosing_body_owner(body_id);
+            // Add `<ExprTy as Iterator>::Item = _` obligation.
+            ocx.register_obligation(Obligation::misc(
+                self.tcx,
+                span,
+                body_def_id,
+                param_env,
+                projection,
+            ));
+            if ocx.select_where_possible().is_empty() {
+                // `ty_var` now holds the type that `Item` is for `ExprTy`.
+                let ty_var = self.resolve_vars_if_possible(ty_var);
+                assocs_in_this_method.push(Some((span, (proj.def_id, ty_var))));
+            } else {
+                // `<ExprTy as Iterator>` didn't select, so likely we've
+                // reached the end of the iterator chain, like the originating
+                // `Vec<_>`.
+                // Keep the space consistent for later zipping.
+                assocs_in_this_method.push(None);
+            }
+        }
+        assocs_in_this_method
+    }
+
+    /// If the type that failed selection is an array or a reference to an array,
+    /// but the trait is implemented for slices, suggest that the user converts
+    /// the array into a slice.
+    fn suggest_convert_to_slice(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        candidate_impls: &[ImplCandidate<'tcx>],
+        span: Span,
+    ) {
+        // We can only suggest the slice coersion for function and binary operation arguments,
+        // since the suggestion would make no sense in turbofish or call
+        let (ObligationCauseCode::BinOp { .. }
+        | ObligationCauseCode::FunctionArgumentObligation { .. }) = obligation.cause.code()
+        else {
+            return;
+        };
+
+        // Three cases where we can make a suggestion:
+        // 1. `[T; _]` (array of T)
+        // 2. `&[T; _]` (reference to array of T)
+        // 3. `&mut [T; _]` (mutable reference to array of T)
+        let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() {
+            ty::Array(element_ty, _) => (element_ty, None),
+
+            ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() {
+                ty::Array(element_ty, _) => (element_ty, Some(mutability)),
+                _ => return,
+            },
+
+            _ => return,
+        };
+
+        // Go through all the candidate impls to see if any of them is for
+        // slices of `element_ty` with `mutability`.
+        let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() {
+            ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => {
+                if matches!(*t.kind(), ty::Slice(e) if e == element_ty)
+                    && m == mutability.unwrap_or(m)
+                {
+                    // Use the candidate's mutability going forward.
+                    mutability = Some(m);
+                    true
+                } else {
+                    false
+                }
+            }
+            _ => false,
+        };
+
+        // Grab the first candidate that matches, if any, and make a suggestion.
+        if let Some(slice_ty) = candidate_impls
+            .iter()
+            .map(|trait_ref| trait_ref.trait_ref.self_ty())
+            .find(|t| is_slice(*t))
+        {
+            let msg = format!("convert the array to a `{slice_ty}` slice instead");
+
+            if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                let mut suggestions = vec![];
+                if snippet.starts_with('&') {
+                } else if let Some(hir::Mutability::Mut) = mutability {
+                    suggestions.push((span.shrink_to_lo(), "&mut ".into()));
+                } else {
+                    suggestions.push((span.shrink_to_lo(), "&".into()));
+                }
+                suggestions.push((span.shrink_to_hi(), "[..]".into()));
+                err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect);
+            } else {
+                err.span_help(span, msg);
+            }
+        }
+    }
+
+    fn explain_hrtb_projection(
+        &self,
+        diag: &mut Diagnostic,
+        pred: ty::PolyTraitPredicate<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: &ObligationCause<'tcx>,
+    ) {
+        if pred.skip_binder().has_escaping_bound_vars() && pred.skip_binder().has_non_region_infer()
+        {
+            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,
+                ));
+                if !ocx.select_where_possible().is_empty() {
+                    // encountered errors.
+                    return;
+                }
+
+                if let ObligationCauseCode::FunctionArgumentObligation {
+                    call_hir_id,
+                    arg_hir_id,
+                    parent_code: _,
+                } = cause.code()
+                {
+                    let arg_span = self.tcx.hir().span(*arg_hir_id);
+                    let mut sp: MultiSpan = arg_span.into();
+
+                    sp.push_span_label(
+                        arg_span,
+                        "the trait solver is unable to infer the \
+                        generic types that should be inferred from this argument",
+                    );
+                    sp.push_span_label(
+                        self.tcx.hir().span(*call_hir_id),
+                        "add turbofish arguments to this call to \
+                        specify the types manually, even if it's redundant",
+                    );
+                    diag.span_note(
+                        sp,
+                        "this is a known limitation of the trait solver that \
+                        will be lifted in the future",
+                    );
+                } else {
+                    let mut sp: MultiSpan = cause.span.into();
+                    sp.push_span_label(
+                        cause.span,
+                        "try adding turbofish arguments to this expression to \
+                        specify the types manually, even if it's redundant",
+                    );
+                    diag.span_note(
+                        sp,
+                        "this is a known limitation of the trait solver that \
+                        will be lifted in the future",
+                    );
+                }
+            });
+        }
+    }
+
+    fn suggest_desugaring_async_fn_in_trait(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+    ) {
+        // Don't suggest if RTN is active -- we should prefer a where-clause bound instead.
+        if self.tcx.features().return_type_notation {
+            return;
+        }
+
+        let trait_def_id = trait_ref.def_id();
+
+        // Only suggest specifying auto traits
+        if !self.tcx.trait_is_auto(trait_def_id) {
+            return;
+        }
+
+        // Look for an RPITIT
+        let ty::Alias(ty::Projection, alias_ty) = trait_ref.self_ty().skip_binder().kind() else {
+            return;
+        };
+        let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) =
+            self.tcx.opt_rpitit_info(alias_ty.def_id)
+        else {
+            return;
+        };
+
+        let auto_trait = self.tcx.def_path_str(trait_def_id);
+        // ... which is a local function
+        let Some(fn_def_id) = fn_def_id.as_local() else {
+            // If it's not local, we can at least mention that the method is async, if it is.
+            if self.tcx.asyncness(fn_def_id).is_async() {
+                err.span_note(
+                    self.tcx.def_span(fn_def_id),
+                    format!(
+                        "`{}::{}` is an `async fn` in trait, which does not \
+                    automatically imply that its future is `{auto_trait}`",
+                        alias_ty.trait_ref(self.tcx),
+                        self.tcx.item_name(fn_def_id)
+                    ),
+                );
+            }
+            return;
+        };
+        let Some(hir::Node::TraitItem(item)) = self.tcx.opt_hir_node_by_def_id(fn_def_id) else {
+            return;
+        };
+
+        // ... whose signature is `async` (i.e. this is an AFIT)
+        let (sig, body) = item.expect_fn();
+        let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) =
+            sig.decl.output
+        else {
+            // This should never happen, but let's not ICE.
+            return;
+        };
+
+        // Check that this is *not* a nested `impl Future` RPIT in an async fn
+        // (i.e. `async fn foo() -> impl Future`)
+        if def.owner_id.to_def_id() != opaque_def_id {
+            return;
+        }
+
+        let Some(sugg) = suggest_desugaring_async_fn_to_impl_future_in_trait(
+            self.tcx,
+            *sig,
+            *body,
+            opaque_def_id.expect_local(),
+            &format!(" + {auto_trait}"),
+        ) else {
+            return;
+        };
+
+        let function_name = self.tcx.def_path_str(fn_def_id);
+        err.multipart_suggestion(
+            format!(
+                "`{auto_trait}` can be made part of the associated future's \
+                guarantees for all implementations of `{function_name}`"
+            ),
+            sugg,
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
+/// Add a hint to add a missing borrow or remove an unnecessary one.
+fn hint_missing_borrow<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    span: Span,
+    found: Ty<'tcx>,
+    expected: Ty<'tcx>,
+    found_node: Node<'_>,
+    err: &mut Diagnostic,
+) {
+    if matches!(found_node, Node::TraitItem(..)) {
+        return;
+    }
+
+    let found_args = match found.kind() {
+        ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*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(),
+        kind => {
+            span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind)
+        }
+    };
+
+    // This could be a variant constructor, for example.
+    let Some(fn_decl) = found_node.fn_decl() else {
+        return;
+    };
+
+    let args = fn_decl.inputs.iter();
+
+    let mut to_borrow = Vec::new();
+    let mut remove_borrow = Vec::new();
+
+    for ((found_arg, expected_arg), arg) in found_args.zip(expected_args).zip(args) {
+        let (found_ty, found_refs) = get_deref_type_and_refs(*found_arg);
+        let (expected_ty, expected_refs) = get_deref_type_and_refs(*expected_arg);
+
+        if infcx.can_eq(param_env, found_ty, expected_ty) {
+            // FIXME: This could handle more exotic cases like mutability mismatches too!
+            if found_refs.len() < expected_refs.len()
+                && found_refs[..] == expected_refs[expected_refs.len() - found_refs.len()..]
+            {
+                to_borrow.push((
+                    arg.span.shrink_to_lo(),
+                    expected_refs[..expected_refs.len() - found_refs.len()]
+                        .iter()
+                        .map(|mutbl| format!("&{}", mutbl.prefix_str()))
+                        .collect::<Vec<_>>()
+                        .join(""),
+                ));
+            } else if found_refs.len() > expected_refs.len() {
+                let mut span = arg.span.shrink_to_lo();
+                let mut left = found_refs.len() - expected_refs.len();
+                let mut ty = arg;
+                while let hir::TyKind::Ref(_, mut_ty) = &ty.kind
+                    && left > 0
+                {
+                    span = span.with_hi(mut_ty.ty.span.lo());
+                    ty = mut_ty.ty;
+                    left -= 1;
+                }
+                let sugg = if left == 0 {
+                    (span, String::new())
+                } else {
+                    (arg.span, expected_arg.to_string())
+                };
+                remove_borrow.push(sugg);
+            }
+        }
+    }
+
+    if !to_borrow.is_empty() {
+        err.subdiagnostic(errors::AdjustSignatureBorrow::Borrow { to_borrow });
+    }
+
+    if !remove_borrow.is_empty() {
+        err.subdiagnostic(errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow });
+    }
+}
+
+/// Collect all the returned expressions within the input expression.
+/// Used to point at the return spans when we want to suggest some change to them.
+#[derive(Default)]
+pub struct ReturnsVisitor<'v> {
+    pub returns: Vec<&'v hir::Expr<'v>>,
+    in_block_tail: bool,
+}
+
+impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
+    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+        // Visit every expression to detect `return` paths, either through the function's tail
+        // expression or `return` statements. We walk all nodes to find `return` statements, but
+        // we only care about tail expressions when `in_block_tail` is `true`, which means that
+        // they're in the return path of the function body.
+        match ex.kind {
+            hir::ExprKind::Ret(Some(ex)) => {
+                self.returns.push(ex);
+            }
+            hir::ExprKind::Block(block, _) if self.in_block_tail => {
+                self.in_block_tail = false;
+                for stmt in block.stmts {
+                    hir::intravisit::walk_stmt(self, stmt);
+                }
+                self.in_block_tail = true;
+                if let Some(expr) = block.expr {
+                    self.visit_expr(expr);
+                }
+            }
+            hir::ExprKind::If(_, then, else_opt) if self.in_block_tail => {
+                self.visit_expr(then);
+                if let Some(el) = else_opt {
+                    self.visit_expr(el);
+                }
+            }
+            hir::ExprKind::Match(_, arms, _) if self.in_block_tail => {
+                for arm in arms {
+                    self.visit_expr(arm.body);
+                }
+            }
+            // We need to walk to find `return`s in the entire body.
+            _ if !self.in_block_tail => hir::intravisit::walk_expr(self, ex),
+            _ => self.returns.push(ex),
+        }
+    }
+
+    fn visit_body(&mut self, body: &'v hir::Body<'v>) {
+        assert!(!self.in_block_tail);
+        self.in_block_tail = true;
+        hir::intravisit::walk_body(self, body);
+    }
+}
+
+/// Collect all the awaited expressions within the input expression.
+#[derive(Default)]
+struct AwaitsVisitor {
+    awaits: Vec<hir::HirId>,
+}
+
+impl<'v> Visitor<'v> for AwaitsVisitor {
+    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+        if let hir::ExprKind::Yield(_, hir::YieldSource::Await { expr: Some(id) }) = ex.kind {
+            self.awaits.push(id)
+        }
+        hir::intravisit::walk_expr(self, ex)
+    }
+}
+
+pub trait NextTypeParamName {
+    fn next_type_param_name(&self, name: Option<&str>) -> String;
+}
+
+impl NextTypeParamName for &[hir::GenericParam<'_>] {
+    fn next_type_param_name(&self, name: Option<&str>) -> String {
+        // This is the list of possible parameter names that we might suggest.
+        let name = name.and_then(|n| n.chars().next()).map(|c| c.to_uppercase().to_string());
+        let name = name.as_deref();
+        let possible_names = [name.unwrap_or("T"), "T", "U", "V", "X", "Y", "Z", "A", "B", "C"];
+        let used_names = self
+            .iter()
+            .filter_map(|p| match p.name {
+                hir::ParamName::Plain(ident) => Some(ident.name),
+                _ => None,
+            })
+            .collect::<Vec<_>>();
+
+        possible_names
+            .iter()
+            .find(|n| !used_names.contains(&Symbol::intern(n)))
+            .unwrap_or(&"ParamName")
+            .to_string()
+    }
+}
+
+/// Collect the spans that we see the generic param `param_did`
+struct ReplaceImplTraitVisitor<'a> {
+    ty_spans: &'a mut Vec<Span>,
+    param_did: DefId,
+}
+
+impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> {
+    fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) {
+        if let hir::TyKind::Path(hir::QPath::Resolved(
+            None,
+            hir::Path { res: Res::Def(_, segment_did), .. },
+        )) = t.kind
+        {
+            if self.param_did == *segment_did {
+                // `fn foo(t: impl Trait)`
+                //            ^^^^^^^^^^ get this to suggest `T` instead
+
+                // There might be more than one `impl Trait`.
+                self.ty_spans.push(t.span);
+                return;
+            }
+        }
+
+        hir::intravisit::walk_ty(self, t);
+    }
+}
+
+pub(super) fn get_explanation_based_on_obligation<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    obligation: &PredicateObligation<'tcx>,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+    trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+    pre_message: String,
+) -> String {
+    if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
+        "consider using `()`, or a `Result`".to_owned()
+    } else {
+        let ty_desc = match trait_ref.skip_binder().self_ty().kind() {
+            ty::FnDef(_, _) => Some("fn item"),
+            ty::Closure(_, _) => Some("closure"),
+            _ => None,
+        };
+
+        match ty_desc {
+            Some(desc) => format!(
+                "{}the trait `{}` is not implemented for {} `{}`",
+                pre_message,
+                trait_predicate.print_modifiers_and_trait_path(),
+                desc,
+                tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None),
+            ),
+            None => format!(
+                "{}the trait `{}` is not implemented for `{}`",
+                pre_message,
+                trait_predicate.print_modifiers_and_trait_path(),
+                tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None),
+            ),
+        }
+    }
+}
+
+// Replace `param` with `replace_ty`
+struct ReplaceImplTraitFolder<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    param: &'tcx ty::GenericParamDef,
+    replace_ty: Ty<'tcx>,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceImplTraitFolder<'tcx> {
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        if let ty::Param(ty::ParamTy { index, .. }) = t.kind() {
+            if self.param.index == *index {
+                return self.replace_ty;
+            }
+        }
+        t.super_fold_with(self)
+    }
+
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+}
+
+pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    sig: hir::FnSig<'tcx>,
+    body: hir::TraitFn<'tcx>,
+    opaque_def_id: LocalDefId,
+    add_bounds: &str,
+) -> Option<Vec<(Span, String)>> {
+    let hir::IsAsync::Async(async_span) = sig.header.asyncness else {
+        return None;
+    };
+    let Ok(async_span) = tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace())
+    else {
+        return None;
+    };
+
+    let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
+    let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else {
+        // `async fn` should always lower to a single bound... but don't ICE.
+        return None;
+    };
+    let Some(hir::PathSegment { args: Some(generics), .. }) =
+        trait_ref.trait_ref.path.segments.last()
+    else {
+        // desugaring to a single path segment for `Future<...>`.
+        return None;
+    };
+    let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) =
+        generics.bindings.get(0).map(|binding| binding.kind)
+    else {
+        // Also should never happen.
+        return None;
+    };
+
+    let mut sugg = if future_output_ty.span.is_empty() {
+        vec![
+            (async_span, String::new()),
+            (
+                future_output_ty.span,
+                format!(" -> impl std::future::Future<Output = ()>{add_bounds}"),
+            ),
+        ]
+    } else {
+        vec![
+            (future_output_ty.span.shrink_to_lo(), "impl std::future::Future<Output = ".to_owned()),
+            (future_output_ty.span.shrink_to_hi(), format!(">{add_bounds}")),
+            (async_span, String::new()),
+        ]
+    };
+
+    // If there's a body, we also need to wrap it in `async {}`
+    if let hir::TraitFn::Provided(body) = body {
+        let body = tcx.hir().body(body);
+        let body_span = body.value.span;
+        let body_span_without_braces =
+            body_span.with_lo(body_span.lo() + BytePos(1)).with_hi(body_span.hi() - BytePos(1));
+        if body_span_without_braces.is_empty() {
+            sugg.push((body_span_without_braces, " async {} ".to_owned()));
+        } else {
+            sugg.extend([
+                (body_span_without_braces.shrink_to_lo(), "async {".to_owned()),
+                (body_span_without_braces.shrink_to_hi(), "} ".to_owned()),
+            ]);
+        }
+    }
+
+    Some(sugg)
+}
+
+fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
+    let mut refs = vec![];
+
+    while let ty::Ref(_, new_ty, mutbl) = ty.kind() {
+        ty = *new_ty;
+        refs.push(*mutbl);
+    }
+
+    (ty, refs)
+}
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
new file mode 100644
index 00000000000..d2598b0defe
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -0,0 +1,3597 @@
+// ignore-tidy-filelength :(
+
+use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
+use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
+use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch};
+use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
+use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use crate::infer::InferCtxtExt as _;
+use crate::infer::{self, InferCtxt};
+use crate::traits::error_reporting::infer_ctxt_ext::InferCtxtExt;
+use crate::traits::error_reporting::{ambiguity, ambiguity::Ambiguity::*};
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use crate::traits::specialize::to_pretty_impl_header;
+use crate::traits::NormalizeExt;
+use crate::traits::{
+    elaborate, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation,
+    ObligationCause, ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow,
+    PredicateObligation, SelectionError, TraitNotObjectSafe,
+};
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+    MultiSpan, StashKey, Style,
+};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Namespace, Res};
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{GenericParam, Item, Node};
+use rustc_infer::infer::error_reporting::TypeErrCtxt;
+use rustc_infer::infer::{InferOk, TypeTrace};
+use rustc_middle::traits::select::OverflowError;
+use rustc_middle::traits::{DefiningAnchor, SelectionOutputTypeParameterMismatch};
+use rustc_middle::ty::abstract_const::NotConstEvaluatable;
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print};
+use rustc_middle::ty::{
+    self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
+    TypeVisitable, TypeVisitableExt,
+};
+use rustc_session::config::DumpSolverProofTree;
+use rustc_session::Limit;
+use rustc_span::def_id::LOCAL_CRATE;
+use rustc_span::symbol::sym;
+use rustc_span::{BytePos, ExpnKind, Span, Symbol, DUMMY_SP};
+use std::borrow::Cow;
+use std::fmt;
+use std::iter;
+
+use super::{
+    dump_proof_tree, ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam,
+    GetSafeTransmuteErrorAndReason, HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst,
+};
+
+pub use rustc_infer::traits::error_reporting::*;
+
+pub trait TypeErrCtxtExt<'tcx> {
+    fn build_overflow_error<T>(
+        &self,
+        predicate: &T,
+        span: Span,
+        suggest_increasing_limit: bool,
+    ) -> DiagnosticBuilder<'tcx>
+    where
+        T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>;
+
+    fn report_overflow_error<T>(
+        &self,
+        predicate: &T,
+        span: Span,
+        suggest_increasing_limit: bool,
+        mutate: impl FnOnce(&mut Diagnostic),
+    ) -> !
+    where
+        T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>;
+
+    fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed;
+
+    fn report_fulfillment_errors(&self, errors: Vec<FulfillmentError<'tcx>>) -> ErrorGuaranteed;
+
+    fn report_overflow_obligation<T>(
+        &self,
+        obligation: &Obligation<'tcx, T>,
+        suggest_increasing_limit: bool,
+    ) -> !
+    where
+        T: ToPredicate<'tcx> + Clone;
+
+    fn suggest_new_overflow_limit(&self, err: &mut Diagnostic);
+
+    fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
+
+    /// The `root_obligation` parameter should be the `root_obligation` field
+    /// from a `FulfillmentError`. If no `FulfillmentError` is available,
+    /// then it should be the same as `obligation`.
+    fn report_selection_error(
+        &self,
+        obligation: PredicateObligation<'tcx>,
+        root_obligation: &PredicateObligation<'tcx>,
+        error: &SelectionError<'tcx>,
+    );
+
+    fn emit_specialized_closure_kind_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+    ) -> Option<ErrorGuaranteed>;
+
+    fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool;
+
+    fn try_conversion_context(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: ty::TraitRef<'tcx>,
+        err: &mut Diagnostic,
+    ) -> bool;
+
+    fn report_const_param_not_wf(
+        &self,
+        ty: Ty<'tcx>,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> DiagnosticBuilder<'tcx>;
+}
+
+impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
+    fn report_fulfillment_errors(
+        &self,
+        mut errors: Vec<FulfillmentError<'tcx>>,
+    ) -> ErrorGuaranteed {
+        #[derive(Debug)]
+        struct ErrorDescriptor<'tcx> {
+            predicate: ty::Predicate<'tcx>,
+            index: Option<usize>, // None if this is an old error
+        }
+
+        let mut error_map: FxIndexMap<_, Vec<_>> = self
+            .reported_trait_errors
+            .borrow()
+            .iter()
+            .map(|(&span, predicates)| {
+                (
+                    span,
+                    predicates
+                        .iter()
+                        .map(|&predicate| ErrorDescriptor { predicate, index: None })
+                        .collect(),
+                )
+            })
+            .collect();
+
+        // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics
+        // with more relevant type information and hide redundant E0282 errors.
+        errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
+            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
+                if Some(pred.def_id()) == self.tcx.lang_items().sized_trait() =>
+            {
+                1
+            }
+            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
+            ty::PredicateKind::Coerce(_) => 2,
+            _ => 0,
+        });
+
+        for (index, error) in errors.iter().enumerate() {
+            // We want to ignore desugarings here: spans are equivalent even
+            // if one is the result of a desugaring and the other is not.
+            let mut span = error.obligation.cause.span;
+            let expn_data = span.ctxt().outer_expn_data();
+            if let ExpnKind::Desugaring(_) = expn_data.kind {
+                span = expn_data.call_site;
+            }
+
+            error_map.entry(span).or_default().push(ErrorDescriptor {
+                predicate: error.obligation.predicate,
+                index: Some(index),
+            });
+        }
+
+        // We do this in 2 passes because we want to display errors in order, though
+        // maybe it *is* better to sort errors by span or something.
+        let mut is_suppressed = vec![false; errors.len()];
+        for (_, error_set) in error_map.iter() {
+            // We want to suppress "duplicate" errors with the same span.
+            for error in error_set {
+                if let Some(index) = error.index {
+                    // Suppress errors that are either:
+                    // 1) strictly implied by another error.
+                    // 2) implied by an error with a smaller index.
+                    for error2 in error_set {
+                        if error2.index.is_some_and(|index2| is_suppressed[index2]) {
+                            // Avoid errors being suppressed by already-suppressed
+                            // errors, to prevent all errors from being suppressed
+                            // at once.
+                            continue;
+                        }
+
+                        if self.error_implies(error2.predicate, error.predicate)
+                            && !(error2.index >= error.index
+                                && self.error_implies(error.predicate, error2.predicate))
+                        {
+                            info!("skipping {:?} (implied by {:?})", error, error2);
+                            is_suppressed[index] = true;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        for from_expansion in [false, true] {
+            for (error, suppressed) in iter::zip(&errors, &is_suppressed) {
+                if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
+                    self.report_fulfillment_error(error);
+                    // We want to ignore desugarings here: spans are equivalent even
+                    // if one is the result of a desugaring and the other is not.
+                    let mut span = error.obligation.cause.span;
+                    let expn_data = span.ctxt().outer_expn_data();
+                    if let ExpnKind::Desugaring(_) = expn_data.kind {
+                        span = expn_data.call_site;
+                    }
+                    self.reported_trait_errors
+                        .borrow_mut()
+                        .entry(span)
+                        .or_default()
+                        .push(error.obligation.predicate);
+                }
+            }
+        }
+
+        self.dcx().span_delayed_bug(DUMMY_SP, "expected fulfillment errors")
+    }
+
+    /// Reports that an overflow has occurred and halts compilation. We
+    /// halt compilation unconditionally because it is important that
+    /// overflows never be masked -- they basically represent computations
+    /// whose result could not be truly determined and thus we can't say
+    /// if the program type checks or not -- and they are unusual
+    /// occurrences in any case.
+    fn report_overflow_error<T>(
+        &self,
+        predicate: &T,
+        span: Span,
+        suggest_increasing_limit: bool,
+        mutate: impl FnOnce(&mut Diagnostic),
+    ) -> !
+    where
+        T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
+    {
+        let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit);
+        mutate(&mut err);
+        err.emit();
+
+        self.dcx().abort_if_errors();
+        // FIXME: this should be something like `build_overflow_error_fatal`, which returns
+        // `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`.
+        unreachable!(
+            "did not expect compilation to continue after `abort_if_errors`, \
+            since an error was definitely emitted!"
+        );
+    }
+
+    fn build_overflow_error<T>(
+        &self,
+        predicate: &T,
+        span: Span,
+        suggest_increasing_limit: bool,
+    ) -> DiagnosticBuilder<'tcx>
+    where
+        T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
+    {
+        let predicate = self.resolve_vars_if_possible(predicate.clone());
+        let mut pred_str = predicate.to_string();
+
+        if pred_str.len() > 50 {
+            // We don't need to save the type to a file, we will be talking about this type already
+            // in a separate note when we explain the obligation, so it will be available that way.
+            let mut cx: FmtPrinter<'_, '_> =
+                FmtPrinter::new_with_limit(self.tcx, Namespace::TypeNS, rustc_session::Limit(6));
+            predicate.print(&mut cx).unwrap();
+            pred_str = cx.into_buffer();
+        }
+        let mut err = struct_span_err!(
+            self.dcx(),
+            span,
+            E0275,
+            "overflow evaluating the requirement `{}`",
+            pred_str,
+        );
+
+        if suggest_increasing_limit {
+            self.suggest_new_overflow_limit(&mut err);
+        }
+
+        err
+    }
+
+    /// Reports that an overflow has occurred and halts compilation. We
+    /// halt compilation unconditionally because it is important that
+    /// overflows never be masked -- they basically represent computations
+    /// whose result could not be truly determined and thus we can't say
+    /// if the program type checks or not -- and they are unusual
+    /// occurrences in any case.
+    fn report_overflow_obligation<T>(
+        &self,
+        obligation: &Obligation<'tcx, T>,
+        suggest_increasing_limit: bool,
+    ) -> !
+    where
+        T: ToPredicate<'tcx> + Clone,
+    {
+        let predicate = obligation.predicate.clone().to_predicate(self.tcx);
+        let predicate = self.resolve_vars_if_possible(predicate);
+        self.report_overflow_error(
+            &predicate,
+            obligation.cause.span,
+            suggest_increasing_limit,
+            |err| {
+                self.note_obligation_cause_code(
+                    obligation.cause.body_id,
+                    err,
+                    predicate,
+                    obligation.param_env,
+                    obligation.cause.code(),
+                    &mut vec![],
+                    &mut Default::default(),
+                );
+            },
+        );
+    }
+
+    fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) {
+        let suggested_limit = match self.tcx.recursion_limit() {
+            Limit(0) => Limit(2),
+            limit => limit * 2,
+        };
+        err.help(format!(
+            "consider increasing the recursion limit by adding a \
+             `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
+            suggested_limit,
+            self.tcx.crate_name(LOCAL_CRATE),
+        ));
+    }
+
+    /// Reports that a cycle was detected which led to overflow and halts
+    /// compilation. This is equivalent to `report_overflow_obligation` except
+    /// that we can give a more helpful error message (and, in particular,
+    /// we do not suggest increasing the overflow limit, which is not
+    /// going to help).
+    fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
+        let cycle = self.resolve_vars_if_possible(cycle.to_owned());
+        assert!(!cycle.is_empty());
+
+        debug!(?cycle, "report_overflow_error_cycle");
+
+        // The 'deepest' obligation is most likely to have a useful
+        // cause 'backtrace'
+        self.report_overflow_obligation(
+            cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(),
+            false,
+        );
+    }
+
+    fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed {
+        let obligation = self.resolve_vars_if_possible(obligation);
+        let mut err = self.build_overflow_error(&obligation.predicate, obligation.cause.span, true);
+        self.note_obligation_cause(&mut err, &obligation);
+        self.point_at_returns_when_relevant(&mut err, &obligation);
+        err.emit()
+    }
+
+    fn report_selection_error(
+        &self,
+        mut obligation: PredicateObligation<'tcx>,
+        root_obligation: &PredicateObligation<'tcx>,
+        error: &SelectionError<'tcx>,
+    ) {
+        let tcx = self.tcx;
+
+        if tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default()
+            == DumpSolverProofTree::OnError
+        {
+            dump_proof_tree(root_obligation, self.infcx);
+        }
+
+        let mut span = obligation.cause.span;
+        // FIXME: statically guarantee this by tainting after the diagnostic is emitted
+        self.set_tainted_by_errors(
+            tcx.dcx().span_delayed_bug(span, "`report_selection_error` did not emit an error"),
+        );
+
+        let mut err = match *error {
+            SelectionError::Unimplemented => {
+                // If this obligation was generated as a result of well-formedness checking, see if we
+                // can get a better error message by performing HIR-based well-formedness checking.
+                if let ObligationCauseCode::WellFormed(Some(wf_loc)) =
+                    root_obligation.cause.code().peel_derives()
+                    && !obligation.predicate.has_non_region_infer()
+                {
+                    if let Some(cause) = self
+                        .tcx
+                        .diagnostic_hir_wf_check((tcx.erase_regions(obligation.predicate), *wf_loc))
+                    {
+                        obligation.cause = cause.clone();
+                        span = obligation.cause.span;
+                    }
+                }
+
+                if let ObligationCauseCode::CompareImplItemObligation {
+                    impl_item_def_id,
+                    trait_item_def_id,
+                    kind: _,
+                } = *obligation.cause.code()
+                {
+                    self.report_extra_impl_obligation(
+                        span,
+                        impl_item_def_id,
+                        trait_item_def_id,
+                        &format!("`{}`", obligation.predicate),
+                    )
+                    .emit();
+                    return;
+                }
+
+                // Report a const-param specific error
+                if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives()
+                {
+                    self.report_const_param_not_wf(ty, &obligation).emit();
+                    return;
+                }
+
+                let bound_predicate = obligation.predicate.kind();
+                match bound_predicate.skip_binder() {
+                    ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => {
+                        let trait_predicate = bound_predicate.rebind(trait_predicate);
+                        let trait_predicate = self.resolve_vars_if_possible(trait_predicate);
+                        let trait_ref = trait_predicate.to_poly_trait_ref();
+
+                        if let Some(_guar) = self.emit_specialized_closure_kind_error(&obligation, trait_ref) {
+                            return;
+                        }
+
+                        // FIXME(effects)
+                        let predicate_is_const = false;
+
+                        if self.dcx().has_errors().is_some()
+                            && trait_predicate.references_error()
+                        {
+                            return;
+                        }
+                        if self.fn_arg_obligation(&obligation) {
+                            // Silence redundant errors on binding acccess that are already
+                            // reported on the binding definition (#56607).
+                            return;
+                        }
+                        let mut file = None;
+                        let (post_message, pre_message, type_def) = self
+                            .get_parent_trait_ref(obligation.cause.code())
+                            .map(|(t, s)| {
+                                let t = self.tcx.short_ty_string(t, &mut file);
+                                (
+                                    format!(" in `{t}`"),
+                                    format!("within `{t}`, "),
+                                    s.map(|s| (format!("within this `{t}`"), s)),
+                                )
+                            })
+                            .unwrap_or_default();
+                        let file_note = file.map(|file| format!(
+                            "the full trait has been written to '{}'",
+                            file.display(),
+                        ));
+
+                        let OnUnimplementedNote {
+                            message,
+                            label,
+                            notes,
+                            parent_label,
+                            append_const_msg,
+                        } = self.on_unimplemented_note(trait_ref, &obligation);
+                        let have_alt_message = message.is_some() || label.is_some();
+                        let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
+                        let is_unsize =
+                            Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait();
+                        let (message, notes, append_const_msg) = if is_try_conversion {
+                            (
+                                Some(format!(
+                                    "`?` couldn't convert the error to `{}`",
+                                    trait_ref.skip_binder().self_ty(),
+                                )),
+                                vec![
+                                    "the question mark operation (`?`) implicitly performs a \
+                                     conversion on the error value using the `From` trait"
+                                        .to_owned(),
+                                ],
+                                Some(AppendConstMessage::Default),
+                            )
+                        } else {
+                            (message, notes, append_const_msg)
+                        };
+
+                        let err_msg = self.get_standard_error_message(
+                            &trait_predicate,
+                            message,
+                            predicate_is_const,
+                            append_const_msg,
+                            post_message,
+                        );
+
+                        let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id())
+                            == self.tcx.lang_items().transmute_trait()
+                        {
+                            // Recompute the safe transmute reason and use that for the error reporting
+                            match self.get_safe_transmute_error_and_reason(
+                                obligation.clone(),
+                                trait_ref,
+                                span,
+                            ) {
+                                GetSafeTransmuteErrorAndReason::Silent => return,
+                                GetSafeTransmuteErrorAndReason::Error {
+                                    err_msg,
+                                    safe_transmute_explanation,
+                                } => (err_msg, Some(safe_transmute_explanation)),
+                            }
+                        } else {
+                            (err_msg, None)
+                        };
+
+                        let mut err = struct_span_err!(self.dcx(), span, E0277, "{}", err_msg);
+
+                        let mut suggested = false;
+                        if is_try_conversion {
+                            suggested = self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err);
+                        }
+
+                        if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
+                            err.span_label(
+                                ret_span,
+                                format!(
+                                    "expected `{}` because of this",
+                                    trait_ref.skip_binder().self_ty()
+                                ),
+                            );
+                        }
+
+                        if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() {
+                            self.add_tuple_trait_message(
+                                obligation.cause.code().peel_derives(),
+                                &mut err,
+                            );
+                        }
+
+                        if Some(trait_ref.def_id()) == tcx.lang_items().drop_trait()
+                            && predicate_is_const
+                        {
+                            err.note("`~const Drop` was renamed to `~const Destruct`");
+                            err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details");
+                        }
+
+                        let explanation = get_explanation_based_on_obligation(
+                            self.tcx,
+                            &obligation,
+                            trait_ref,
+                            &trait_predicate,
+                            pre_message,
+                        );
+
+                        self.check_for_binding_assigned_block_without_tail_expression(
+                            &obligation,
+                            &mut err,
+                            trait_predicate,
+                        );
+                        if self.suggest_add_reference_to_arg(
+                            &obligation,
+                            &mut err,
+                            trait_predicate,
+                            have_alt_message,
+                        ) {
+                            self.note_obligation_cause(&mut err, &obligation);
+                            err.emit();
+                            return;
+                        }
+
+                        file_note.map(|note| err.note(note));
+                        if let Some(s) = label {
+                            // If it has a custom `#[rustc_on_unimplemented]`
+                            // error message, let's display it as the label!
+                            err.span_label(span, s);
+                            if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) {
+                                // When the self type is a type param We don't need to "the trait
+                                // `std::marker::Sized` is not implemented for `T`" as we will point
+                                // at the type param with a label to suggest constraining it.
+                                err.help(explanation);
+                            }
+                        } else if let Some(custom_explanation) = safe_transmute_explanation {
+                            err.span_label(span, custom_explanation);
+                        } else {
+                            err.span_label(span, explanation);
+                        }
+
+                        if let ObligationCauseCode::Coercion { source, target } =
+                            *obligation.cause.code().peel_derives()
+                        {
+                            if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() {
+                                self.suggest_borrowing_for_object_cast(
+                                    &mut err,
+                                    root_obligation,
+                                    source,
+                                    target,
+                                );
+                            }
+                        }
+
+                        let UnsatisfiedConst(unsatisfied_const) = self
+                            .maybe_add_note_for_unsatisfied_const(
+                                &obligation,
+                                trait_ref,
+                                &trait_predicate,
+                                &mut err,
+                                span,
+                            );
+
+                        if let Some((msg, span)) = type_def {
+                            err.span_label(span, msg);
+                        }
+                        for note in notes {
+                            // If it has a custom `#[rustc_on_unimplemented]` note, let's display it
+                            err.note(note);
+                        }
+                        if let Some(s) = parent_label {
+                            let body = obligation.cause.body_id;
+                            err.span_label(tcx.def_span(body), s);
+                        }
+
+                        self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
+                        self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate);
+                        suggested |= self.suggest_dereferences(&obligation, &mut err, trait_predicate);
+                        suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
+                        let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
+                        suggested = if let &[cand] = &impl_candidates[..] {
+                            let cand = cand.trait_ref;
+                            if let (ty::FnPtr(_), ty::FnDef(..)) =
+                                (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind())
+                            {
+                                err.span_suggestion(
+                                    span.shrink_to_hi(),
+                                    format!(
+                                        "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`",
+                                        cand.print_trait_sugared(),
+                                        cand.self_ty(),
+                                    ),
+                                    format!(" as {}", cand.self_ty()),
+                                    Applicability::MaybeIncorrect,
+                                );
+                                true
+                            } else {
+                                false
+                            }
+                        } else {
+                            false
+                        } || suggested;
+                        suggested |=
+                            self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
+                        suggested |= self.suggest_semicolon_removal(
+                            &obligation,
+                            &mut err,
+                            span,
+                            trait_predicate,
+                        );
+                        self.note_version_mismatch(&mut err, &trait_ref);
+                        self.suggest_remove_await(&obligation, &mut err);
+                        self.suggest_derive(&obligation, &mut err, trait_predicate);
+
+                        if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() {
+                            self.suggest_await_before_try(
+                                &mut err,
+                                &obligation,
+                                trait_predicate,
+                                span,
+                            );
+                        }
+
+                        if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) {
+                            err.emit();
+                            return;
+                        }
+
+                        if self.suggest_impl_trait(&mut err, &obligation, trait_predicate) {
+                            err.emit();
+                            return;
+                        }
+
+                        if is_unsize {
+                            // If the obligation failed due to a missing implementation of the
+                            // `Unsize` trait, give a pointer to why that might be the case
+                            err.note(
+                                "all implementations of `Unsize` are provided \
+                                automatically by the compiler, see \
+                                <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> \
+                                for more information",
+                            );
+                        }
+
+                        let is_fn_trait = tcx.is_fn_trait(trait_ref.def_id());
+                        let is_target_feature_fn = if let ty::FnDef(def_id, _) =
+                            *trait_ref.skip_binder().self_ty().kind()
+                        {
+                            !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
+                        } else {
+                            false
+                        };
+                        if is_fn_trait && is_target_feature_fn {
+                            err.note(
+                                "`#[target_feature]` functions do not implement the `Fn` traits",
+                            );
+                        }
+
+                        self.try_to_add_help_message(
+                            &obligation,
+                            trait_ref,
+                            &trait_predicate,
+                            &mut err,
+                            span,
+                            is_fn_trait,
+                            suggested,
+                            unsatisfied_const,
+                        );
+
+                        // Changing mutability doesn't make a difference to whether we have
+                        // an `Unsize` impl (Fixes ICE in #71036)
+                        if !is_unsize {
+                            self.suggest_change_mut(&obligation, &mut err, trait_predicate);
+                        }
+
+                        // If this error is due to `!: Trait` not implemented but `(): Trait` is
+                        // implemented, and fallback has occurred, then it could be due to a
+                        // variable that used to fallback to `()` now falling back to `!`. Issue a
+                        // note informing about the change in behaviour.
+                        if trait_predicate.skip_binder().self_ty().is_never()
+                            && self.fallback_has_occurred
+                        {
+                            let predicate = trait_predicate.map_bound(|trait_pred| {
+                                trait_pred.with_self_ty(self.tcx, Ty::new_unit(self.tcx))
+                            });
+                            let unit_obligation = obligation.with(tcx, predicate);
+                            if self.predicate_may_hold(&unit_obligation) {
+                                err.note(
+                                    "this error might have been caused by changes to \
+                                    Rust's type-inference algorithm (see issue #48950 \
+                                    <https://github.com/rust-lang/rust/issues/48950> \
+                                    for more information)",
+                                );
+                                err.help("did you intend to use the type `()` here instead?");
+                            }
+                        }
+
+                        self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause);
+                        self.suggest_desugaring_async_fn_in_trait(&mut err, trait_ref);
+
+                        // Return early if the trait is Debug or Display and the invocation
+                        // originates within a standard library macro, because the output
+                        // is otherwise overwhelming and unhelpful (see #85844 for an
+                        // example).
+
+                        let in_std_macro =
+                            match obligation.cause.span.ctxt().outer_expn_data().macro_def_id {
+                                Some(macro_def_id) => {
+                                    let crate_name = tcx.crate_name(macro_def_id.krate);
+                                    crate_name == sym::std || crate_name == sym::core
+                                }
+                                None => false,
+                            };
+
+                        if in_std_macro
+                            && matches!(
+                                self.tcx.get_diagnostic_name(trait_ref.def_id()),
+                                Some(sym::Debug | sym::Display)
+                            )
+                        {
+                            err.emit();
+                            return;
+                        }
+
+                        err
+                    }
+
+                    ty::PredicateKind::Subtype(predicate) => {
+                        // Errors for Subtype predicates show up as
+                        // `FulfillmentErrorCode::CodeSubtypeError`,
+                        // not selection error.
+                        span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate)
+                    }
+
+                    ty::PredicateKind::Coerce(predicate) => {
+                        // Errors for Coerce predicates show up as
+                        // `FulfillmentErrorCode::CodeSubtypeError`,
+                        // not selection error.
+                        span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate)
+                    }
+
+                    ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
+                    | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => {
+                        span_bug!(
+                            span,
+                            "outlives clauses should not error outside borrowck. obligation: `{:?}`",
+                            obligation
+                        )
+                    }
+
+                    ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => {
+                        span_bug!(
+                            span,
+                            "projection clauses should be implied from elsewhere. obligation: `{:?}`",
+                            obligation
+                        )
+                    }
+
+                    ty::PredicateKind::ObjectSafe(trait_def_id) => {
+                        let violations = self.tcx.object_safety_violations(trait_def_id);
+                        report_object_safety_error(self.tcx, span, trait_def_id, violations)
+                    }
+
+                    ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => {
+                        let ty = self.resolve_vars_if_possible(ty);
+                        if self.next_trait_solver() {
+                            // FIXME: we'll need a better message which takes into account
+                            // which bounds actually failed to hold.
+                            self.dcx().struct_span_err(
+                                span,
+                                format!("the type `{ty}` is not well-formed"),
+                            )
+                        } else {
+                            // WF predicates cannot themselves make
+                            // errors. They can only block due to
+                            // ambiguity; otherwise, they always
+                            // degenerate into other obligations
+                            // (which may fail).
+                            span_bug!(span, "WF predicate not satisfied for {:?}", ty);
+                        }
+                    }
+
+                    ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => {
+                        // Errors for `ConstEvaluatable` predicates show up as
+                        // `SelectionError::ConstEvalFailure`,
+                        // not `Unimplemented`.
+                        span_bug!(
+                            span,
+                            "const-evaluatable requirement gave wrong error: `{:?}`",
+                            obligation
+                        )
+                    }
+
+                    ty::PredicateKind::ConstEquate(..) => {
+                        // Errors for `ConstEquate` predicates show up as
+                        // `SelectionError::ConstEvalFailure`,
+                        // not `Unimplemented`.
+                        span_bug!(
+                            span,
+                            "const-equate requirement gave wrong error: `{:?}`",
+                            obligation
+                        )
+                    }
+
+                    ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"),
+
+                    ty::PredicateKind::NormalizesTo(..) => span_bug!(
+                        span,
+                        "NormalizesTo predicate should never be the predicate cause of a SelectionError"
+                    ),
+
+                    ty::PredicateKind::AliasRelate(..) => span_bug!(
+                        span,
+                        "AliasRelate predicate should never be the predicate cause of a SelectionError"
+                    ),
+
+                    ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
+                        let mut diag = self.dcx().struct_span_err(
+                            span,
+                            format!("the constant `{ct}` is not of type `{ty}`"),
+                        );
+                        self.note_type_err(
+                            &mut diag,
+                            &obligation.cause,
+                            None,
+                            None,
+                            TypeError::Sorts(ty::error::ExpectedFound::new(true, ty, ct.ty())),
+                            false,
+                            false,
+                        );
+                        diag
+                    }
+                }
+            }
+
+            OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch {
+                found_trait_ref,
+                expected_trait_ref,
+                terr: terr @ TypeError::CyclicTy(_),
+            }) => self.report_type_parameter_mismatch_cyclic_type_error(
+                &obligation,
+                found_trait_ref,
+                expected_trait_ref,
+                terr,
+            ),
+            OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch {
+                found_trait_ref,
+                expected_trait_ref,
+                terr: _,
+            }) => {
+                match self.report_type_parameter_mismatch_error(
+                    &obligation,
+                    span,
+                    found_trait_ref,
+                    expected_trait_ref,
+                ) {
+                    Some(err) => err,
+                    None => return,
+                }
+            }
+
+            SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage(
+                &obligation,
+                def_id,
+            ),
+
+            TraitNotObjectSafe(did) => {
+                let violations = self.tcx.object_safety_violations(did);
+                report_object_safety_error(self.tcx, span, did, violations)
+            }
+
+            SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => {
+                bug!(
+                    "MentionsInfer should have been handled in `traits/fulfill.rs` or `traits/select/mod.rs`"
+                )
+            }
+            SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => {
+                match self.report_not_const_evaluatable_error(&obligation, span) {
+                    Some(err) => err,
+                    None => return,
+                }
+            }
+
+            // Already reported in the query.
+            SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) |
+            // Already reported.
+            Overflow(OverflowError::Error(_)) => return,
+
+            Overflow(_) => {
+                bug!("overflow should be handled before the `report_selection_error` path");
+            }
+            SelectionError::ErrorReporting => {
+                bug!("ErrorReporting Overflow should not reach `report_selection_err` call")
+            }
+        };
+
+        self.note_obligation_cause(&mut err, &obligation);
+        self.point_at_returns_when_relevant(&mut err, &obligation);
+        err.emit();
+    }
+
+    fn emit_specialized_closure_kind_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+    ) -> Option<ErrorGuaranteed> {
+        if let ty::Closure(closure_def_id, closure_args) = *trait_ref.self_ty().skip_binder().kind()
+            && let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id())
+            && let Some(found_kind) = self.closure_kind(closure_args)
+            && !found_kind.extends(expected_kind)
+            && let sig = closure_args.as_closure().sig()
+            && self.can_sub(
+                obligation.param_env,
+                trait_ref,
+                sig.map_bound(|sig| {
+                    ty::TraitRef::new(
+                        self.tcx,
+                        trait_ref.def_id(),
+                        [trait_ref.self_ty().skip_binder(), sig.inputs()[0]],
+                    )
+                }),
+            )
+        {
+            let mut err =
+                self.report_closure_error(&obligation, closure_def_id, found_kind, expected_kind);
+            self.note_obligation_cause(&mut err, &obligation);
+            self.point_at_returns_when_relevant(&mut err, &obligation);
+            Some(err.emit())
+        } else {
+            None
+        }
+    }
+
+    fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool {
+        if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
+            obligation.cause.code()
+            && let Some(Node::Expr(arg)) = self.tcx.opt_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 Some(preds) = self.reported_trait_errors.borrow().get(&pat.span)
+            && preds.contains(&obligation.predicate)
+        {
+            return true;
+        }
+        false
+    }
+
+    /// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`,
+    /// identify thoe method chain sub-expressions that could or could not have been annotated
+    /// with `?`.
+    fn try_conversion_context(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: ty::TraitRef<'tcx>,
+        err: &mut Diagnostic,
+    ) -> bool {
+        let span = obligation.cause.span;
+        struct V<'v> {
+            search_span: Span,
+            found: Option<&'v hir::Expr<'v>>,
+        }
+        impl<'v> Visitor<'v> for V<'v> {
+            fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+                if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind
+                {
+                    if ex.span.with_lo(ex.span.hi() - BytePos(1)).source_equal(self.search_span) {
+                        if let hir::ExprKind::Call(_, [expr, ..]) = expr.kind {
+                            self.found = Some(expr);
+                            return;
+                        }
+                    }
+                }
+                hir::intravisit::walk_expr(self, ex);
+            }
+        }
+        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
+            }
+            _ => return false,
+        };
+        let mut v = V { search_span: span, found: None };
+        v.visit_body(self.tcx.hir().body(*body_id));
+        let Some(expr) = v.found else {
+            return false;
+        };
+        let Some(typeck) = &self.typeck_results else {
+            return false;
+        };
+        let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent()
+        else {
+            return false;
+        };
+        if !self.tcx.is_diagnostic_item(sym::FromResidual, y.def_id()) {
+            return false;
+        }
+        let self_ty = trait_ref.self_ty();
+        let found_ty = trait_ref.args.get(1).and_then(|a| a.as_type());
+
+        let mut prev_ty = self.resolve_vars_if_possible(
+            typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)),
+        );
+
+        // We always look at the `E` type, because that's the only one affected by `?`. If the
+        // incorrect `Result<T, E>` is because of the `T`, we'll get an E0308 on the whole
+        // expression, after the `?` has "unwrapped" the `T`.
+        let get_e_type = |prev_ty: Ty<'tcx>| -> Option<Ty<'tcx>> {
+            let ty::Adt(def, args) = prev_ty.kind() else {
+                return None;
+            };
+            let Some(arg) = args.get(1) else {
+                return None;
+            };
+            if !self.tcx.is_diagnostic_item(sym::Result, def.did()) {
+                return None;
+            }
+            arg.as_type()
+        };
+
+        let mut suggested = false;
+        let mut chain = vec![];
+
+        // The following logic is simlar to `point_at_chain`, but that's focused on associated types
+        let mut expr = expr;
+        while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind {
+            // Point at every method call in the chain with the `Result` type.
+            // let foo = bar.iter().map(mapper)?;
+            //               ------ -----------
+            expr = rcvr_expr;
+            chain.push((span, prev_ty));
+
+            let next_ty = self.resolve_vars_if_possible(
+                typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)),
+            );
+
+            let is_diagnostic_item = |symbol: Symbol, ty: Ty<'tcx>| {
+                let ty::Adt(def, _) = ty.kind() else {
+                    return false;
+                };
+                self.tcx.is_diagnostic_item(symbol, def.did())
+            };
+            // For each method in the chain, see if this is `Result::map_err` or
+            // `Option::ok_or_else` and if it is, see if the closure passed to it has an incorrect
+            // trailing `;`.
+            if let Some(ty) = get_e_type(prev_ty)
+                && let Some(found_ty) = found_ty
+                // Ideally we would instead use `FnCtxt::lookup_method_for_diagnostic` for 100%
+                // accurate check, but we are in the wrong stage to do that and looking for
+                // `Result::map_err` by checking the Self type and the path segment is enough.
+                // sym::ok_or_else
+                && (
+                    ( // Result::map_err
+                        path_segment.ident.name == sym::map_err
+                            && is_diagnostic_item(sym::Result, next_ty)
+                    ) || ( // Option::ok_or_else
+                        path_segment.ident.name == sym::ok_or_else
+                            && is_diagnostic_item(sym::Option, next_ty)
+                    )
+                )
+                // Found `Result<_, ()>?`
+                && let ty::Tuple(tys) = found_ty.kind()
+                && tys.is_empty()
+                // The current method call returns `Result<_, ()>`
+                && self.can_eq(obligation.param_env, ty, found_ty)
+                // There's a single argument in the method call and it is a closure
+                && args.len() == 1
+                && let Some(arg) = args.get(0)
+                && let hir::ExprKind::Closure(closure) = arg.kind
+                // The closure has a block for its body with no tail expression
+                && let body = self.tcx.hir().body(closure.body)
+                && let hir::ExprKind::Block(block, _) = body.value.kind
+                && let None = block.expr
+                // The last statement is of a type that can be converted to the return error type
+                && let [.., stmt] = block.stmts
+                && let hir::StmtKind::Semi(expr) = stmt.kind
+                && let expr_ty = self.resolve_vars_if_possible(
+                    typeck.expr_ty_adjusted_opt(expr)
+                        .unwrap_or(Ty::new_misc_error(self.tcx)),
+                )
+                && self
+                    .infcx
+                    .type_implements_trait(
+                        self.tcx.get_diagnostic_item(sym::From).unwrap(),
+                        [self_ty, expr_ty],
+                        obligation.param_env,
+                    )
+                    .must_apply_modulo_regions()
+            {
+                suggested = true;
+                err.span_suggestion_short(
+                    stmt.span.with_lo(expr.span.hi()),
+                    "remove this semicolon",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+            }
+
+            prev_ty = next_ty;
+
+            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 Some(parent) = self.tcx.hir().find_parent(binding.hir_id)
+            {
+                // We've reached the root of the method call chain...
+                if let hir::Node::Local(local) = parent
+                    && let Some(binding_expr) = local.init
+                {
+                    // ...and it is a binding. Get the binding creation and continue the chain.
+                    expr = binding_expr;
+                }
+                if let hir::Node::Param(_param) = parent {
+                    // ...and it is a an fn argument.
+                    break;
+                }
+            }
+        }
+        // `expr` is now the "root" expression of the method call chain, which can be any
+        // expression kind, like a method call or a path. If this expression is `Result<T, E>` as
+        // well, then we also point at it.
+        prev_ty = self.resolve_vars_if_possible(
+            typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)),
+        );
+        chain.push((expr.span, prev_ty));
+
+        let mut prev = None;
+        for (span, err_ty) in chain.into_iter().rev() {
+            let err_ty = get_e_type(err_ty);
+            let err_ty = match (err_ty, prev) {
+                (Some(err_ty), Some(prev)) if !self.can_eq(obligation.param_env, err_ty, prev) => {
+                    err_ty
+                }
+                (Some(err_ty), None) => err_ty,
+                _ => {
+                    prev = err_ty;
+                    continue;
+                }
+            };
+            if self
+                .infcx
+                .type_implements_trait(
+                    self.tcx.get_diagnostic_item(sym::From).unwrap(),
+                    [self_ty, err_ty],
+                    obligation.param_env,
+                )
+                .must_apply_modulo_regions()
+            {
+                if !suggested {
+                    err.span_label(span, format!("this has type `Result<_, {err_ty}>`"));
+                }
+            } else {
+                err.span_label(
+                    span,
+                    format!(
+                        "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`",
+                    ),
+                );
+            }
+            prev = Some(err_ty);
+        }
+        suggested
+    }
+
+    fn report_const_param_not_wf(
+        &self,
+        ty: Ty<'tcx>,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        let span = obligation.cause.span;
+
+        let mut diag = match ty.kind() {
+            _ if ty.has_param() => {
+                span_bug!(span, "const param tys cannot mention other generic parameters");
+            }
+            ty::Float(_) => {
+                struct_span_err!(
+                    self.dcx(),
+                    span,
+                    E0741,
+                    "`{ty}` is forbidden as the type of a const generic parameter",
+                )
+            }
+            ty::FnPtr(_) => {
+                struct_span_err!(
+                    self.dcx(),
+                    span,
+                    E0741,
+                    "using function pointers as const generic parameters is forbidden",
+                )
+            }
+            ty::RawPtr(_) => {
+                struct_span_err!(
+                    self.dcx(),
+                    span,
+                    E0741,
+                    "using raw pointers as const generic parameters is forbidden",
+                )
+            }
+            ty::Adt(def, _) => {
+                // We should probably see if we're *allowed* to derive `ConstParamTy` on the type...
+                let mut diag = struct_span_err!(
+                    self.dcx(),
+                    span,
+                    E0741,
+                    "`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter",
+                );
+                // Only suggest derive if this isn't a derived obligation,
+                // and the struct is local.
+                if let Some(span) = self.tcx.hir().span_if_local(def.did())
+                    && obligation.cause.code().parent().is_none()
+                {
+                    if ty.is_structural_eq_shallow(self.tcx) {
+                        diag.span_suggestion(
+                            span,
+                            "add `#[derive(ConstParamTy)]` to the struct",
+                            "#[derive(ConstParamTy)]\n",
+                            Applicability::MachineApplicable,
+                        );
+                    } else {
+                        // FIXME(adt_const_params): We should check there's not already an
+                        // overlapping `Eq`/`PartialEq` impl.
+                        diag.span_suggestion(
+                            span,
+                            "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct",
+                            "#[derive(ConstParamTy, PartialEq, Eq)]\n",
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+                diag
+            }
+            _ => {
+                struct_span_err!(
+                    self.dcx(),
+                    span,
+                    E0741,
+                    "`{ty}` can't be used as a const parameter type",
+                )
+            }
+        };
+
+        let mut code = obligation.cause.code();
+        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()
+                ));
+            }
+            code = next_code;
+            pred = next_pred;
+        }
+
+        diag
+    }
+}
+
+pub(super) trait InferCtxtPrivExt<'tcx> {
+    // returns if `cond` not occurring implies that `error` does not occur - i.e., that
+    // `error` occurring implies that `cond` occurs.
+    fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool;
+
+    fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>);
+
+    fn report_projection_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        error: &MismatchedProjectionTypes<'tcx>,
+    );
+
+    fn maybe_detailed_projection_msg(
+        &self,
+        pred: ty::ProjectionPredicate<'tcx>,
+        normalized_ty: ty::Term<'tcx>,
+        expected_ty: ty::Term<'tcx>,
+    ) -> Option<String>;
+
+    fn fuzzy_match_tys(
+        &self,
+        a: Ty<'tcx>,
+        b: Ty<'tcx>,
+        ignoring_lifetimes: bool,
+    ) -> Option<CandidateSimilarity>;
+
+    fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str;
+
+    fn find_similar_impl_candidates(
+        &self,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> Vec<ImplCandidate<'tcx>>;
+
+    fn report_similar_impl_candidates(
+        &self,
+        impl_candidates: &[ImplCandidate<'tcx>],
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        body_def_id: LocalDefId,
+        err: &mut Diagnostic,
+        other: bool,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> bool;
+
+    fn report_similar_impl_candidates_for_root_obligation(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
+        body_def_id: LocalDefId,
+        err: &mut Diagnostic,
+    );
+
+    /// Gets the parent trait chain start
+    fn get_parent_trait_ref(
+        &self,
+        code: &ObligationCauseCode<'tcx>,
+    ) -> Option<(Ty<'tcx>, Option<Span>)>;
+
+    /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
+    /// with the same path as `trait_ref`, a help message about
+    /// a probable version mismatch is added to `err`
+    fn note_version_mismatch(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: &ty::PolyTraitRef<'tcx>,
+    ) -> bool;
+
+    /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
+    /// `trait_ref`.
+    ///
+    /// For this to work, `new_self_ty` must have no escaping bound variables.
+    fn mk_trait_obligation_with_new_self_ty(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
+    ) -> PredicateObligation<'tcx>;
+
+    fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>);
+
+    fn predicate_can_apply(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool;
+
+    fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>);
+
+    fn suggest_unsized_bound_if_applicable(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+    );
+
+    fn annotate_source_of_ambiguity(
+        &self,
+        err: &mut Diagnostic,
+        impls: &[ambiguity::Ambiguity],
+        predicate: ty::Predicate<'tcx>,
+    );
+
+    fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>);
+
+    fn maybe_indirection_for_unsized(
+        &self,
+        err: &mut Diagnostic,
+        item: &'tcx Item<'tcx>,
+        param: &'tcx GenericParam<'tcx>,
+    ) -> bool;
+
+    fn is_recursive_obligation(
+        &self,
+        obligated_types: &mut Vec<Ty<'tcx>>,
+        cause_code: &ObligationCauseCode<'tcx>,
+    ) -> bool;
+
+    fn get_standard_error_message(
+        &self,
+        trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+        message: Option<String>,
+        predicate_is_const: bool,
+        append_const_msg: Option<AppendConstMessage>,
+        post_message: String,
+    ) -> String;
+
+    fn get_safe_transmute_error_and_reason(
+        &self,
+        obligation: PredicateObligation<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        span: Span,
+    ) -> GetSafeTransmuteErrorAndReason;
+
+    fn add_tuple_trait_message(
+        &self,
+        obligation_cause_code: &ObligationCauseCode<'tcx>,
+        err: &mut Diagnostic,
+    );
+
+    fn try_to_add_help_message(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+        err: &mut Diagnostic,
+        span: Span,
+        is_fn_trait: bool,
+        suggested: bool,
+        unsatisfied_const: bool,
+    );
+
+    fn add_help_message_for_fn_trait(
+        &self,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        err: &mut Diagnostic,
+        implemented_kind: ty::ClosureKind,
+        params: ty::Binder<'tcx, Ty<'tcx>>,
+    );
+
+    fn maybe_add_note_for_unsatisfied_const(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+        err: &mut Diagnostic,
+        span: Span,
+    ) -> UnsatisfiedConst;
+
+    fn report_closure_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        closure_def_id: DefId,
+        found_kind: ty::ClosureKind,
+        kind: ty::ClosureKind,
+    ) -> DiagnosticBuilder<'tcx>;
+
+    fn report_type_parameter_mismatch_cyclic_type_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        terr: TypeError<'tcx>,
+    ) -> DiagnosticBuilder<'tcx>;
+
+    fn report_opaque_type_auto_trait_leakage(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        def_id: DefId,
+    ) -> DiagnosticBuilder<'tcx>;
+
+    fn report_type_parameter_mismatch_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        span: Span,
+        found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+    ) -> Option<DiagnosticBuilder<'tcx>>;
+
+    fn report_not_const_evaluatable_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        span: Span,
+    ) -> Option<DiagnosticBuilder<'tcx>>;
+}
+
+impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
+    // returns if `cond` not occurring implies that `error` does not occur - i.e., that
+    // `error` occurring implies that `cond` occurs.
+    fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool {
+        if cond == error {
+            return true;
+        }
+
+        // FIXME: It should be possible to deal with `ForAll` in a cleaner way.
+        let bound_error = error.kind();
+        let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) {
+            (
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)),
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(error)),
+            ) => (cond, bound_error.rebind(error)),
+            _ => {
+                // FIXME: make this work in other cases too.
+                return false;
+            }
+        };
+
+        for pred in elaborate(self.tcx, std::iter::once(cond)) {
+            let bound_predicate = pred.kind();
+            if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(implication)) =
+                bound_predicate.skip_binder()
+            {
+                let error = error.to_poly_trait_ref();
+                let implication = bound_predicate.rebind(implication.trait_ref);
+                // FIXME: I'm just not taking associated types at all here.
+                // Eventually I'll need to implement param-env-aware
+                // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic.
+                let param_env = ty::ParamEnv::empty();
+                if self.can_sub(param_env, error, implication) {
+                    debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication);
+                    return true;
+                }
+            }
+        }
+
+        false
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) {
+        if self.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default()
+            == DumpSolverProofTree::OnError
+        {
+            dump_proof_tree(&error.root_obligation, self.infcx);
+        }
+
+        match error.code {
+            FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
+                self.report_selection_error(
+                    error.obligation.clone(),
+                    &error.root_obligation,
+                    selection_error,
+                );
+            }
+            FulfillmentErrorCode::CodeProjectionError(ref e) => {
+                self.report_projection_error(&error.obligation, e);
+            }
+            FulfillmentErrorCode::CodeAmbiguity { overflow: false } => {
+                self.maybe_report_ambiguity(&error.obligation);
+            }
+            FulfillmentErrorCode::CodeAmbiguity { overflow: true } => {
+                self.report_overflow_no_abort(error.obligation.clone());
+            }
+            FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => {
+                self.report_mismatched_types(
+                    &error.obligation.cause,
+                    expected_found.expected,
+                    expected_found.found,
+                    *err,
+                )
+                .emit();
+            }
+            FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => {
+                let mut diag = self.report_mismatched_consts(
+                    &error.obligation.cause,
+                    expected_found.expected,
+                    expected_found.found,
+                    *err,
+                );
+                let code = error.obligation.cause.code().peel_derives().peel_match_impls();
+                if let ObligationCauseCode::BindingObligation(..)
+                | ObligationCauseCode::ItemObligation(..)
+                | ObligationCauseCode::ExprBindingObligation(..)
+                | ObligationCauseCode::ExprItemObligation(..) = code
+                {
+                    self.note_obligation_cause_code(
+                        error.obligation.cause.body_id,
+                        &mut diag,
+                        error.obligation.predicate,
+                        error.obligation.param_env,
+                        code,
+                        &mut vec![],
+                        &mut Default::default(),
+                    );
+                }
+                diag.emit();
+            }
+            FulfillmentErrorCode::CodeCycle(ref cycle) => {
+                self.report_overflow_obligation_cycle(cycle);
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn report_projection_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        error: &MismatchedProjectionTypes<'tcx>,
+    ) {
+        let predicate = self.resolve_vars_if_possible(obligation.predicate);
+
+        if predicate.references_error() {
+            return;
+        }
+
+        self.probe(|_| {
+            let ocx = ObligationCtxt::new(self);
+
+            // try to find the mismatched types to report the error with.
+            //
+            // this can fail if the problem was higher-ranked, in which
+            // cause I have no idea for a good error message.
+            let bound_predicate = predicate.kind();
+            let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) =
+                bound_predicate.skip_binder()
+            {
+                let data = self.instantiate_binder_with_fresh_vars(
+                    obligation.cause.span,
+                    infer::BoundRegionConversionTime::HigherRankedType,
+                    bound_predicate.rebind(data),
+                );
+                let unnormalized_term = match data.term.unpack() {
+                    ty::TermKind::Ty(_) => Ty::new_projection(
+                        self.tcx,
+                        data.projection_ty.def_id,
+                        data.projection_ty.args,
+                    )
+                    .into(),
+                    ty::TermKind::Const(ct) => ty::Const::new_unevaluated(
+                        self.tcx,
+                        ty::UnevaluatedConst {
+                            def: data.projection_ty.def_id,
+                            args: data.projection_ty.args,
+                        },
+                        ct.ty(),
+                    )
+                    .into(),
+                };
+                // FIXME(-Znext-solver): For diagnostic purposes, it would be nice
+                // to deeply normalize this type.
+                let normalized_term =
+                    ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
+
+                debug!(?obligation.cause, ?obligation.param_env);
+
+                debug!(?normalized_term, data.ty = ?data.term);
+
+                let is_normalized_term_expected = !matches!(
+                    obligation.cause.code().peel_derives(),
+                    ObligationCauseCode::ItemObligation(_)
+                        | ObligationCauseCode::BindingObligation(_, _)
+                        | ObligationCauseCode::ExprItemObligation(..)
+                        | ObligationCauseCode::ExprBindingObligation(..)
+                        | ObligationCauseCode::Coercion { .. }
+                );
+
+                // constrain inference variables a bit more to nested obligations from normalize so
+                // we can have more helpful errors.
+                //
+                // we intentionally drop errors from normalization here,
+                // since the normalization is just done to improve the error message.
+                let _ = ocx.select_where_possible();
+
+                if let Err(new_err) = ocx.eq_exp(
+                    &obligation.cause,
+                    obligation.param_env,
+                    is_normalized_term_expected,
+                    normalized_term,
+                    data.term,
+                ) {
+                    (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err)
+                } else {
+                    (None, error.err)
+                }
+            } else {
+                (None, error.err)
+            };
+
+            let msg = values
+                .and_then(|(predicate, _, normalized_term, expected_term)| {
+                    self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term)
+                })
+                .unwrap_or_else(|| {
+                    let mut cx = FmtPrinter::new_with_limit(
+                        self.tcx,
+                        Namespace::TypeNS,
+                        rustc_session::Limit(10),
+                    );
+                    with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", {
+                        self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
+                        cx.into_buffer()
+                    }))
+                });
+            let mut diag = struct_span_err!(self.dcx(), obligation.cause.span, E0271, "{msg}");
+
+            let secondary_span = (|| {
+                let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) =
+                    predicate.kind().skip_binder()
+                else {
+                    return None;
+                };
+
+                let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_ty.def_id)?;
+                let trait_assoc_ident = trait_assoc_item.ident(self.tcx);
+
+                let mut associated_items = vec![];
+                self.tcx.for_each_relevant_impl(
+                    self.tcx.trait_of_item(proj.projection_ty.def_id)?,
+                    proj.projection_ty.self_ty(),
+                    |impl_def_id| {
+                        associated_items.extend(
+                            self.tcx
+                                .associated_items(impl_def_id)
+                                .in_definition_order()
+                                .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident),
+                        );
+                    },
+                );
+
+                let [associated_item]: &[ty::AssocItem] = &associated_items[..] else {
+                    return None;
+                };
+                match self.tcx.hir().get_if_local(associated_item.def_id) {
+                    Some(
+                        hir::Node::TraitItem(hir::TraitItem {
+                            kind: hir::TraitItemKind::Type(_, Some(ty)),
+                            ..
+                        })
+                        | hir::Node::ImplItem(hir::ImplItem {
+                            kind: hir::ImplItemKind::Type(ty),
+                            ..
+                        }),
+                    ) => Some((
+                        ty.span,
+                        with_forced_trimmed_paths!(Cow::from(format!(
+                            "type mismatch resolving `{}`",
+                            {
+                                let mut cx = FmtPrinter::new_with_limit(
+                                    self.tcx,
+                                    Namespace::TypeNS,
+                                    rustc_session::Limit(5),
+                                );
+                                self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
+                                cx.into_buffer()
+                            }
+                        ))),
+                    )),
+                    _ => None,
+                }
+            })();
+
+            self.note_type_err(
+                &mut diag,
+                &obligation.cause,
+                secondary_span,
+                values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| {
+                    infer::ValuePairs::Terms(ExpectedFound::new(
+                        is_normalized_ty_expected,
+                        normalized_ty,
+                        expected_ty,
+                    ))
+                }),
+                err,
+                true,
+                false,
+            );
+            self.note_obligation_cause(&mut diag, obligation);
+            diag.emit();
+        });
+    }
+
+    fn maybe_detailed_projection_msg(
+        &self,
+        pred: ty::ProjectionPredicate<'tcx>,
+        normalized_ty: ty::Term<'tcx>,
+        expected_ty: ty::Term<'tcx>,
+    ) -> Option<String> {
+        let trait_def_id = pred.projection_ty.trait_def_id(self.tcx);
+        let self_ty = pred.projection_ty.self_ty();
+
+        with_forced_trimmed_paths! {
+            if Some(pred.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() {
+                let fn_kind = self_ty.prefix_string(self.tcx);
+                let item = match self_ty.kind() {
+                    ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(),
+                    _ => self_ty.to_string(),
+                };
+                Some(format!(
+                    "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \
+                     returns `{normalized_ty}`",
+                ))
+            } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() {
+                Some(format!(
+                    "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \
+                     resolves to `{normalized_ty}`"
+                ))
+            } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) {
+                Some(format!(
+                    "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \
+                     yields `{normalized_ty}`"
+                ))
+            } else {
+                None
+            }
+        }
+    }
+
+    fn fuzzy_match_tys(
+        &self,
+        mut a: Ty<'tcx>,
+        mut b: Ty<'tcx>,
+        ignoring_lifetimes: bool,
+    ) -> Option<CandidateSimilarity> {
+        /// returns the fuzzy category of a given type, or None
+        /// if the type can be equated to any type.
+        fn type_category(tcx: TyCtxt<'_>, t: Ty<'_>) -> Option<u32> {
+            match t.kind() {
+                ty::Bool => Some(0),
+                ty::Char => Some(1),
+                ty::Str => Some(2),
+                ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => Some(2),
+                ty::Int(..)
+                | ty::Uint(..)
+                | ty::Float(..)
+                | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) => Some(4),
+                ty::Ref(..) | ty::RawPtr(..) => Some(5),
+                ty::Array(..) | ty::Slice(..) => Some(6),
+                ty::FnDef(..) | ty::FnPtr(..) => Some(7),
+                ty::Dynamic(..) => Some(8),
+                ty::Closure(..) => Some(9),
+                ty::Tuple(..) => Some(10),
+                ty::Param(..) => Some(11),
+                ty::Alias(ty::Projection, ..) => Some(12),
+                ty::Alias(ty::Inherent, ..) => Some(13),
+                ty::Alias(ty::Opaque, ..) => Some(14),
+                ty::Alias(ty::Weak, ..) => Some(15),
+                ty::Never => Some(16),
+                ty::Adt(..) => Some(17),
+                ty::Coroutine(..) => Some(18),
+                ty::Foreign(..) => Some(19),
+                ty::CoroutineWitness(..) => Some(20),
+                ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None,
+            }
+        }
+
+        let strip_references = |mut t: Ty<'tcx>| -> Ty<'tcx> {
+            loop {
+                match t.kind() {
+                    ty::Ref(_, inner, _) | ty::RawPtr(ty::TypeAndMut { ty: inner, .. }) => {
+                        t = *inner
+                    }
+                    _ => break t,
+                }
+            }
+        };
+
+        if !ignoring_lifetimes {
+            a = strip_references(a);
+            b = strip_references(b);
+        }
+
+        let cat_a = type_category(self.tcx, a)?;
+        let cat_b = type_category(self.tcx, b)?;
+        if a == b {
+            Some(CandidateSimilarity::Exact { ignoring_lifetimes })
+        } else if cat_a == cat_b {
+            match (a.kind(), b.kind()) {
+                (ty::Adt(def_a, _), ty::Adt(def_b, _)) => def_a == def_b,
+                (ty::Foreign(def_a), ty::Foreign(def_b)) => def_a == def_b,
+                // Matching on references results in a lot of unhelpful
+                // suggestions, so let's just not do that for now.
+                //
+                // We still upgrade successful matches to `ignoring_lifetimes: true`
+                // to prioritize that impl.
+                (ty::Ref(..) | ty::RawPtr(..), ty::Ref(..) | ty::RawPtr(..)) => {
+                    self.fuzzy_match_tys(a, b, true).is_some()
+                }
+                _ => true,
+            }
+            .then_some(CandidateSimilarity::Fuzzy { ignoring_lifetimes })
+        } else if ignoring_lifetimes {
+            None
+        } else {
+            self.fuzzy_match_tys(a, b, true)
+        }
+    }
+
+    fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str {
+        match kind {
+            hir::ClosureKind::Closure => "a closure",
+            hir::ClosureKind::Coroutine(kind) => match kind {
+                hir::CoroutineKind::Coroutine(_) => "a coroutine",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Async,
+                    hir::CoroutineSource::Block,
+                ) => "an async block",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Async,
+                    hir::CoroutineSource::Fn,
+                ) => "an async function",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Async,
+                    hir::CoroutineSource::Closure,
+                ) => "an async closure",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::AsyncGen,
+                    hir::CoroutineSource::Block,
+                ) => "an async gen block",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::AsyncGen,
+                    hir::CoroutineSource::Fn,
+                ) => "an async gen function",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::AsyncGen,
+                    hir::CoroutineSource::Closure,
+                ) => "an async gen closure",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Gen,
+                    hir::CoroutineSource::Block,
+                ) => "a gen block",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Gen,
+                    hir::CoroutineSource::Fn,
+                ) => "a gen function",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Gen,
+                    hir::CoroutineSource::Closure,
+                ) => "a gen closure",
+            },
+        }
+    }
+
+    fn find_similar_impl_candidates(
+        &self,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> Vec<ImplCandidate<'tcx>> {
+        let mut candidates: Vec<_> = self
+            .tcx
+            .all_impls(trait_pred.def_id())
+            .filter_map(|def_id| {
+                if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative
+                    || !self.tcx.is_user_visible_dep(def_id.krate)
+                {
+                    return None;
+                }
+
+                let imp = self.tcx.impl_trait_ref(def_id).unwrap().skip_binder();
+
+                self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false).map(
+                    |similarity| ImplCandidate { trait_ref: imp, similarity, impl_def_id: def_id },
+                )
+            })
+            .collect();
+        if candidates.iter().any(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })) {
+            // If any of the candidates is a perfect match, we don't want to show all of them.
+            // This is particularly relevant for the case of numeric types (as they all have the
+            // same category).
+            candidates.retain(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. }));
+        }
+        candidates
+    }
+
+    fn report_similar_impl_candidates(
+        &self,
+        impl_candidates: &[ImplCandidate<'tcx>],
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        body_def_id: LocalDefId,
+        err: &mut Diagnostic,
+        other: bool,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> bool {
+        // If we have a single implementation, try to unify it with the trait ref
+        // that failed. This should uncover a better hint for what *is* implemented.
+        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;
+                }
+
+                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;
+                    }
+                }
+
+                // Literally nothing unified, just give up.
+                if terrs.len() == impl_trait_ref.args.len() {
+                    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![
+                    (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle),
+                    ("is".to_string(), Style::Highlight),
+                    (" implemented for `".to_string(), Style::NoStyle),
+                    (cand.self_ty().to_string(), Style::Highlight),
+                    ("`".to_string(), Style::NoStyle),
+                ]);
+
+                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
+            }) {
+                return true;
+            }
+        }
+
+        let other = if other { "other " } else { "" };
+        let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
+            if candidates.is_empty() {
+                return false;
+            }
+            if let &[cand] = &candidates[..] {
+                let (desc, mention_castable) =
+                    match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) {
+                        (ty::FnPtr(_), ty::FnDef(..)) => {
+                            (" implemented for fn pointer `", ", cast using `as`")
+                        }
+                        (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""),
+                        _ => (" implemented for `", ""),
+                    };
+                err.highlighted_help(vec![
+                    (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle),
+                    ("is".to_string(), Style::Highlight),
+                    (desc.to_string(), Style::NoStyle),
+                    (cand.self_ty().to_string(), Style::Highlight),
+                    ("`".to_string(), Style::NoStyle),
+                    (mention_castable.to_string(), Style::NoStyle),
+                ]);
+                return true;
+            }
+            let trait_ref = TraitRef::identity(self.tcx, candidates[0].def_id);
+            // Check if the trait is the same in all cases. If so, we'll only show the type.
+            let mut traits: Vec<_> =
+                candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect();
+            traits.sort();
+            traits.dedup();
+            // FIXME: this could use a better heuristic, like just checking
+            // that args[1..] is the same.
+            let all_traits_equal = traits.len() == 1;
+
+            let candidates: Vec<String> = candidates
+                .into_iter()
+                .map(|c| {
+                    if all_traits_equal {
+                        format!("\n  {}", c.self_ty())
+                    } else {
+                        format!("\n  {c}")
+                    }
+                })
+                .collect();
+
+            let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
+            err.help(format!(
+                "the following {other}types implement trait `{}`:{}{}",
+                trait_ref.print_trait_sugared(),
+                candidates[..end].join(""),
+                if candidates.len() > 9 {
+                    format!("\nand {} others", candidates.len() - 8)
+                } else {
+                    String::new()
+                }
+            ));
+            true
+        };
+
+        let def_id = trait_ref.def_id();
+        if impl_candidates.is_empty() {
+            if self.tcx.trait_is_auto(def_id)
+                || self.tcx.lang_items().iter().any(|(_, id)| id == def_id)
+                || self.tcx.get_diagnostic_name(def_id).is_some()
+            {
+                // Mentioning implementers of `Copy`, `Debug` and friends is not useful.
+                return false;
+            }
+            let mut impl_candidates: Vec<_> = self
+                .tcx
+                .all_impls(def_id)
+                // Ignore automatically derived impls and `!Trait` impls.
+                .filter(|&def_id| {
+                    self.tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative
+                        || self.tcx.is_automatically_derived(def_id)
+                })
+                .filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
+                .map(ty::EarlyBinder::instantiate_identity)
+                .filter(|trait_ref| {
+                    let self_ty = trait_ref.self_ty();
+                    // Avoid mentioning type parameters.
+                    if let ty::Param(_) = self_ty.kind() {
+                        false
+                    }
+                    // Avoid mentioning types that are private to another crate
+                    else if let ty::Adt(def, _) = self_ty.peel_refs().kind() {
+                        // FIXME(compiler-errors): This could be generalized, both to
+                        // be more granular, and probably look past other `#[fundamental]`
+                        // types, too.
+                        self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx)
+                    } else {
+                        true
+                    }
+                })
+                .collect();
+
+            impl_candidates.sort();
+            impl_candidates.dedup();
+            return report(impl_candidates, err);
+        }
+
+        // Sort impl candidates so that ordering is consistent for UI tests.
+        // because the ordering of `impl_candidates` may not be deterministic:
+        // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507
+        //
+        // Prefer more similar candidates first, then sort lexicographically
+        // by their normalized string representation.
+        let mut impl_candidates: Vec<_> = impl_candidates
+            .iter()
+            .cloned()
+            .map(|mut cand| {
+                // Fold the consts so that they shows up as, e.g., `10`
+                // instead of `core::::array::{impl#30}::{constant#0}`.
+                cand.trait_ref = cand.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()),
+                });
+                cand
+            })
+            .collect();
+        impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref));
+        let mut impl_candidates: Vec<_> =
+            impl_candidates.into_iter().map(|cand| cand.trait_ref).collect();
+        impl_candidates.dedup();
+
+        report(impl_candidates, err)
+    }
+
+    fn report_similar_impl_candidates_for_root_obligation(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
+        body_def_id: LocalDefId,
+        err: &mut Diagnostic,
+    ) {
+        // This is *almost* equivalent to
+        // `obligation.cause.code().peel_derives()`, but it gives us the
+        // trait predicate for that corresponding root obligation. This
+        // lets us get a derived obligation from a type parameter, like
+        // when calling `string.strip_suffix(p)` where `p` is *not* an
+        // implementer of `Pattern<'_>`.
+        let mut code = obligation.cause.code();
+        let mut trait_pred = trait_predicate;
+        let mut peeled = false;
+        while let Some((parent_code, parent_trait_pred)) = code.parent() {
+            code = parent_code;
+            if let Some(parent_trait_pred) = parent_trait_pred {
+                trait_pred = parent_trait_pred;
+                peeled = true;
+            }
+        }
+        let def_id = trait_pred.def_id();
+        // Mention *all* the `impl`s for the *top most* obligation, the
+        // user might have meant to use one of them, if any found. We skip
+        // auto-traits or fundamental traits that might not be exactly what
+        // the user might expect to be presented with. Instead this is
+        // useful for less general traits.
+        if peeled
+            && !self.tcx.trait_is_auto(def_id)
+            && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id)
+        {
+            let trait_ref = trait_pred.to_poly_trait_ref();
+            let impl_candidates = self.find_similar_impl_candidates(trait_pred);
+            self.report_similar_impl_candidates(
+                &impl_candidates,
+                trait_ref,
+                body_def_id,
+                err,
+                true,
+                obligation.param_env,
+            );
+        }
+    }
+
+    /// Gets the parent trait chain start
+    fn get_parent_trait_ref(
+        &self,
+        code: &ObligationCauseCode<'tcx>,
+    ) -> Option<(Ty<'tcx>, Option<Span>)> {
+        match code {
+            ObligationCauseCode::BuiltinDerivedObligation(data) => {
+                let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred);
+                match self.get_parent_trait_ref(&data.parent_code) {
+                    Some(t) => Some(t),
+                    None => {
+                        let ty = parent_trait_ref.skip_binder().self_ty();
+                        let span = TyCategory::from_ty(self.tcx, ty)
+                            .map(|(_, def_id)| self.tcx.def_span(def_id));
+                        Some((ty, span))
+                    }
+                }
+            }
+            ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => {
+                self.get_parent_trait_ref(parent_code)
+            }
+            _ => None,
+        }
+    }
+
+    /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
+    /// with the same path as `trait_ref`, a help message about
+    /// a probable version mismatch is added to `err`
+    fn note_version_mismatch(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: &ty::PolyTraitRef<'tcx>,
+    ) -> bool {
+        let get_trait_impls = |trait_def_id| {
+            let mut trait_impls = vec![];
+            self.tcx.for_each_relevant_impl(
+                trait_def_id,
+                trait_ref.skip_binder().self_ty(),
+                |impl_def_id| {
+                    trait_impls.push(impl_def_id);
+                },
+            );
+            trait_impls
+        };
+
+        let required_trait_path = self.tcx.def_path_str(trait_ref.def_id());
+        let traits_with_same_path: std::collections::BTreeSet<_> = self
+            .tcx
+            .all_traits()
+            .filter(|trait_def_id| *trait_def_id != trait_ref.def_id())
+            .filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path)
+            .collect();
+        let mut suggested = false;
+        for trait_with_same_path in traits_with_same_path {
+            let trait_impls = get_trait_impls(trait_with_same_path);
+            if trait_impls.is_empty() {
+                continue;
+            }
+            let impl_spans: Vec<_> =
+                trait_impls.iter().map(|impl_def_id| self.tcx.def_span(*impl_def_id)).collect();
+            err.span_help(
+                impl_spans,
+                format!("trait impl{} with same name found", pluralize!(trait_impls.len())),
+            );
+            let trait_crate = self.tcx.crate_name(trait_with_same_path.krate);
+            let crate_msg =
+                format!("perhaps two different versions of crate `{trait_crate}` are being used?");
+            err.note(crate_msg);
+            suggested = true;
+        }
+        suggested
+    }
+
+    fn mk_trait_obligation_with_new_self_ty(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
+    ) -> PredicateObligation<'tcx> {
+        let trait_pred =
+            trait_ref_and_ty.map_bound(|(tr, new_self_ty)| tr.with_self_ty(self.tcx, new_self_ty));
+
+        Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred)
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) {
+        // Unable to successfully determine, probably means
+        // insufficient type information, but could mean
+        // ambiguous impls. The latter *ought* to be a
+        // coherence violation, so we don't report it here.
+
+        let predicate = self.resolve_vars_if_possible(obligation.predicate);
+        let span = obligation.cause.span;
+
+        debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
+
+        // Ambiguity errors are often caused as fallout from earlier errors.
+        // We ignore them if this `infcx` is tainted in some cases below.
+
+        let bound_predicate = predicate.kind();
+        let mut err = match bound_predicate.skip_binder() {
+            ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
+                let trait_ref = bound_predicate.rebind(data.trait_ref);
+                debug!(?trait_ref);
+
+                if predicate.references_error() {
+                    return;
+                }
+
+                // 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
+                // #0> doesn't implement Sized". It may even be true that we
+                // could just skip over all checks where the self-ty is an
+                // inference variable, but I was afraid that there might be an
+                // inference variable created, registered as an obligation, and
+                // then never forced by writeback, and hence by skipping here we'd
+                // be ignoring the fact that we don't KNOW the type works
+                // out. Though even that would probably be harmless, given that
+                // we're only talking about builtin traits, which are known to be
+                // inhabited. We used to check for `self.tcx.sess.has_errors()` to
+                // avoid inundating the user with unnecessary errors, but we now
+                // check upstream for type errors and don't add the obligations to
+                // begin with in those cases.
+                if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) {
+                    if let None = self.tainted_by_errors() {
+                        let err = self.emit_inference_failure_err(
+                            obligation.cause.body_id,
+                            span,
+                            trait_ref.self_ty().skip_binder().into(),
+                            ErrorCode::E0282,
+                            false,
+                        );
+                        err.stash(span, StashKey::MaybeForgetReturn);
+                    }
+                    return;
+                }
+
+                // Typically, this ambiguity should only happen if
+                // there are unresolved type inference variables
+                // (otherwise it would suggest a coherence
+                // failure). But given #21974 that is not necessarily
+                // the case -- we can have multiple where clauses that
+                // are only distinguished by a region, which results
+                // in an ambiguity even when all types are fully
+                // known, since we don't dispatch based on region
+                // relationships.
+
+                // Pick the first substitution that still contains inference variables as the one
+                // we're going to emit an error for. If there are none (see above), fall back to
+                // a more general error.
+                let subst = data.trait_ref.args.iter().find(|s| s.has_non_region_infer());
+
+                let mut err = if let Some(subst) = subst {
+                    self.emit_inference_failure_err(
+                        obligation.cause.body_id,
+                        span,
+                        subst,
+                        ErrorCode::E0283,
+                        true,
+                    )
+                } else {
+                    struct_span_err!(
+                        self.dcx(),
+                        span,
+                        E0283,
+                        "type annotations needed: cannot satisfy `{}`",
+                        predicate,
+                    )
+                };
+
+                let mut ambiguities = ambiguity::recompute_applicable_impls(
+                    self.infcx,
+                    &obligation.with(self.tcx, trait_ref),
+                );
+                let has_non_region_infer =
+                    trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer());
+                // It doesn't make sense to talk about applicable impls if there are more than a
+                // handful of them. If there are a lot of them, but only a few of them have no type
+                // params, we only show those, as they are more likely to be useful/intended.
+                if ambiguities.len() > 5 {
+                    let infcx = self.infcx;
+                    if !ambiguities.iter().all(|option| match option {
+                        DefId(did) => infcx.fresh_args_for_item(DUMMY_SP, *did).is_empty(),
+                        ParamEnv(_) => true,
+                    }) {
+                        // If not all are blanket impls, we filter blanked impls out.
+                        ambiguities.retain(|option| match option {
+                            DefId(did) => infcx.fresh_args_for_item(DUMMY_SP, *did).is_empty(),
+                            ParamEnv(_) => true,
+                        });
+                    }
+                }
+                if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
+                    if self.tainted_by_errors().is_some() && subst.is_none() {
+                        // If `subst.is_none()`, then this is probably two param-env
+                        // candidates or impl candidates that are equal modulo lifetimes.
+                        // Therefore, if we've already emitted an error, just skip this
+                        // one, since it's not particularly actionable.
+                        err.cancel();
+                        return;
+                    }
+                    self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
+                } else {
+                    if self.tainted_by_errors().is_some() {
+                        err.cancel();
+                        return;
+                    }
+                    err.note(format!("cannot satisfy `{predicate}`"));
+                    let impl_candidates = self
+                        .find_similar_impl_candidates(predicate.to_opt_poly_trait_pred().unwrap());
+                    if impl_candidates.len() < 40 {
+                        self.report_similar_impl_candidates(
+                            impl_candidates.as_slice(),
+                            trait_ref,
+                            obligation.cause.body_id,
+                            &mut err,
+                            false,
+                            obligation.param_env,
+                        );
+                    }
+                }
+
+                if let ObligationCauseCode::ItemObligation(def_id)
+                | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code()
+                {
+                    self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
+                }
+
+                if let Some(ty::GenericArgKind::Type(_)) = subst.map(|subst| subst.unpack())
+                    && let Some(body_id) =
+                        self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
+                {
+                    let mut expr_finder = FindExprBySpan::new(span);
+                    expr_finder.visit_expr(self.tcx.hir().body(body_id).value);
+
+                    if let Some(hir::Expr {
+                        kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
+                        ..
+                    }) = expr_finder.result
+                        && let [
+                            ..,
+                            trait_path_segment @ hir::PathSegment {
+                                res: Res::Def(DefKind::Trait, trait_id),
+                                ..
+                            },
+                            hir::PathSegment {
+                                ident: assoc_item_name,
+                                res: Res::Def(_, item_id),
+                                ..
+                            },
+                        ] = path.segments
+                        && data.trait_ref.def_id == *trait_id
+                        && self.tcx.trait_of_item(*item_id) == Some(*trait_id)
+                        && let None = self.tainted_by_errors()
+                    {
+                        let (verb, noun) = match self.tcx.associated_item(item_id).kind {
+                            ty::AssocKind::Const => ("refer to the", "constant"),
+                            ty::AssocKind::Fn => ("call", "function"),
+                            // This is already covered by E0223, but this following single match
+                            // arm doesn't hurt here.
+                            ty::AssocKind::Type => ("refer to the", "type"),
+                        };
+
+                        // Replace the more general E0283 with a more specific error
+                        err.cancel();
+                        err = self.dcx().struct_span_err_with_code(
+                            span,
+                            format!(
+                                "cannot {verb} associated {noun} on trait without specifying the \
+                                 corresponding `impl` type",
+                            ),
+                            rustc_errors::error_code!(E0790),
+                        );
+
+                        if let Some(local_def_id) = data.trait_ref.def_id.as_local()
+                            && let Some(hir::Node::Item(hir::Item {
+                                ident: trait_name,
+                                kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs),
+                                ..
+                            })) = self.tcx.opt_hir_node_by_def_id(local_def_id)
+                            && let Some(method_ref) = trait_item_refs
+                                .iter()
+                                .find(|item_ref| item_ref.ident == *assoc_item_name)
+                        {
+                            err.span_label(
+                                method_ref.span,
+                                format!("`{trait_name}::{assoc_item_name}` defined here"),
+                            );
+                        }
+
+                        err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
+
+                        let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
+
+                        if let Some(impl_def_id) =
+                            trait_impls.non_blanket_impls().values().flatten().next()
+                        {
+                            let non_blanket_impl_count =
+                                trait_impls.non_blanket_impls().values().flatten().count();
+                            // If there is only one implementation of the trait, suggest using it.
+                            // Otherwise, use a placeholder comment for the implementation.
+                            let (message, self_type) = if non_blanket_impl_count == 1 {
+                                (
+                                    "use the fully-qualified path to the only available \
+                                     implementation",
+                                    format!(
+                                        "{}",
+                                        self.tcx.type_of(impl_def_id).instantiate_identity()
+                                    ),
+                                )
+                            } else {
+                                (
+                                    "use a fully-qualified path to a specific available \
+                                     implementation",
+                                    "/* self type */".to_string(),
+                                )
+                            };
+                            let mut suggestions =
+                                vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))];
+                            if let Some(generic_arg) = trait_path_segment.args {
+                                let between_span =
+                                    trait_path_segment.ident.span.between(generic_arg.span_ext);
+                                // get rid of :: between Trait and <type>
+                                // must be '::' between them, otherwise the parser won't accept the code
+                                suggestions.push((between_span, "".to_string()));
+                                suggestions
+                                    .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
+                            } else {
+                                suggestions.push((
+                                    trait_path_segment.ident.span.shrink_to_hi(),
+                                    ">".to_string(),
+                                ));
+                            }
+                            err.multipart_suggestion(
+                                message,
+                                suggestions,
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
+                };
+
+                err
+            }
+
+            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+                // Same hacky approach as above to avoid deluging user
+                // with error messages.
+                if arg.references_error()
+                    || self.dcx().has_errors().is_some()
+                    || self.tainted_by_errors().is_some()
+                {
+                    return;
+                }
+
+                self.emit_inference_failure_err(
+                    obligation.cause.body_id,
+                    span,
+                    arg,
+                    ErrorCode::E0282,
+                    false,
+                )
+            }
+
+            ty::PredicateKind::Subtype(data) => {
+                if data.references_error()
+                    || self.dcx().has_errors().is_some()
+                    || self.tainted_by_errors().is_some()
+                {
+                    // no need to overload user in such cases
+                    return;
+                }
+                let SubtypePredicate { a_is_expected: _, a, b } = data;
+                // both must be type variables, or the other would've been instantiated
+                assert!(a.is_ty_var() && b.is_ty_var());
+                self.emit_inference_failure_err(
+                    obligation.cause.body_id,
+                    span,
+                    a.into(),
+                    ErrorCode::E0282,
+                    true,
+                )
+            }
+            ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
+                if predicate.references_error() || self.tainted_by_errors().is_some() {
+                    return;
+                }
+                let subst = data
+                    .projection_ty
+                    .args
+                    .iter()
+                    .chain(Some(data.term.into_arg()))
+                    .find(|g| g.has_non_region_infer());
+                if let Some(subst) = subst {
+                    let mut err = self.emit_inference_failure_err(
+                        obligation.cause.body_id,
+                        span,
+                        subst,
+                        ErrorCode::E0284,
+                        true,
+                    );
+                    err.note(format!("cannot satisfy `{predicate}`"));
+                    err
+                } else {
+                    // If we can't find a substitution, just print a generic error
+                    let mut err = struct_span_err!(
+                        self.dcx(),
+                        span,
+                        E0284,
+                        "type annotations needed: cannot satisfy `{}`",
+                        predicate,
+                    );
+                    err.span_label(span, format!("cannot satisfy `{predicate}`"));
+                    err
+                }
+            }
+
+            ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
+                if predicate.references_error() || self.tainted_by_errors().is_some() {
+                    return;
+                }
+                let subst = data.walk().find(|g| g.is_non_region_infer());
+                if let Some(subst) = subst {
+                    let err = self.emit_inference_failure_err(
+                        obligation.cause.body_id,
+                        span,
+                        subst,
+                        ErrorCode::E0284,
+                        true,
+                    );
+                    err
+                } else {
+                    // If we can't find a substitution, just print a generic error
+                    let mut err = struct_span_err!(
+                        self.dcx(),
+                        span,
+                        E0284,
+                        "type annotations needed: cannot satisfy `{}`",
+                        predicate,
+                    );
+                    err.span_label(span, format!("cannot satisfy `{predicate}`"));
+                    err
+                }
+            }
+            _ => {
+                if self.dcx().has_errors().is_some() || self.tainted_by_errors().is_some() {
+                    return;
+                }
+                let mut err = struct_span_err!(
+                    self.dcx(),
+                    span,
+                    E0284,
+                    "type annotations needed: cannot satisfy `{}`",
+                    predicate,
+                );
+                err.span_label(span, format!("cannot satisfy `{predicate}`"));
+                err
+            }
+        };
+        self.note_obligation_cause(&mut err, obligation);
+        err.emit();
+    }
+
+    fn annotate_source_of_ambiguity(
+        &self,
+        err: &mut Diagnostic,
+        ambiguities: &[ambiguity::Ambiguity],
+        predicate: ty::Predicate<'tcx>,
+    ) {
+        let mut spans = vec![];
+        let mut crates = vec![];
+        let mut post = vec![];
+        let mut has_param_env = false;
+        for ambiguity in ambiguities {
+            match ambiguity {
+                ambiguity::Ambiguity::DefId(impl_def_id) => {
+                    match self.tcx.span_of_impl(*impl_def_id) {
+                        Ok(span) => spans.push(span),
+                        Err(name) => {
+                            crates.push(name);
+                            if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
+                                post.push(header);
+                            }
+                        }
+                    }
+                }
+                ambiguity::Ambiguity::ParamEnv(span) => {
+                    has_param_env = true;
+                    spans.push(*span);
+                }
+            }
+        }
+        let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect();
+        crate_names.sort();
+        crate_names.dedup();
+        post.sort();
+        post.dedup();
+
+        if self.tainted_by_errors().is_some()
+            && (crate_names.len() == 1
+                && spans.len() == 0
+                && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str())
+                || predicate.visit_with(&mut HasNumericInferVisitor).is_break())
+        {
+            // Avoid complaining about other inference issues for expressions like
+            // `42 >> 1`, where the types are still `{integer}`, but we want to
+            // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too?
+            // NOTE(eddyb) this was `.cancel()`, but `err`
+            // is borrowed, so we can't fully defuse it.
+            err.downgrade_to_delayed_bug();
+            return;
+        }
+
+        let msg = format!(
+            "multiple `impl`s{} satisfying `{}` found",
+            if has_param_env { " or `where` clauses" } else { "" },
+            predicate
+        );
+        let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
+            format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),)
+        } else if post.len() == 1 {
+            format!(": `{}`", post[0])
+        } else {
+            String::new()
+        };
+
+        match (spans.len(), crates.len(), crate_names.len()) {
+            (0, 0, 0) => {
+                err.note(format!("cannot satisfy `{predicate}`"));
+            }
+            (0, _, 1) => {
+                err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,));
+            }
+            (0, _, _) => {
+                err.note(format!(
+                    "{} in the following crates: {}{}",
+                    msg,
+                    crate_names.join(", "),
+                    post,
+                ));
+            }
+            (_, 0, 0) => {
+                let span: MultiSpan = spans.into();
+                err.span_note(span, msg);
+            }
+            (_, 1, 1) => {
+                let span: MultiSpan = spans.into();
+                err.span_note(span, msg);
+                err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,));
+            }
+            _ => {
+                let span: MultiSpan = spans.into();
+                err.span_note(span, msg);
+                err.note(format!(
+                    "and more `impl`s found in the following crates: {}{}",
+                    crate_names.join(", "),
+                    post,
+                ));
+            }
+        }
+    }
+
+    /// Returns `true` if the trait predicate may apply for *some* assignment
+    /// to the type parameters.
+    fn predicate_can_apply(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool {
+        struct ParamToVarFolder<'a, 'tcx> {
+            infcx: &'a InferCtxt<'tcx>,
+            var_map: FxHashMap<Ty<'tcx>, Ty<'tcx>>,
+        }
+
+        impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ParamToVarFolder<'a, 'tcx> {
+            fn interner(&self) -> TyCtxt<'tcx> {
+                self.infcx.tcx
+            }
+
+            fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+                if let ty::Param(_) = *ty.kind() {
+                    let infcx = self.infcx;
+                    *self.var_map.entry(ty).or_insert_with(|| {
+                        infcx.next_ty_var(TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::MiscVariable,
+                            span: DUMMY_SP,
+                        })
+                    })
+                } else {
+                    ty.super_fold_with(self)
+                }
+            }
+        }
+
+        self.probe(|_| {
+            let cleaned_pred =
+                pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() });
+
+            let InferOk { value: cleaned_pred, .. } =
+                self.infcx.at(&ObligationCause::dummy(), param_env).normalize(cleaned_pred);
+
+            let obligation =
+                Obligation::new(self.tcx, ObligationCause::dummy(), param_env, cleaned_pred);
+
+            self.predicate_may_hold(&obligation)
+        })
+    }
+
+    fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>) {
+        // First, attempt to add note to this error with an async-await-specific
+        // message, and fall back to regular note otherwise.
+        if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
+            self.note_obligation_cause_code(
+                obligation.cause.body_id,
+                err,
+                obligation.predicate,
+                obligation.param_env,
+                obligation.cause.code(),
+                &mut vec![],
+                &mut Default::default(),
+            );
+            self.suggest_unsized_bound_if_applicable(err, obligation);
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn suggest_unsized_bound_if_applicable(
+        &self,
+        err: &mut Diagnostic,
+        obligation: &PredicateObligation<'tcx>,
+    ) {
+        let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
+            obligation.predicate.kind().skip_binder()
+        else {
+            return;
+        };
+        let (ObligationCauseCode::BindingObligation(item_def_id, span)
+        | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..)) =
+            *obligation.cause.code().peel_derives()
+        else {
+            return;
+        };
+        debug!(?pred, ?item_def_id, ?span);
+
+        let (Some(node), true) = (
+            self.tcx.hir().get_if_local(item_def_id),
+            Some(pred.def_id()) == self.tcx.lang_items().sized_trait(),
+        ) else {
+            return;
+        };
+        self.maybe_suggest_unsized_generics(err, span, node);
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) {
+        let Some(generics) = node.generics() else {
+            return;
+        };
+        let sized_trait = self.tcx.lang_items().sized_trait();
+        debug!(?generics.params);
+        debug!(?generics.predicates);
+        let Some(param) = generics.params.iter().find(|param| param.span == span) else {
+            return;
+        };
+        // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
+        // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
+        let explicitly_sized = generics
+            .bounds_for_param(param.def_id)
+            .flat_map(|bp| bp.bounds)
+            .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
+        if explicitly_sized {
+            return;
+        }
+        debug!(?param);
+        match node {
+            hir::Node::Item(
+                item @ hir::Item {
+                    // Only suggest indirection for uses of type parameters in ADTs.
+                    kind:
+                        hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..),
+                    ..
+                },
+            ) => {
+                if self.maybe_indirection_for_unsized(err, item, param) {
+                    return;
+                }
+            }
+            _ => {}
+        };
+        // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
+        let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param.def_id)
+        {
+            (s, " +")
+        } else {
+            (span.shrink_to_hi(), ":")
+        };
+        err.span_suggestion_verbose(
+            span,
+            "consider relaxing the implicit `Sized` restriction",
+            format!("{separator} ?Sized"),
+            Applicability::MachineApplicable,
+        );
+    }
+
+    fn maybe_indirection_for_unsized(
+        &self,
+        err: &mut Diagnostic,
+        item: &Item<'tcx>,
+        param: &GenericParam<'tcx>,
+    ) -> bool {
+        // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
+        // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
+        // is not. Look for invalid "bare" parameter uses, and suggest using indirection.
+        let mut visitor =
+            FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
+        visitor.visit_item(item);
+        if visitor.invalid_spans.is_empty() {
+            return false;
+        }
+        let mut multispan: MultiSpan = param.span.into();
+        multispan.push_span_label(
+            param.span,
+            format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
+        );
+        for sp in visitor.invalid_spans {
+            multispan.push_span_label(
+                sp,
+                format!("...if indirection were used here: `Box<{}>`", param.name.ident()),
+            );
+        }
+        err.span_help(
+            multispan,
+            format!(
+                "you could relax the implicit `Sized` bound on `{T}` if it were \
+                used through indirection like `&{T}` or `Box<{T}>`",
+                T = param.name.ident(),
+            ),
+        );
+        true
+    }
+
+    fn is_recursive_obligation(
+        &self,
+        obligated_types: &mut Vec<Ty<'tcx>>,
+        cause_code: &ObligationCauseCode<'tcx>,
+    ) -> bool {
+        if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code {
+            let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred);
+            let self_ty = parent_trait_ref.skip_binder().self_ty();
+            if obligated_types.iter().any(|ot| ot == &self_ty) {
+                return true;
+            }
+            if let ty::Adt(def, args) = self_ty.kind()
+                && let [arg] = &args[..]
+                && let ty::GenericArgKind::Type(ty) = arg.unpack()
+                && let ty::Adt(inner_def, _) = ty.kind()
+                && inner_def == def
+            {
+                return true;
+            }
+        }
+        false
+    }
+
+    fn get_standard_error_message(
+        &self,
+        trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+        message: Option<String>,
+        predicate_is_const: bool,
+        append_const_msg: Option<AppendConstMessage>,
+        post_message: String,
+    ) -> String {
+        message
+            .and_then(|cannot_do_this| {
+                match (predicate_is_const, append_const_msg) {
+                    // do nothing if predicate is not const
+                    (false, _) => Some(cannot_do_this),
+                    // suggested using default post message
+                    (true, Some(AppendConstMessage::Default)) => {
+                        Some(format!("{cannot_do_this} in const contexts"))
+                    }
+                    // overridden post message
+                    (true, Some(AppendConstMessage::Custom(custom_msg, _))) => {
+                        Some(format!("{cannot_do_this}{custom_msg}"))
+                    }
+                    // fallback to generic message
+                    (true, None) => None,
+                }
+            })
+            .unwrap_or_else(|| {
+                format!("the trait bound `{trait_predicate}` is not satisfied{post_message}")
+            })
+    }
+
+    fn get_safe_transmute_error_and_reason(
+        &self,
+        obligation: PredicateObligation<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        span: Span,
+    ) -> GetSafeTransmuteErrorAndReason {
+        use rustc_transmute::Answer;
+
+        // Erase regions because layout code doesn't particularly care about regions.
+        let trait_ref =
+            self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
+
+        let src_and_dst = rustc_transmute::Types {
+            dst: trait_ref.args.type_at(0),
+            src: trait_ref.args.type_at(1),
+        };
+        let scope = trait_ref.args.type_at(2);
+        let Some(assume) = rustc_transmute::Assume::from_const(
+            self.infcx.tcx,
+            obligation.param_env,
+            trait_ref.args.const_at(3),
+        ) else {
+            span_bug!(
+                span,
+                "Unable to construct rustc_transmute::Assume where it was previously possible"
+            );
+        };
+
+        match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
+            obligation.cause,
+            src_and_dst,
+            scope,
+            assume,
+        ) {
+            Answer::No(reason) => {
+                let dst = trait_ref.args.type_at(0);
+                let src = trait_ref.args.type_at(1);
+                let err_msg = format!(
+                    "`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`"
+                );
+                let safe_transmute_explanation = match reason {
+                    rustc_transmute::Reason::SrcIsUnspecified => {
+                        format!("`{src}` does not have a well-specified layout")
+                    }
+
+                    rustc_transmute::Reason::DstIsUnspecified => {
+                        format!("`{dst}` does not have a well-specified layout")
+                    }
+
+                    rustc_transmute::Reason::DstIsBitIncompatible => {
+                        format!("At least one value of `{src}` isn't a bit-valid value of `{dst}`")
+                    }
+
+                    rustc_transmute::Reason::DstIsPrivate => format!(
+                        "`{dst}` is or contains a type or field that is not visible in that scope"
+                    ),
+                    rustc_transmute::Reason::DstIsTooBig => {
+                        format!("The size of `{src}` is smaller than the size of `{dst}`")
+                    }
+                    rustc_transmute::Reason::SrcSizeOverflow => {
+                        format!(
+                            "values of the type `{src}` are too big for the current architecture"
+                        )
+                    }
+                    rustc_transmute::Reason::DstSizeOverflow => {
+                        format!(
+                            "values of the type `{dst}` are too big for the current architecture"
+                        )
+                    }
+                    rustc_transmute::Reason::DstHasStricterAlignment {
+                        src_min_align,
+                        dst_min_align,
+                    } => {
+                        format!(
+                            "The minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})"
+                        )
+                    }
+                    rustc_transmute::Reason::DstIsMoreUnique => {
+                        format!("`{src}` is a shared reference, but `{dst}` is a unique reference")
+                    }
+                    // Already reported by rustc
+                    rustc_transmute::Reason::TypeError => {
+                        return GetSafeTransmuteErrorAndReason::Silent;
+                    }
+                    rustc_transmute::Reason::SrcLayoutUnknown => {
+                        format!("`{src}` has an unknown layout")
+                    }
+                    rustc_transmute::Reason::DstLayoutUnknown => {
+                        format!("`{dst}` has an unknown layout")
+                    }
+                };
+                GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation }
+            }
+            // Should never get a Yes at this point! We already ran it before, and did not get a Yes.
+            Answer::Yes => span_bug!(
+                span,
+                "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
+            ),
+            other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
+        }
+    }
+
+    fn add_tuple_trait_message(
+        &self,
+        obligation_cause_code: &ObligationCauseCode<'tcx>,
+        err: &mut Diagnostic,
+    ) {
+        match obligation_cause_code {
+            ObligationCauseCode::RustCall => {
+                err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument");
+            }
+            ObligationCauseCode::BindingObligation(def_id, _)
+            | ObligationCauseCode::ItemObligation(def_id)
+                if self.tcx.is_fn_trait(*def_id) =>
+            {
+                err.code(rustc_errors::error_code!(E0059));
+                err.set_primary_message(format!(
+                    "type parameter to bare `{}` trait must be a tuple",
+                    self.tcx.def_path_str(*def_id)
+                ));
+            }
+            _ => {}
+        }
+    }
+
+    fn try_to_add_help_message(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+        err: &mut Diagnostic,
+        span: Span,
+        is_fn_trait: bool,
+        suggested: bool,
+        unsatisfied_const: bool,
+    ) {
+        let body_def_id = obligation.cause.body_id;
+        let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } =
+            obligation.cause.code()
+        {
+            *rhs_span
+        } else {
+            span
+        };
+
+        // Try to report a help message
+        if is_fn_trait
+            && let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
+                obligation.param_env,
+                trait_ref.self_ty(),
+                trait_predicate.skip_binder().polarity,
+            )
+        {
+            self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params);
+        } else if !trait_ref.has_non_region_infer()
+            && self.predicate_can_apply(obligation.param_env, *trait_predicate)
+        {
+            // If a where-clause may be useful, remind the
+            // user that they can add it.
+            //
+            // don't display an on-unimplemented note, as
+            // these notes will often be of the form
+            //     "the type `T` can't be frobnicated"
+            // which is somewhat confusing.
+            self.suggest_restricting_param_bound(
+                err,
+                *trait_predicate,
+                None,
+                obligation.cause.body_id,
+            );
+        } else if trait_ref.def_id().is_local()
+            && self.tcx.trait_impls_of(trait_ref.def_id()).is_empty()
+            && !self.tcx.trait_is_auto(trait_ref.def_id())
+            && !self.tcx.trait_is_alias(trait_ref.def_id())
+        {
+            err.span_help(
+                self.tcx.def_span(trait_ref.def_id()),
+                crate::fluent_generated::trait_selection_trait_has_no_impls,
+            );
+        } else if !suggested && !unsatisfied_const {
+            // Can't show anything else useful, try to find similar impls.
+            let impl_candidates = self.find_similar_impl_candidates(*trait_predicate);
+            if !self.report_similar_impl_candidates(
+                &impl_candidates,
+                trait_ref,
+                body_def_id,
+                err,
+                true,
+                obligation.param_env,
+            ) {
+                self.report_similar_impl_candidates_for_root_obligation(
+                    obligation,
+                    *trait_predicate,
+                    body_def_id,
+                    err,
+                );
+            }
+
+            self.suggest_convert_to_slice(
+                err,
+                obligation,
+                trait_ref,
+                impl_candidates.as_slice(),
+                span,
+            );
+        }
+    }
+
+    fn add_help_message_for_fn_trait(
+        &self,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        err: &mut Diagnostic,
+        implemented_kind: ty::ClosureKind,
+        params: ty::Binder<'tcx, Ty<'tcx>>,
+    ) {
+        // If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following
+        // suggestion to add trait bounds for the type, since we only typically implement
+        // these traits once.
+
+        // Note if the `FnMut` or `FnOnce` is less general than the trait we're trying
+        // to implement.
+        let selected_kind = self
+            .tcx
+            .fn_trait_kind_from_def_id(trait_ref.def_id())
+            .expect("expected to map DefId to ClosureKind");
+        if !implemented_kind.extends(selected_kind) {
+            err.note(format!(
+                "`{}` implements `{}`, but it must implement `{}`, which is more general",
+                trait_ref.skip_binder().self_ty(),
+                implemented_kind,
+                selected_kind
+            ));
+        }
+
+        // Note any argument mismatches
+        let given_ty = params.skip_binder();
+        let expected_ty = trait_ref.skip_binder().args.type_at(1);
+        if let ty::Tuple(given) = given_ty.kind()
+            && let ty::Tuple(expected) = expected_ty.kind()
+        {
+            if expected.len() != given.len() {
+                // Note number of types that were expected and given
+                err.note(
+                    format!(
+                        "expected a closure taking {} argument{}, but one taking {} argument{} was given",
+                        given.len(),
+                        pluralize!(given.len()),
+                        expected.len(),
+                        pluralize!(expected.len()),
+                    )
+                );
+            } else if !self.same_type_modulo_infer(given_ty, expected_ty) {
+                // Print type mismatch
+                let (expected_args, given_args) = self.cmp(given_ty, expected_ty);
+                err.note_expected_found(
+                    &"a closure with arguments",
+                    expected_args,
+                    &"a closure with arguments",
+                    given_args,
+                );
+            }
+        }
+    }
+
+    fn maybe_add_note_for_unsatisfied_const(
+        &self,
+        _obligation: &PredicateObligation<'tcx>,
+        _trait_ref: ty::PolyTraitRef<'tcx>,
+        _trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+        _err: &mut Diagnostic,
+        _span: Span,
+    ) -> UnsatisfiedConst {
+        let unsatisfied_const = UnsatisfiedConst(false);
+        // FIXME(effects)
+        unsatisfied_const
+    }
+
+    fn report_closure_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        closure_def_id: DefId,
+        found_kind: ty::ClosureKind,
+        kind: ty::ClosureKind,
+    ) -> DiagnosticBuilder<'tcx> {
+        let closure_span = self.tcx.def_span(closure_def_id);
+
+        let mut err = ClosureKindMismatch {
+            closure_span,
+            expected: kind,
+            found: found_kind,
+            cause_span: obligation.cause.span,
+            fn_once_label: None,
+            fn_mut_label: None,
+        };
+
+        // Additional context information explaining why the closure only implements
+        // a particular trait.
+        if let Some(typeck_results) = &self.typeck_results {
+            let hir_id = self.tcx.local_def_id_to_hir_id(closure_def_id.expect_local());
+            match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) {
+                (ty::ClosureKind::FnOnce, Some((span, place))) => {
+                    err.fn_once_label = Some(ClosureFnOnceLabel {
+                        span: *span,
+                        place: ty::place_to_string_for_capture(self.tcx, place),
+                    })
+                }
+                (ty::ClosureKind::FnMut, Some((span, place))) => {
+                    err.fn_mut_label = Some(ClosureFnMutLabel {
+                        span: *span,
+                        place: ty::place_to_string_for_capture(self.tcx, place),
+                    })
+                }
+                _ => {}
+            }
+        }
+
+        self.dcx().create_err(err)
+    }
+
+    fn report_type_parameter_mismatch_cyclic_type_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        terr: TypeError<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        let self_ty = found_trait_ref.self_ty().skip_binder();
+        let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() {
+            (
+                ObligationCause::dummy_with_span(self.tcx.def_span(def_id)),
+                TypeError::CyclicTy(self_ty),
+            )
+        } else {
+            (obligation.cause.clone(), terr)
+        };
+        self.report_and_explain_type_error(
+            TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref),
+            terr,
+        )
+    }
+
+    fn report_opaque_type_auto_trait_leakage(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        def_id: DefId,
+    ) -> DiagnosticBuilder<'tcx> {
+        let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
+            hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
+                "opaque type".to_string()
+            }
+            hir::OpaqueTyOrigin::TyAlias { .. } => {
+                format!("`{}`", self.tcx.def_path_debug_str(def_id))
+            }
+        };
+        let mut err = self.dcx().struct_span_err(
+            obligation.cause.span,
+            format!("cannot check whether the hidden type of {name} satisfies auto traits"),
+        );
+        err.span_note(self.tcx.def_span(def_id), "opaque type is declared here");
+        match self.defining_use_anchor {
+            DefiningAnchor::Bubble | DefiningAnchor::Error => {}
+            DefiningAnchor::Bind(bind) => {
+                err.span_note(
+                    self.tcx.def_ident_span(bind).unwrap_or_else(|| self.tcx.def_span(bind)),
+                    "this item depends on auto traits of the hidden type, \
+                    but may also be registering the hidden type. \
+                    This is not supported right now. \
+                    You can try moving the opaque type and the item that actually registers a hidden type into a new submodule".to_string(),
+                );
+            }
+        };
+
+        if let Some(diag) = self.dcx().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle)
+        {
+            diag.cancel();
+        }
+
+        err
+    }
+
+    fn report_type_parameter_mismatch_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        span: Span,
+        found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+    ) -> Option<DiagnosticBuilder<'tcx>> {
+        let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref);
+        let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref);
+
+        if expected_trait_ref.self_ty().references_error() {
+            return None;
+        }
+
+        let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else {
+            return None;
+        };
+
+        let found_did = match *found_trait_ty.kind() {
+            ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) | ty::Coroutine(did, ..) => {
+                Some(did)
+            }
+            ty::Adt(def, _) => Some(def.did()),
+            _ => None,
+        };
+
+        let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did));
+        let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did));
+
+        if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) {
+            // We check closures twice, with obligations flowing in different directions,
+            // but we want to complain about them only once.
+            return None;
+        }
+
+        self.reported_closure_mismatch.borrow_mut().insert((span, found_span));
+
+        let mut not_tupled = false;
+
+        let found = match found_trait_ref.skip_binder().args.type_at(1).kind() {
+            ty::Tuple(tys) => vec![ArgKind::empty(); tys.len()],
+            _ => {
+                not_tupled = true;
+                vec![ArgKind::empty()]
+            }
+        };
+
+        let expected_ty = expected_trait_ref.skip_binder().args.type_at(1);
+        let expected = match expected_ty.kind() {
+            ty::Tuple(tys) => {
+                tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect()
+            }
+            _ => {
+                not_tupled = true;
+                vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())]
+            }
+        };
+
+        // If this is a `Fn` family trait and either the expected or found
+        // is not tupled, then fall back to just a regular mismatch error.
+        // This shouldn't be common unless manually implementing one of the
+        // traits manually, but don't make it more confusing when it does
+        // happen.
+        Some(
+            if Some(expected_trait_ref.def_id()) != self.tcx.lang_items().coroutine_trait()
+                && not_tupled
+            {
+                self.report_and_explain_type_error(
+                    TypeTrace::poly_trait_refs(
+                        &obligation.cause,
+                        true,
+                        expected_trait_ref,
+                        found_trait_ref,
+                    ),
+                    ty::error::TypeError::Mismatch,
+                )
+            } else if found.len() == expected.len() {
+                self.report_closure_arg_mismatch(
+                    span,
+                    found_span,
+                    found_trait_ref,
+                    expected_trait_ref,
+                    obligation.cause.code(),
+                    found_node,
+                    obligation.param_env,
+                )
+            } else {
+                let (closure_span, closure_arg_span, found) = found_did
+                    .and_then(|did| {
+                        let node = self.tcx.hir().get_if_local(did)?;
+                        let (found_span, closure_arg_span, found) =
+                            self.get_fn_like_arguments(node)?;
+                        Some((Some(found_span), closure_arg_span, found))
+                    })
+                    .unwrap_or((found_span, None, found));
+
+                self.report_arg_count_mismatch(
+                    span,
+                    closure_span,
+                    expected,
+                    found,
+                    found_trait_ty.is_closure(),
+                    closure_arg_span,
+                )
+            },
+        )
+    }
+
+    fn report_not_const_evaluatable_error(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        span: Span,
+    ) -> Option<DiagnosticBuilder<'tcx>> {
+        if !self.tcx.features().generic_const_exprs {
+            let mut err = self
+                .dcx()
+                .struct_span_err(span, "constant expression depends on a generic parameter");
+            // FIXME(const_generics): we should suggest to the user how they can resolve this
+            // issue. However, this is currently not actually possible
+            // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
+            //
+            // Note that with `feature(generic_const_exprs)` this case should not
+            // be reachable.
+            err.note("this may fail depending on what value the parameter takes");
+            err.emit();
+            return None;
+        }
+
+        match obligation.predicate.kind().skip_binder() {
+            ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() {
+                ty::ConstKind::Unevaluated(uv) => {
+                    let mut err =
+                        self.dcx().struct_span_err(span, "unconstrained generic constant");
+                    let const_span = self.tcx.def_span(uv.def);
+                    match self.tcx.sess.source_map().span_to_snippet(const_span) {
+                            Ok(snippet) => err.help(format!(
+                                "try adding a `where` bound using this expression: `where [(); {snippet}]:`"
+                            )),
+                            _ => err.help("consider adding a `where` bound using this expression"),
+                        };
+                    Some(err)
+                }
+                ty::ConstKind::Expr(_) => {
+                    let err = self
+                        .dcx()
+                        .struct_span_err(span, format!("unconstrained generic constant `{ct}`"));
+                    Some(err)
+                }
+                _ => {
+                    bug!("const evaluatable failed for non-unevaluated const `{ct:?}`");
+                }
+            },
+            _ => {
+                span_bug!(
+                    span,
+                    "unexpected non-ConstEvaluatable predicate, this should not be reachable"
+                )
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
new file mode 100644
index 00000000000..045d7e444b6
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -0,0 +1,826 @@
+use crate::infer::{InferCtxt, TyOrConstInferVar};
+use crate::traits::error_reporting::TypeErrCtxtExt;
+use rustc_data_structures::captures::Captures;
+use rustc_data_structures::obligation_forest::ProcessResult;
+use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
+use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
+use rustc_infer::infer::DefineOpaqueTypes;
+use rustc_infer::traits::ProjectionCacheKey;
+use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
+use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::ty::abstract_const::NotConstEvaluatable;
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::GenericArgsRef;
+use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt};
+use std::marker::PhantomData;
+
+use super::const_evaluatable;
+use super::project::{self, ProjectAndUnifyResult};
+use super::select::SelectionContext;
+use super::wf;
+use super::CodeAmbiguity;
+use super::CodeProjectionError;
+use super::CodeSelectionError;
+use super::EvaluationResult;
+use super::PredicateObligation;
+use super::Unimplemented;
+use super::{FulfillmentError, FulfillmentErrorCode};
+
+use crate::traits::project::PolyProjectionObligation;
+use crate::traits::project::ProjectionCacheKeyExt as _;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+
+impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
+    /// Note that we include both the `ParamEnv` and the `Predicate`,
+    /// as the `ParamEnv` can influence whether fulfillment succeeds
+    /// or fails.
+    type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>;
+
+    fn as_cache_key(&self) -> Self::CacheKey {
+        self.obligation.param_env.and(self.obligation.predicate)
+    }
+}
+
+/// The fulfillment context is used to drive trait resolution. It
+/// consists of a list of obligations that must be (eventually)
+/// satisfied. The job is to track which are satisfied, which yielded
+/// errors, and which are still pending. At any point, users can call
+/// `select_where_possible`, and the fulfillment context will try to do
+/// selection, retaining only those obligations that remain
+/// ambiguous. This may be helpful in pushing type inference
+/// along. Once all type inference constraints have been generated, the
+/// method `select_all_or_error` can be used to report any remaining
+/// ambiguous cases as errors.
+pub struct FulfillmentContext<'tcx> {
+    /// A list of all obligations that have been registered with this
+    /// fulfillment context.
+    predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
+
+    /// The snapshot in which this context was created. Using the context
+    /// outside of this snapshot leads to subtle bugs if the snapshot
+    /// gets rolled back. Because of this we explicitly check that we only
+    /// use the context in exactly this snapshot.
+    usable_in_snapshot: usize,
+}
+
+#[derive(Clone, Debug)]
+pub struct PendingPredicateObligation<'tcx> {
+    pub obligation: PredicateObligation<'tcx>,
+    // This is far more often read than modified, meaning that we
+    // should mostly optimize for reading speed, while modifying is not as relevant.
+    //
+    // For whatever reason using a boxed slice is slower than using a `Vec` here.
+    pub stalled_on: Vec<TyOrConstInferVar>,
+}
+
+// `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+static_assert_size!(PendingPredicateObligation<'_>, 72);
+
+impl<'tcx> FulfillmentContext<'tcx> {
+    /// Creates a new fulfillment context.
+    pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx> {
+        FulfillmentContext {
+            predicates: ObligationForest::new(),
+            usable_in_snapshot: infcx.num_open_snapshots(),
+        }
+    }
+
+    /// Attempts to select obligations using `selcx`.
+    fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> {
+        let span = debug_span!("select", obligation_forest_size = ?self.predicates.len());
+        let _enter = span.enter();
+
+        // Process pending obligations.
+        let outcome: Outcome<_, _> =
+            self.predicates.process_obligations(&mut FulfillProcessor { selcx });
+
+        // FIXME: if we kept the original cache key, we could mark projection
+        // obligations as complete for the projection cache here.
+
+        let errors: Vec<FulfillmentError<'tcx>> =
+            outcome.errors.into_iter().map(to_fulfillment_error).collect();
+
+        debug!(
+            "select({} predicates remaining, {} errors) done",
+            self.predicates.len(),
+            errors.len()
+        );
+
+        errors
+    }
+}
+
+impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
+    #[inline]
+    fn register_predicate_obligation(
+        &mut self,
+        infcx: &InferCtxt<'tcx>,
+        mut obligation: PredicateObligation<'tcx>,
+    ) {
+        assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
+        // this helps to reduce duplicate errors, as well as making
+        // debug output much nicer to read and so on.
+        debug_assert!(!obligation.param_env.has_non_region_infer());
+        obligation.predicate = infcx.resolve_vars_if_possible(obligation.predicate);
+
+        debug!(?obligation, "register_predicate_obligation");
+
+        self.predicates
+            .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
+    }
+
+    fn collect_remaining_errors(
+        &mut self,
+        _infcx: &InferCtxt<'tcx>,
+    ) -> Vec<FulfillmentError<'tcx>> {
+        self.predicates
+            .to_errors(CodeAmbiguity { overflow: false })
+            .into_iter()
+            .map(to_fulfillment_error)
+            .collect()
+    }
+
+    fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
+        let selcx = SelectionContext::new(infcx);
+        self.select(selcx)
+    }
+
+    fn drain_unstalled_obligations(
+        &mut self,
+        infcx: &InferCtxt<'tcx>,
+    ) -> Vec<PredicateObligation<'tcx>> {
+        let mut processor = DrainProcessor { removed_predicates: Vec::new(), infcx };
+        let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut processor);
+        assert!(outcome.errors.is_empty());
+        return processor.removed_predicates;
+
+        struct DrainProcessor<'a, 'tcx> {
+            infcx: &'a InferCtxt<'tcx>,
+            removed_predicates: Vec<PredicateObligation<'tcx>>,
+        }
+
+        impl<'tcx> ObligationProcessor for DrainProcessor<'_, 'tcx> {
+            type Obligation = PendingPredicateObligation<'tcx>;
+            type Error = !;
+            type OUT = Outcome<Self::Obligation, Self::Error>;
+
+            fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool {
+                pending_obligation
+                    .stalled_on
+                    .iter()
+                    .any(|&var| self.infcx.ty_or_const_infer_var_changed(var))
+            }
+
+            fn process_obligation(
+                &mut self,
+                pending_obligation: &mut PendingPredicateObligation<'tcx>,
+            ) -> ProcessResult<PendingPredicateObligation<'tcx>, !> {
+                assert!(self.needs_process_obligation(pending_obligation));
+                self.removed_predicates.push(pending_obligation.obligation.clone());
+                ProcessResult::Changed(vec![])
+            }
+
+            fn process_backedge<'c, I>(
+                &mut self,
+                cycle: I,
+                _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
+            ) -> Result<(), !>
+            where
+                I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
+            {
+                self.removed_predicates.extend(cycle.map(|c| c.obligation.clone()));
+                Ok(())
+            }
+        }
+    }
+
+    fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
+        self.predicates.map_pending_obligations(|o| o.obligation.clone())
+    }
+}
+
+struct FulfillProcessor<'a, 'tcx> {
+    selcx: SelectionContext<'a, 'tcx>,
+}
+
+fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligation<'_>> {
+    os.into_iter()
+        .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] })
+        .collect()
+}
+
+impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
+    type Obligation = PendingPredicateObligation<'tcx>;
+    type Error = FulfillmentErrorCode<'tcx>;
+    type OUT = Outcome<Self::Obligation, Self::Error>;
+
+    /// Compared to `needs_process_obligation` this and its callees
+    /// contain some optimizations that come at the price of false negatives.
+    ///
+    /// They
+    /// - reduce branching by covering only the most common case
+    /// - take a read-only view of the unification tables which allows skipping undo_log
+    ///   construction.
+    /// - bail out on value-cache misses in ena to avoid pointer chasing
+    /// - hoist RefCell locking out of the loop
+    #[inline]
+    fn skippable_obligations<'b>(
+        &'b self,
+        it: impl Iterator<Item = &'b Self::Obligation>,
+    ) -> usize {
+        let is_unchanged = self.selcx.infcx.is_ty_infer_var_definitely_unchanged();
+
+        it.take_while(|o| match o.stalled_on.as_slice() {
+            [o] => is_unchanged(*o),
+            _ => false,
+        })
+        .count()
+    }
+
+    /// Identifies whether a predicate obligation needs processing.
+    ///
+    /// This is always inlined because it has a single callsite and it is
+    /// called *very* frequently. Be careful modifying this code! Several
+    /// compile-time benchmarks are very sensitive to even small changes.
+    #[inline(always)]
+    fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool {
+        // If we were stalled on some unresolved variables, first check whether
+        // any of them have been resolved; if not, don't bother doing more work
+        // yet.
+        let stalled_on = &pending_obligation.stalled_on;
+        match stalled_on.len() {
+            // This case is the hottest most of the time, being hit up to 99%
+            // of the time. `keccak` and `cranelift-codegen-0.82.1` are
+            // benchmarks that particularly stress this path.
+            1 => self.selcx.infcx.ty_or_const_infer_var_changed(stalled_on[0]),
+
+            // In this case we haven't changed, but wish to make a change. Note
+            // that this is a special case, and is not equivalent to the `_`
+            // case below, which would return `false` for an empty `stalled_on`
+            // vector.
+            //
+            // This case is usually hit only 1% of the time or less, though it
+            // reaches 20% in `wasmparser-0.101.0`.
+            0 => true,
+
+            // This case is usually hit only 1% of the time or less, though it
+            // reaches 95% in `mime-0.3.16`, 64% in `wast-54.0.0`, and 12% in
+            // `inflate-0.4.5`.
+            //
+            // The obvious way of writing this, with a call to `any()` and no
+            // closure, is currently slower than this version.
+            _ => (|| {
+                for &infer_var in stalled_on {
+                    if self.selcx.infcx.ty_or_const_infer_var_changed(infer_var) {
+                        return true;
+                    }
+                }
+                false
+            })(),
+        }
+    }
+
+    /// Processes a predicate obligation and returns either:
+    /// - `Changed(v)` if the predicate is true, presuming that `v` are also true
+    /// - `Unchanged` if we don't have enough info to be sure
+    /// - `Error(e)` if the predicate does not hold
+    ///
+    /// This is called much less often than `needs_process_obligation`, so we
+    /// never inline it.
+    #[inline(never)]
+    #[instrument(level = "debug", skip(self, pending_obligation))]
+    fn process_obligation(
+        &mut self,
+        pending_obligation: &mut PendingPredicateObligation<'tcx>,
+    ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
+        pending_obligation.stalled_on.truncate(0);
+
+        let obligation = &mut pending_obligation.obligation;
+
+        debug!(?obligation, "pre-resolve");
+
+        if obligation.predicate.has_non_region_infer() {
+            obligation.predicate = self.selcx.infcx.resolve_vars_if_possible(obligation.predicate);
+        }
+
+        let obligation = &pending_obligation.obligation;
+
+        let infcx = self.selcx.infcx;
+
+        if obligation.predicate.has_projections() {
+            let mut obligations = Vec::new();
+            let predicate = crate::traits::project::try_normalize_with_depth_to(
+                &mut self.selcx,
+                obligation.param_env,
+                obligation.cause.clone(),
+                obligation.recursion_depth + 1,
+                obligation.predicate,
+                &mut obligations,
+            );
+            if predicate != obligation.predicate {
+                obligations.push(obligation.with(infcx.tcx, predicate));
+                return ProcessResult::Changed(mk_pending(obligations));
+            }
+        }
+        let binder = obligation.predicate.kind();
+        match binder.no_bound_vars() {
+            None => match binder.skip_binder() {
+                // Evaluation will discard candidates using the leak check.
+                // This means we need to pass it the bound version of our
+                // predicate.
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) => {
+                    let trait_obligation = obligation.with(infcx.tcx, binder.rebind(trait_ref));
+
+                    self.process_trait_obligation(
+                        obligation,
+                        trait_obligation,
+                        &mut pending_obligation.stalled_on,
+                    )
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
+                    let project_obligation = obligation.with(infcx.tcx, binder.rebind(data));
+
+                    self.process_projection_obligation(
+                        obligation,
+                        project_obligation,
+                        &mut pending_obligation.stalled_on,
+                    )
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(_))
+                | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_))
+                | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
+                | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_))
+                | ty::PredicateKind::ObjectSafe(_)
+                | ty::PredicateKind::Subtype(_)
+                | ty::PredicateKind::Coerce(_)
+                | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
+                | ty::PredicateKind::ConstEquate(..) => {
+                    let pred =
+                        ty::Binder::dummy(infcx.instantiate_binder_with_placeholders(binder));
+                    ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)]))
+                }
+                ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
+                ty::PredicateKind::NormalizesTo(..) => {
+                    bug!("NormalizesTo is only used by the new solver")
+                }
+                ty::PredicateKind::AliasRelate(..) => {
+                    bug!("AliasRelate is only used by the new solver")
+                }
+            },
+            Some(pred) => match pred {
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
+                    let trait_obligation = obligation.with(infcx.tcx, Binder::dummy(data));
+
+                    self.process_trait_obligation(
+                        obligation,
+                        trait_obligation,
+                        &mut pending_obligation.stalled_on,
+                    )
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => {
+                    if infcx.considering_regions {
+                        infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data));
+                    }
+
+                    ProcessResult::Changed(vec![])
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
+                    t_a,
+                    r_b,
+                ))) => {
+                    if infcx.considering_regions {
+                        infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause);
+                    }
+                    ProcessResult::Changed(vec![])
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::Projection(ref data)) => {
+                    let project_obligation = obligation.with(infcx.tcx, Binder::dummy(*data));
+
+                    self.process_projection_obligation(
+                        obligation,
+                        project_obligation,
+                        &mut pending_obligation.stalled_on,
+                    )
+                }
+
+                ty::PredicateKind::ObjectSafe(trait_def_id) => {
+                    if !self.selcx.tcx().check_is_object_safe(trait_def_id) {
+                        ProcessResult::Error(CodeSelectionError(Unimplemented))
+                    } else {
+                        ProcessResult::Changed(vec![])
+                    }
+                }
+
+                ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
+                ty::PredicateKind::NormalizesTo(..) => {
+                    bug!("NormalizesTo is only used by the new solver")
+                }
+                ty::PredicateKind::AliasRelate(..) => {
+                    bug!("AliasRelate is only used by the new solver")
+                }
+
+                // General case overflow check. Allow `process_trait_obligation`
+                // and `process_projection_obligation` to handle checking for
+                // the recursion limit themselves. Also don't check some
+                // predicate kinds that don't give further obligations.
+                _ if !self
+                    .selcx
+                    .tcx()
+                    .recursion_limit()
+                    .value_within_limit(obligation.recursion_depth) =>
+                {
+                    self.selcx.infcx.err_ctxt().report_overflow_error(
+                        &obligation.predicate,
+                        obligation.cause.span,
+                        false,
+                        |_| {},
+                    );
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+                    match wf::obligations(
+                        self.selcx.infcx,
+                        obligation.param_env,
+                        obligation.cause.body_id,
+                        obligation.recursion_depth + 1,
+                        arg,
+                        obligation.cause.span,
+                    ) {
+                        None => {
+                            pending_obligation.stalled_on =
+                                vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()];
+                            ProcessResult::Unchanged
+                        }
+                        Some(os) => ProcessResult::Changed(mk_pending(os)),
+                    }
+                }
+
+                ty::PredicateKind::Subtype(subtype) => {
+                    match self.selcx.infcx.subtype_predicate(
+                        &obligation.cause,
+                        obligation.param_env,
+                        Binder::dummy(subtype),
+                    ) {
+                        Err((a, b)) => {
+                            // None means that both are unresolved.
+                            pending_obligation.stalled_on =
+                                vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)];
+                            ProcessResult::Unchanged
+                        }
+                        Ok(Ok(mut ok)) => {
+                            for subobligation in &mut ok.obligations {
+                                subobligation.set_depth_from_parent(obligation.recursion_depth);
+                            }
+                            ProcessResult::Changed(mk_pending(ok.obligations))
+                        }
+                        Ok(Err(err)) => {
+                            let expected_found =
+                                ExpectedFound::new(subtype.a_is_expected, subtype.a, subtype.b);
+                            ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError(
+                                expected_found,
+                                err,
+                            ))
+                        }
+                    }
+                }
+
+                ty::PredicateKind::Coerce(coerce) => {
+                    match self.selcx.infcx.coerce_predicate(
+                        &obligation.cause,
+                        obligation.param_env,
+                        Binder::dummy(coerce),
+                    ) {
+                        Err((a, b)) => {
+                            // None means that both are unresolved.
+                            pending_obligation.stalled_on =
+                                vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)];
+                            ProcessResult::Unchanged
+                        }
+                        Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)),
+                        Ok(Err(err)) => {
+                            let expected_found = ExpectedFound::new(false, coerce.a, coerce.b);
+                            ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError(
+                                expected_found,
+                                err,
+                            ))
+                        }
+                    }
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => {
+                    match const_evaluatable::is_const_evaluatable(
+                        self.selcx.infcx,
+                        uv,
+                        obligation.param_env,
+                        obligation.cause.span,
+                    ) {
+                        Ok(()) => ProcessResult::Changed(vec![]),
+                        Err(NotConstEvaluatable::MentionsInfer) => {
+                            pending_obligation.stalled_on.clear();
+                            pending_obligation.stalled_on.extend(
+                                uv.walk().filter_map(TyOrConstInferVar::maybe_from_generic_arg),
+                            );
+                            ProcessResult::Unchanged
+                        }
+                        Err(
+                            e @ NotConstEvaluatable::MentionsParam
+                            | e @ NotConstEvaluatable::Error(_),
+                        ) => ProcessResult::Error(CodeSelectionError(
+                            SelectionError::NotConstEvaluatable(e),
+                        )),
+                    }
+                }
+
+                ty::PredicateKind::ConstEquate(c1, c2) => {
+                    let tcx = self.selcx.tcx();
+                    assert!(
+                        tcx.features().generic_const_exprs,
+                        "`ConstEquate` without a feature gate: {c1:?} {c2:?}",
+                    );
+                    // FIXME: we probably should only try to unify abstract constants
+                    // if the constants depend on generic parameters.
+                    //
+                    // Let's just see where this breaks :shrug:
+                    {
+                        let c1 = tcx.expand_abstract_consts(c1);
+                        let c2 = tcx.expand_abstract_consts(c2);
+                        debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2);
+
+                        use rustc_hir::def::DefKind;
+                        use ty::Unevaluated;
+                        match (c1.kind(), c2.kind()) {
+                            (Unevaluated(a), Unevaluated(b))
+                                if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
+                            {
+                                if let Ok(new_obligations) = infcx
+                                    .at(&obligation.cause, obligation.param_env)
+                                    .trace(c1, c2)
+                                    .eq(DefineOpaqueTypes::No, a.args, b.args)
+                                {
+                                    return ProcessResult::Changed(mk_pending(
+                                        new_obligations.into_obligations(),
+                                    ));
+                                }
+                            }
+                            (_, Unevaluated(_)) | (Unevaluated(_), _) => (),
+                            (_, _) => {
+                                if let Ok(new_obligations) = infcx
+                                    .at(&obligation.cause, obligation.param_env)
+                                    .eq(DefineOpaqueTypes::No, c1, c2)
+                                {
+                                    return ProcessResult::Changed(mk_pending(
+                                        new_obligations.into_obligations(),
+                                    ));
+                                }
+                            }
+                        }
+                    }
+
+                    let stalled_on = &mut pending_obligation.stalled_on;
+
+                    let mut evaluate = |c: Const<'tcx>| {
+                        if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() {
+                            match self.selcx.infcx.try_const_eval_resolve(
+                                obligation.param_env,
+                                unevaluated,
+                                c.ty(),
+                                Some(obligation.cause.span),
+                            ) {
+                                Ok(val) => Ok(val),
+                                Err(e) => {
+                                    match e {
+                                        ErrorHandled::TooGeneric(..) => {
+                                            stalled_on.extend(unevaluated.args.iter().filter_map(
+                                                TyOrConstInferVar::maybe_from_generic_arg,
+                                            ));
+                                        }
+                                        _ => {}
+                                    }
+                                    Err(e)
+                                }
+                            }
+                        } else {
+                            Ok(c)
+                        }
+                    };
+
+                    match (evaluate(c1), evaluate(c2)) {
+                        (Ok(c1), Ok(c2)) => {
+                            match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq(
+                                DefineOpaqueTypes::No,
+                                c1,
+                                c2,
+                            ) {
+                                Ok(inf_ok) => {
+                                    ProcessResult::Changed(mk_pending(inf_ok.into_obligations()))
+                                }
+                                Err(err) => ProcessResult::Error(
+                                    FulfillmentErrorCode::CodeConstEquateError(
+                                        ExpectedFound::new(true, c1, c2),
+                                        err,
+                                    ),
+                                ),
+                            }
+                        }
+                        (Err(ErrorHandled::Reported(reported, _)), _)
+                        | (_, Err(ErrorHandled::Reported(reported, _))) => ProcessResult::Error(
+                            CodeSelectionError(SelectionError::NotConstEvaluatable(
+                                NotConstEvaluatable::Error(reported.into()),
+                            )),
+                        ),
+                        (Err(ErrorHandled::TooGeneric(_)), _)
+                        | (_, Err(ErrorHandled::TooGeneric(_))) => {
+                            if c1.has_non_region_infer() || c2.has_non_region_infer() {
+                                ProcessResult::Unchanged
+                            } else {
+                                // Two different constants using generic parameters ~> error.
+                                let expected_found = ExpectedFound::new(true, c1, c2);
+                                ProcessResult::Error(FulfillmentErrorCode::CodeConstEquateError(
+                                    expected_found,
+                                    TypeError::ConstMismatch(expected_found),
+                                ))
+                            }
+                        }
+                    }
+                }
+                ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
+                    match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq(
+                        DefineOpaqueTypes::No,
+                        ct.ty(),
+                        ty,
+                    ) {
+                        Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())),
+                        Err(_) => ProcessResult::Error(FulfillmentErrorCode::CodeSelectionError(
+                            SelectionError::Unimplemented,
+                        )),
+                    }
+                }
+            },
+        }
+    }
+
+    #[inline(never)]
+    fn process_backedge<'c, I>(
+        &mut self,
+        cycle: I,
+        _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
+    ) -> Result<(), FulfillmentErrorCode<'tcx>>
+    where
+        I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
+    {
+        if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
+            debug!("process_child_obligations: coinductive match");
+            Ok(())
+        } else {
+            let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
+            Err(FulfillmentErrorCode::CodeCycle(cycle))
+        }
+    }
+}
+
+impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
+    #[instrument(level = "debug", skip(self, obligation, stalled_on))]
+    fn process_trait_obligation(
+        &mut self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_obligation: PolyTraitObligation<'tcx>,
+        stalled_on: &mut Vec<TyOrConstInferVar>,
+    ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
+        let infcx = self.selcx.infcx;
+        if obligation.predicate.is_global() && !self.selcx.is_intercrate() {
+            // no type variables present, can use evaluation for better caching.
+            // FIXME: consider caching errors too.
+            if infcx.predicate_must_hold_considering_regions(obligation) {
+                debug!(
+                    "selecting trait at depth {} evaluated to holds",
+                    obligation.recursion_depth
+                );
+                return ProcessResult::Changed(vec![]);
+            }
+        }
+
+        match self.selcx.poly_select(&trait_obligation) {
+            Ok(Some(impl_source)) => {
+                debug!("selecting trait at depth {} yielded Ok(Some)", obligation.recursion_depth);
+                ProcessResult::Changed(mk_pending(impl_source.nested_obligations()))
+            }
+            Ok(None) => {
+                debug!("selecting trait at depth {} yielded Ok(None)", obligation.recursion_depth);
+
+                // This is a bit subtle: for the most part, the
+                // only reason we can fail to make progress on
+                // trait selection is because we don't have enough
+                // information about the types in the trait.
+                stalled_on.clear();
+                stalled_on.extend(args_infer_vars(
+                    &self.selcx,
+                    trait_obligation.predicate.map_bound(|pred| pred.trait_ref.args),
+                ));
+
+                debug!(
+                    "process_predicate: pending obligation {:?} now stalled on {:?}",
+                    infcx.resolve_vars_if_possible(obligation.clone()),
+                    stalled_on
+                );
+
+                ProcessResult::Unchanged
+            }
+            Err(selection_err) => {
+                debug!("selecting trait at depth {} yielded Err", obligation.recursion_depth);
+
+                ProcessResult::Error(CodeSelectionError(selection_err))
+            }
+        }
+    }
+
+    fn process_projection_obligation(
+        &mut self,
+        obligation: &PredicateObligation<'tcx>,
+        project_obligation: PolyProjectionObligation<'tcx>,
+        stalled_on: &mut Vec<TyOrConstInferVar>,
+    ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
+        let tcx = self.selcx.tcx();
+
+        if obligation.predicate.is_global() && !self.selcx.is_intercrate() {
+            // no type variables present, can use evaluation for better caching.
+            // FIXME: consider caching errors too.
+            if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) {
+                if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate(
+                    &mut self.selcx,
+                    project_obligation.predicate,
+                ) {
+                    // If `predicate_must_hold_considering_regions` succeeds, then we've
+                    // evaluated all sub-obligations. We can therefore mark the 'root'
+                    // obligation as complete, and skip evaluating sub-obligations.
+                    self.selcx
+                        .infcx
+                        .inner
+                        .borrow_mut()
+                        .projection_cache()
+                        .complete(key, EvaluationResult::EvaluatedToOk);
+                }
+                return ProcessResult::Changed(vec![]);
+            } else {
+                debug!("Does NOT hold: {:?}", obligation);
+            }
+        }
+
+        match project::poly_project_and_unify_type(&mut self.selcx, &project_obligation) {
+            ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)),
+            ProjectAndUnifyResult::FailedNormalization => {
+                stalled_on.clear();
+                stalled_on.extend(args_infer_vars(
+                    &self.selcx,
+                    project_obligation.predicate.map_bound(|pred| pred.projection_ty.args),
+                ));
+                ProcessResult::Unchanged
+            }
+            // Let the caller handle the recursion
+            ProjectAndUnifyResult::Recursive => ProcessResult::Changed(mk_pending(vec![
+                project_obligation.with(tcx, project_obligation.predicate),
+            ])),
+            ProjectAndUnifyResult::MismatchedProjectionTypes(e) => {
+                ProcessResult::Error(CodeProjectionError(e))
+            }
+        }
+    }
+}
+
+/// Returns the set of inference variables contained in `args`.
+fn args_infer_vars<'a, 'tcx>(
+    selcx: &SelectionContext<'a, 'tcx>,
+    args: ty::Binder<'tcx, GenericArgsRef<'tcx>>,
+) -> impl Iterator<Item = TyOrConstInferVar> + Captures<'tcx> {
+    selcx
+        .infcx
+        .resolve_vars_if_possible(args)
+        .skip_binder() // ok because this check doesn't care about regions
+        .iter()
+        .filter(|arg| arg.has_non_region_infer())
+        .flat_map(|arg| {
+            let mut walker = arg.walk();
+            while let Some(c) = walker.next() {
+                if !c.has_non_region_infer() {
+                    walker.visited.remove(&c);
+                    walker.skip_current_subtree();
+                }
+            }
+            walker.visited.into_iter()
+        })
+        .filter_map(TyOrConstInferVar::maybe_from_generic_arg)
+}
+
+fn to_fulfillment_error<'tcx>(
+    error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>,
+) -> FulfillmentError<'tcx> {
+    let mut iter = error.backtrace.into_iter();
+    let obligation = iter.next().unwrap().obligation;
+    // The root obligation is the last item in the backtrace - if there's only
+    // one item, then it's the same as the main obligation
+    let root_obligation = iter.next_back().map_or_else(|| obligation.clone(), |e| e.obligation);
+    FulfillmentError::new(obligation, error.error, root_obligation)
+}
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
new file mode 100644
index 00000000000..073174127d6
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -0,0 +1,224 @@
+//! Miscellaneous type-system utilities that are too small to deserve their own modules.
+
+use crate::traits::{self, ObligationCause, ObligationCtxt};
+
+use hir::LangItem;
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_hir as hir;
+use rustc_infer::infer::canonical::Canonical;
+use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
+use rustc_infer::traits::query::NoSolution;
+use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
+use rustc_middle::ty::{self, AdtDef, GenericArg, List, Ty, TyCtxt, TypeVisitableExt};
+use rustc_span::DUMMY_SP;
+
+use super::outlives_bounds::InferCtxtExt;
+
+pub enum CopyImplementationError<'tcx> {
+    InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
+    NotAnAdt,
+    HasDestructor,
+}
+
+pub enum ConstParamTyImplementationError<'tcx> {
+    InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
+    NotAnAdtOrBuiltinAllowed,
+}
+
+pub enum InfringingFieldsReason<'tcx> {
+    Fulfill(Vec<FulfillmentError<'tcx>>),
+    Regions(Vec<RegionResolutionError<'tcx>>),
+}
+
+/// Checks that the fields of the type (an ADT) all implement copy.
+///
+/// If fields don't implement copy, return an error containing a list of
+/// those violating fields.
+///
+/// If it's not an ADT, int ty, `bool`, float ty, `char`, raw pointer, `!`,
+/// a reference or an array returns `Err(NotAnAdt)`.
+pub fn type_allowed_to_implement_copy<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    self_type: Ty<'tcx>,
+    parent_cause: ObligationCause<'tcx>,
+) -> Result<(), CopyImplementationError<'tcx>> {
+    let (adt, args) = match self_type.kind() {
+        // These types used to have a builtin impl.
+        // Now libcore provides that impl.
+        ty::Uint(_)
+        | ty::Int(_)
+        | ty::Bool
+        | ty::Float(_)
+        | ty::Char
+        | ty::RawPtr(..)
+        | ty::Never
+        | ty::Ref(_, _, hir::Mutability::Not)
+        | ty::Array(..) => return Ok(()),
+
+        &ty::Adt(adt, args) => (adt, args),
+
+        _ => return Err(CopyImplementationError::NotAnAdt),
+    };
+
+    all_fields_implement_trait(
+        tcx,
+        param_env,
+        self_type,
+        adt,
+        args,
+        parent_cause,
+        hir::LangItem::Copy,
+    )
+    .map_err(CopyImplementationError::InfringingFields)?;
+
+    if adt.has_dtor(tcx) {
+        return Err(CopyImplementationError::HasDestructor);
+    }
+
+    Ok(())
+}
+
+/// Checks that the fields of the type (an ADT) all implement `ConstParamTy`.
+///
+/// If fields don't implement `ConstParamTy`, return an error containing a list of
+/// those violating fields.
+///
+/// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`.
+pub fn type_allowed_to_implement_const_param_ty<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    self_type: Ty<'tcx>,
+    parent_cause: ObligationCause<'tcx>,
+) -> Result<(), ConstParamTyImplementationError<'tcx>> {
+    let (adt, args) = match self_type.kind() {
+        // `core` provides these impls.
+        ty::Uint(_)
+        | ty::Int(_)
+        | ty::Bool
+        | ty::Char
+        | ty::Str
+        | ty::Array(..)
+        | ty::Slice(_)
+        | ty::Ref(.., hir::Mutability::Not)
+        | ty::Tuple(_) => return Ok(()),
+
+        &ty::Adt(adt, args) => (adt, args),
+
+        _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
+    };
+
+    all_fields_implement_trait(
+        tcx,
+        param_env,
+        self_type,
+        adt,
+        args,
+        parent_cause,
+        hir::LangItem::ConstParamTy,
+    )
+    .map_err(ConstParamTyImplementationError::InfrigingFields)?;
+
+    Ok(())
+}
+
+/// Check that all fields of a given `adt` implement `lang_item` trait.
+pub fn all_fields_implement_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    self_type: Ty<'tcx>,
+    adt: AdtDef<'tcx>,
+    args: &'tcx List<GenericArg<'tcx>>,
+    parent_cause: ObligationCause<'tcx>,
+    lang_item: LangItem,
+) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>> {
+    let trait_def_id = tcx.require_lang_item(lang_item, Some(parent_cause.span));
+
+    let mut infringing = Vec::new();
+    for variant in adt.variants() {
+        for field in &variant.fields {
+            // Do this per-field to get better error messages.
+            let infcx = tcx.infer_ctxt().build();
+            let ocx = traits::ObligationCtxt::new(&infcx);
+
+            let unnormalized_ty = field.ty(tcx, args);
+            if unnormalized_ty.references_error() {
+                continue;
+            }
+
+            let field_span = tcx.def_span(field.did);
+            let field_ty_span = match tcx.hir().get_if_local(field.did) {
+                Some(hir::Node::Field(field_def)) => field_def.ty.span,
+                _ => field_span,
+            };
+
+            // FIXME(compiler-errors): This gives us better spans for bad
+            // projection types like in issue-50480.
+            // If the ADT has args, point to the cause we are given.
+            // If it does not, then this field probably doesn't normalize
+            // to begin with, and point to the bad field's span instead.
+            let normalization_cause = if field
+                .ty(tcx, traits::GenericArgs::identity_for_item(tcx, adt.did()))
+                .has_non_region_param()
+            {
+                parent_cause.clone()
+            } else {
+                ObligationCause::dummy_with_span(field_ty_span)
+            };
+            let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty);
+            let normalization_errors = ocx.select_where_possible();
+
+            // NOTE: The post-normalization type may also reference errors,
+            // such as when we project to a missing type or we have a mismatch
+            // between expected and found const-generic types. Don't report an
+            // additional copy error here, since it's not typically useful.
+            if !normalization_errors.is_empty() || ty.references_error() {
+                tcx.dcx().span_delayed_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking {tr} implementation", tr = tcx.def_path_str(trait_def_id)));
+                continue;
+            }
+
+            ocx.register_bound(
+                ObligationCause::dummy_with_span(field_ty_span),
+                param_env,
+                ty,
+                trait_def_id,
+            );
+            let errors = ocx.select_all_or_error();
+            if !errors.is_empty() {
+                infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors)));
+            }
+
+            // Check regions assuming the self type of the impl is WF
+            let outlives_env = OutlivesEnvironment::with_bounds(
+                param_env,
+                infcx.implied_bounds_tys(
+                    param_env,
+                    parent_cause.body_id,
+                    FxIndexSet::from_iter([self_type]),
+                ),
+            );
+            let errors = infcx.resolve_regions(&outlives_env);
+            if !errors.is_empty() {
+                infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
+            }
+        }
+    }
+
+    if infringing.is_empty() { Ok(()) } else { Err(infringing) }
+}
+
+pub fn check_tys_might_be_eq<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    canonical: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>>,
+) -> Result<(), NoSolution> {
+    let (infcx, key, _) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical);
+    let (param_env, (ty_a, ty_b)) = key.into_parts();
+    let ocx = ObligationCtxt::new(&infcx);
+
+    let result = ocx.eq(&ObligationCause::dummy(), param_env, ty_a, ty_b);
+    // use `select_where_possible` instead of `select_all_or_error` so that
+    // we don't get errors from obligations being ambiguous.
+    let errors = ocx.select_where_possible();
+
+    if errors.len() > 0 || result.is_err() { Err(NoSolution) } else { Ok(()) }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
new file mode 100644
index 00000000000..c52b8f37ef3
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -0,0 +1,549 @@
+//! Trait Resolution. See the [rustc dev guide] for more information on how this works.
+//!
+//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
+
+pub mod auto_trait;
+pub(crate) mod coherence;
+pub mod const_evaluatable;
+mod engine;
+pub mod error_reporting;
+mod fulfill;
+pub mod misc;
+mod object_safety;
+pub mod outlives_bounds;
+pub mod project;
+pub mod query;
+#[allow(hidden_glob_reexports)]
+mod select;
+mod specialize;
+mod structural_match;
+mod structural_normalize;
+#[allow(hidden_glob_reexports)]
+mod util;
+pub mod vtable;
+pub mod wf;
+
+use crate::infer::outlives::env::OutlivesEnvironment;
+use crate::infer::{InferCtxt, TyCtxtInferExt};
+use crate::traits::error_reporting::TypeErrCtxtExt as _;
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use rustc_errors::ErrorGuaranteed;
+use rustc_middle::query::Providers;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFolder, TypeSuperVisitable};
+use rustc_middle::ty::{GenericArgs, GenericArgsRef};
+use rustc_span::def_id::DefId;
+use rustc_span::Span;
+
+use std::fmt::Debug;
+use std::ops::ControlFlow;
+
+pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+
+pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
+pub use self::coherence::{OrphanCheckErr, OverlapResult};
+pub use self::engine::{ObligationCtxt, TraitEngineExt};
+pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
+pub use self::object_safety::astconv_object_safety_violations;
+pub use self::object_safety::is_vtable_safe_method;
+pub use self::object_safety::object_safety_violations_for_assoc_item;
+pub use self::object_safety::ObjectSafetyViolation;
+pub use self::project::NormalizeExt;
+pub use self::project::{normalize_inherent_projection, normalize_projection_type};
+pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
+pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
+pub use self::specialize::specialization_graph::FutureCompatOverlapError;
+pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
+pub use self::specialize::{
+    specialization_graph, translate_args, translate_args_with_cause, OverlapError,
+};
+pub use self::structural_match::search_for_structural_match_violation;
+pub use self::structural_normalize::StructurallyNormalizeExt;
+pub use self::util::elaborate;
+pub use self::util::{
+    check_args_compatible, supertrait_def_ids, supertraits, transitive_bounds,
+    transitive_bounds_that_define_assoc_item, SupertraitDefIds,
+};
+pub use self::util::{expand_trait_aliases, TraitAliasExpander};
+pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
+
+pub use rustc_infer::traits::*;
+
+/// Whether to skip the leak check, as part of a future compatibility warning step.
+///
+/// The "default" for skip-leak-check corresponds to the current
+/// behavior (do not skip the leak check) -- not the behavior we are
+/// transitioning into.
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
+pub enum SkipLeakCheck {
+    Yes,
+    #[default]
+    No,
+}
+
+impl SkipLeakCheck {
+    fn is_yes(self) -> bool {
+        self == SkipLeakCheck::Yes
+    }
+}
+
+/// The mode that trait queries run in.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum TraitQueryMode {
+    /// Standard/un-canonicalized queries get accurate
+    /// spans etc. passed in and hence can do reasonable
+    /// error reporting on their own.
+    Standard,
+    /// Canonical queries get dummy spans and hence
+    /// must generally propagate errors to
+    /// pre-canonicalization callsites.
+    Canonical,
+}
+
+/// Creates predicate obligations from the generic bounds.
+#[instrument(level = "debug", skip(cause, param_env))]
+pub fn predicates_for_generics<'tcx>(
+    cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    generic_bounds: ty::InstantiatedPredicates<'tcx>,
+) -> impl Iterator<Item = PredicateObligation<'tcx>> {
+    generic_bounds.into_iter().enumerate().map(move |(idx, (clause, span))| Obligation {
+        cause: cause(idx, span),
+        recursion_depth: 0,
+        param_env,
+        predicate: clause.as_predicate(),
+    })
+}
+
+/// Determines whether the type `ty` is known to meet `bound` and
+/// returns true if so. Returns false if `ty` either does not meet
+/// `bound` or is not known to meet bound (note that this is
+/// conservative towards *no impl*, which is the opposite of the
+/// `evaluate` methods).
+pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    ty: Ty<'tcx>,
+    def_id: DefId,
+) -> bool {
+    let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]);
+    pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref)
+}
+
+/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist?
+///
+/// Ping me on zulip if you want to use this method and need help with finding
+/// an appropriate replacement.
+#[instrument(level = "debug", skip(infcx, param_env, pred), ret)]
+fn pred_known_to_hold_modulo_regions<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    pred: impl ToPredicate<'tcx>,
+) -> bool {
+    let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred);
+
+    let result = infcx.evaluate_obligation_no_overflow(&obligation);
+    debug!(?result);
+
+    if result.must_apply_modulo_regions() {
+        true
+    } else if result.may_apply() {
+        // Sometimes obligations are ambiguous because the recursive evaluator
+        // is not smart enough, so we fall back to fulfillment when we're not certain
+        // that an obligation holds or not. Even still, we must make sure that
+        // the we do no inference in the process of checking this obligation.
+        let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
+        infcx.probe(|_| {
+            let ocx = ObligationCtxt::new(infcx);
+            ocx.register_obligation(obligation);
+
+            let errors = ocx.select_all_or_error();
+            match errors.as_slice() {
+                // Only known to hold if we did no inference.
+                [] => infcx.shallow_resolve(goal) == goal,
+
+                errors => {
+                    debug!(?errors);
+                    false
+                }
+            }
+        })
+    } else {
+        false
+    }
+}
+
+#[instrument(level = "debug", skip(tcx, elaborated_env))]
+fn do_normalize_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    cause: ObligationCause<'tcx>,
+    elaborated_env: ty::ParamEnv<'tcx>,
+    predicates: Vec<ty::Clause<'tcx>>,
+) -> Result<Vec<ty::Clause<'tcx>>, ErrorGuaranteed> {
+    let span = cause.span;
+    // FIXME. We should really... do something with these region
+    // obligations. But this call just continues the older
+    // behavior (i.e., doesn't cause any new bugs), and it would
+    // take some further refactoring to actually solve them. In
+    // particular, we would have to handle implied bounds
+    // properly, and that code is currently largely confined to
+    // regionck (though I made some efforts to extract it
+    // out). -nmatsakis
+    //
+    // @arielby: In any case, these obligations are checked
+    // by wfcheck anyway, so I'm not sure we have to check
+    // them here too, and we will remove this function when
+    // we move over to lazy normalization *anyway*.
+    let infcx = tcx.infer_ctxt().ignoring_regions().build();
+    let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) {
+        Ok(predicates) => predicates,
+        Err(errors) => {
+            let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
+            return Err(reported);
+        }
+    };
+
+    debug!("do_normalize_predicates: normalized predicates = {:?}", predicates);
+
+    // We can use the `elaborated_env` here; the region code only
+    // cares about declarations like `'a: 'b`.
+    let outlives_env = OutlivesEnvironment::new(elaborated_env);
+
+    // FIXME: It's very weird that we ignore region obligations but apparently
+    // still need to use `resolve_regions` as we need the resolved regions in
+    // the normalized predicates.
+    let errors = infcx.resolve_regions(&outlives_env);
+    if !errors.is_empty() {
+        tcx.dcx().span_delayed_bug(
+            span,
+            format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"),
+        );
+    }
+
+    match infcx.fully_resolve(predicates) {
+        Ok(predicates) => Ok(predicates),
+        Err(fixup_err) => {
+            // If we encounter a fixup error, it means that some type
+            // variable wound up unconstrained. I actually don't know
+            // if this can happen, and I certainly don't expect it to
+            // happen often, but if it did happen it probably
+            // represents a legitimate failure due to some kind of
+            // unconstrained variable.
+            //
+            // @lcnr: Let's still ICE here for now. I want a test case
+            // for that.
+            span_bug!(
+                span,
+                "inference variables in normalized parameter environment: {}",
+                fixup_err
+            );
+        }
+    }
+}
+
+// FIXME: this is gonna need to be removed ...
+/// Normalizes the parameter environment, reporting errors if they occur.
+#[instrument(level = "debug", skip(tcx))]
+pub fn normalize_param_env_or_error<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    unnormalized_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+) -> ty::ParamEnv<'tcx> {
+    // I'm not wild about reporting errors here; I'd prefer to
+    // have the errors get reported at a defined place (e.g.,
+    // during typeck). Instead I have all parameter
+    // environments, in effect, going through this function
+    // and hence potentially reporting errors. This ensures of
+    // course that we never forget to normalize (the
+    // alternative seemed like it would involve a lot of
+    // manual invocations of this fn -- and then we'd have to
+    // deal with the errors at each of those sites).
+    //
+    // In any case, in practice, typeck constructs all the
+    // parameter environments once for every fn as it goes,
+    // and errors will get reported then; so outside of type inference we
+    // can be sure that no errors should occur.
+    let mut predicates: Vec<_> = util::elaborate(
+        tcx,
+        unnormalized_env.caller_bounds().into_iter().map(|predicate| {
+            if tcx.features().generic_const_exprs {
+                return predicate;
+            }
+
+            struct ConstNormalizer<'tcx>(TyCtxt<'tcx>);
+
+            impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ConstNormalizer<'tcx> {
+                fn interner(&self) -> TyCtxt<'tcx> {
+                    self.0
+                }
+
+                fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+                    // While it is pretty sus to be evaluating things with an empty param env, it
+                    // should actually be okay since without `feature(generic_const_exprs)` the only
+                    // const arguments that have a non-empty param env are array repeat counts. These
+                    // do not appear in the type system though.
+                    c.normalize(self.0, ty::ParamEnv::empty())
+                }
+            }
+
+            // This whole normalization step is a hack to work around the fact that
+            // `normalize_param_env_or_error` is fundamentally broken from using an
+            // unnormalized param env with a trait solver that expects the param env
+            // to be normalized.
+            //
+            // When normalizing the param env we can end up evaluating obligations
+            // that have been normalized but can only be proven via a where clause
+            // which is still in its unnormalized form. example:
+            //
+            // Attempting to prove `T: Trait<<u8 as Identity>::Assoc>` in a param env
+            // with a `T: Trait<<u8 as Identity>::Assoc>` where clause will fail because
+            // we first normalize obligations before proving them so we end up proving
+            // `T: Trait<u8>`. Since lazy normalization is not implemented equating `u8`
+            // with `<u8 as Identity>::Assoc` fails outright so we incorrectly believe that
+            // we cannot prove `T: Trait<u8>`.
+            //
+            // The same thing is true for const generics- attempting to prove
+            // `T: Trait<ConstKind::Unevaluated(...)>` with the same thing as a where clauses
+            // will fail. After normalization we may be attempting to prove `T: Trait<4>` with
+            // the unnormalized where clause `T: Trait<ConstKind::Unevaluated(...)>`. In order
+            // for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)`
+            // but as we do not have lazy norm implemented, equating the two consts fails outright.
+            //
+            // Ideally we would not normalize consts here at all but it is required for backwards
+            // compatibility. Eventually when lazy norm is implemented this can just be removed.
+            // We do not normalize types here as there is no backwards compatibility requirement
+            // for us to do so.
+            //
+            // FIXME(-Znext-solver): remove this hack since we have deferred projection equality
+            predicate.fold_with(&mut ConstNormalizer(tcx))
+        }),
+    )
+    .collect();
+
+    debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
+
+    let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal());
+
+    // HACK: we are trying to normalize the param-env inside *itself*. The problem is that
+    // normalization expects its param-env to be already normalized, which means we have
+    // a circularity.
+    //
+    // The way we handle this is by normalizing the param-env inside an unnormalized version
+    // of the param-env, which means that if the param-env contains unnormalized projections,
+    // we'll have some normalization failures. This is unfortunate.
+    //
+    // Lazy normalization would basically handle this by treating just the
+    // normalizing-a-trait-ref-requires-itself cycles as evaluation failures.
+    //
+    // Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated
+    // types, so to make the situation less bad, we normalize all the predicates *but*
+    // the `TypeOutlives` predicates first inside the unnormalized parameter environment, and
+    // then we normalize the `TypeOutlives` bounds inside the normalized parameter environment.
+    //
+    // This works fairly well because trait matching does not actually care about param-env
+    // TypeOutlives predicates - these are normally used by regionck.
+    let outlives_predicates: Vec<_> = predicates
+        .extract_if(|predicate| {
+            matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..))
+        })
+        .collect();
+
+    debug!(
+        "normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})",
+        predicates, outlives_predicates
+    );
+    let Ok(non_outlives_predicates) =
+        do_normalize_predicates(tcx, cause.clone(), elaborated_env, predicates)
+    else {
+        // An unnormalized env is better than nothing.
+        debug!("normalize_param_env_or_error: errored resolving non-outlives predicates");
+        return elaborated_env;
+    };
+
+    debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates);
+
+    // Not sure whether it is better to include the unnormalized TypeOutlives predicates
+    // here. I believe they should not matter, because we are ignoring TypeOutlives param-env
+    // predicates here anyway. Keeping them here anyway because it seems safer.
+    let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned();
+    let outlives_env =
+        ty::ParamEnv::new(tcx.mk_clauses_from_iter(outlives_env), unnormalized_env.reveal());
+    let Ok(outlives_predicates) =
+        do_normalize_predicates(tcx, cause, outlives_env, outlives_predicates)
+    else {
+        // An unnormalized env is better than nothing.
+        debug!("normalize_param_env_or_error: errored resolving outlives predicates");
+        return elaborated_env;
+    };
+    debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates);
+
+    let mut predicates = non_outlives_predicates;
+    predicates.extend(outlives_predicates);
+    debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
+    ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal())
+}
+
+/// Normalize a type and process all resulting obligations, returning any errors.
+///
+/// FIXME(-Znext-solver): This should be replaced by `At::deeply_normalize`
+/// which has the same behavior with the new solver. Because using a separate
+/// fulfillment context worsens caching in the old solver, `At::deeply_normalize`
+/// is still lazy with the old solver as it otherwise negatively impacts perf.
+#[instrument(skip_all)]
+pub fn fully_normalize<'tcx, T>(
+    infcx: &InferCtxt<'tcx>,
+    cause: ObligationCause<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    value: T,
+) -> Result<T, Vec<FulfillmentError<'tcx>>>
+where
+    T: TypeFoldable<TyCtxt<'tcx>>,
+{
+    let ocx = ObligationCtxt::new(infcx);
+    debug!(?value);
+    let normalized_value = ocx.normalize(&cause, param_env, value);
+    debug!(?normalized_value);
+    debug!("select_all_or_error start");
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        return Err(errors);
+    }
+    debug!("select_all_or_error complete");
+    let resolved_value = infcx.resolve_vars_if_possible(normalized_value);
+    debug!(?resolved_value);
+    Ok(resolved_value)
+}
+
+/// Normalizes the predicates and checks whether they hold in an empty environment. If this
+/// returns true, then either normalize encountered an error or one of the predicates did not
+/// hold. Used when creating vtables to check for unsatisfiable methods.
+pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec<ty::Clause<'tcx>>) -> bool {
+    debug!("impossible_predicates(predicates={:?})", predicates);
+
+    let infcx = tcx.infer_ctxt().build();
+    let param_env = ty::ParamEnv::reveal_all();
+    let ocx = ObligationCtxt::new(&infcx);
+    let predicates = ocx.normalize(&ObligationCause::dummy(), param_env, predicates);
+    for predicate in predicates {
+        let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate);
+        ocx.register_obligation(obligation);
+    }
+    let errors = ocx.select_all_or_error();
+
+    let result = !errors.is_empty();
+    debug!("impossible_predicates = {:?}", result);
+    result
+}
+
+fn subst_and_check_impossible_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    key: (DefId, GenericArgsRef<'tcx>),
+) -> bool {
+    debug!("subst_and_check_impossible_predicates(key={:?})", key);
+
+    let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates;
+
+    // Specifically check trait fulfillment to avoid an error when trying to resolve
+    // associated items.
+    if let Some(trait_def_id) = tcx.trait_of_item(key.0) {
+        let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1);
+        predicates.push(ty::Binder::dummy(trait_ref).to_predicate(tcx));
+    }
+
+    predicates.retain(|predicate| !predicate.has_param());
+    let result = impossible_predicates(tcx, predicates);
+
+    debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result);
+    result
+}
+
+/// Checks whether a trait's associated item is impossible to reference on a given impl.
+///
+/// This only considers predicates that reference the impl's generics, and not
+/// those that reference the method's generics.
+fn is_impossible_associated_item(
+    tcx: TyCtxt<'_>,
+    (impl_def_id, trait_item_def_id): (DefId, DefId),
+) -> bool {
+    struct ReferencesOnlyParentGenerics<'tcx> {
+        tcx: TyCtxt<'tcx>,
+        generics: &'tcx ty::Generics,
+        trait_item_def_id: DefId,
+    }
+    impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ReferencesOnlyParentGenerics<'tcx> {
+        type BreakTy = ();
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            // If this is a parameter from the trait item's own generics, then bail
+            if let ty::Param(param) = t.kind()
+                && let param_def_id = self.generics.type_param(param, self.tcx).def_id
+                && self.tcx.parent(param_def_id) == self.trait_item_def_id
+            {
+                return ControlFlow::Break(());
+            }
+            t.super_visit_with(self)
+        }
+        fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+            if let ty::ReEarlyParam(param) = r.kind()
+                && let param_def_id = self.generics.region_param(&param, self.tcx).def_id
+                && self.tcx.parent(param_def_id) == self.trait_item_def_id
+            {
+                return ControlFlow::Break(());
+            }
+            ControlFlow::Continue(())
+        }
+        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+            if let ty::ConstKind::Param(param) = ct.kind()
+                && let param_def_id = self.generics.const_param(&param, self.tcx).def_id
+                && self.tcx.parent(param_def_id) == self.trait_item_def_id
+            {
+                return ControlFlow::Break(());
+            }
+            ct.super_visit_with(self)
+        }
+    }
+
+    let generics = tcx.generics_of(trait_item_def_id);
+    let predicates = tcx.predicates_of(trait_item_def_id);
+    let impl_trait_ref = tcx
+        .impl_trait_ref(impl_def_id)
+        .expect("expected impl to correspond to trait")
+        .instantiate_identity();
+    let param_env = tcx.param_env(impl_def_id);
+
+    let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id };
+    let predicates_for_trait = predicates.predicates.iter().filter_map(|(pred, span)| {
+        pred.visit_with(&mut visitor).is_continue().then(|| {
+            Obligation::new(
+                tcx,
+                ObligationCause::dummy_with_span(*span),
+                param_env,
+                ty::EarlyBinder::bind(*pred).instantiate(tcx, impl_trait_ref.args),
+            )
+        })
+    });
+
+    let infcx = tcx.infer_ctxt().ignoring_regions().build();
+    for obligation in predicates_for_trait {
+        // Ignore overflow error, to be conservative.
+        if let Ok(result) = infcx.evaluate_obligation(&obligation)
+            && !result.may_apply()
+        {
+            return true;
+        }
+    }
+    false
+}
+
+pub fn provide(providers: &mut Providers) {
+    object_safety::provide(providers);
+    vtable::provide(providers);
+    *providers = Providers {
+        specialization_graph_of: specialize::specialization_graph_provider,
+        specializes: specialize::specializes,
+        subst_and_check_impossible_predicates,
+        check_tys_might_be_eq: misc::check_tys_might_be_eq,
+        is_impossible_associated_item,
+        ..*providers
+    };
+}
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
new file mode 100644
index 00000000000..6e68dee76a2
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -0,0 +1,930 @@
+//! "Object safety" refers to the ability for a trait to be converted
+//! to an object. In general, traits may only be converted to an
+//! object if all of their methods meet certain criteria. In particular,
+//! they must:
+//!
+//!   - have a suitable receiver from which we can extract a vtable and coerce to a "thin" version
+//!     that doesn't contain the vtable;
+//!   - not reference the erased type `Self` except for in this receiver;
+//!   - not have generic type parameters.
+
+use super::elaborate;
+
+use crate::infer::TyCtxtInferExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::{self, Obligation, ObligationCause};
+use rustc_errors::{DelayDm, FatalError, MultiSpan};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_middle::query::Providers;
+use rustc_middle::ty::{
+    self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
+};
+use rustc_middle::ty::{GenericArg, GenericArgs};
+use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
+use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
+use rustc_span::symbol::Symbol;
+use rustc_span::Span;
+use smallvec::SmallVec;
+
+use std::iter;
+use std::ops::ControlFlow;
+
+pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation};
+
+/// Returns the object safety violations that affect
+/// astconv -- currently, `Self` in supertraits. This is needed
+/// because `object_safety_violations` can't be used during
+/// type collection.
+pub fn astconv_object_safety_violations(
+    tcx: TyCtxt<'_>,
+    trait_def_id: DefId,
+) -> Vec<ObjectSafetyViolation> {
+    debug_assert!(tcx.generics_of(trait_def_id).has_self);
+    let violations = traits::supertrait_def_ids(tcx, trait_def_id)
+        .map(|def_id| predicates_reference_self(tcx, def_id, true))
+        .filter(|spans| !spans.is_empty())
+        .map(ObjectSafetyViolation::SupertraitSelf)
+        .collect();
+
+    debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations);
+
+    violations
+}
+
+fn object_safety_violations(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &'_ [ObjectSafetyViolation] {
+    debug_assert!(tcx.generics_of(trait_def_id).has_self);
+    debug!("object_safety_violations: {:?}", trait_def_id);
+
+    tcx.arena.alloc_from_iter(
+        traits::supertrait_def_ids(tcx, trait_def_id)
+            .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id)),
+    )
+}
+
+fn check_is_object_safe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
+    let violations = tcx.object_safety_violations(trait_def_id);
+
+    if violations.is_empty() {
+        return true;
+    }
+
+    // If the trait contains any other violations, then let the error reporting path
+    // report it instead of emitting a warning here.
+    if violations.iter().all(|violation| {
+        matches!(
+            violation,
+            ObjectSafetyViolation::Method(_, MethodViolationCode::WhereClauseReferencesSelf, _)
+        )
+    }) {
+        for violation in violations {
+            if let ObjectSafetyViolation::Method(
+                _,
+                MethodViolationCode::WhereClauseReferencesSelf,
+                span,
+            ) = violation
+            {
+                lint_object_unsafe_trait(tcx, *span, trait_def_id, violation);
+            }
+        }
+        return true;
+    }
+
+    false
+}
+
+/// We say a method is *vtable safe* if it can be invoked on a trait
+/// object. Note that object-safe traits can have some
+/// non-vtable-safe methods, so long as they require `Self: Sized` or
+/// otherwise ensure that they cannot be used when `Self = Trait`.
+///
+/// [`MethodViolationCode::WhereClauseReferencesSelf`] is considered object safe due to backwards
+/// compatibility, see <https://github.com/rust-lang/rust/issues/51443> and
+/// [`WHERE_CLAUSES_OBJECT_SAFETY`].
+pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::AssocItem) -> bool {
+    debug_assert!(tcx.generics_of(trait_def_id).has_self);
+    debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method);
+    // Any method that has a `Self: Sized` bound cannot be called.
+    if tcx.generics_require_sized_self(method.def_id) {
+        return false;
+    }
+
+    virtual_call_violations_for_method(tcx, trait_def_id, method)
+        .iter()
+        .all(|v| matches!(v, MethodViolationCode::WhereClauseReferencesSelf))
+}
+
+fn object_safety_violations_for_trait(
+    tcx: TyCtxt<'_>,
+    trait_def_id: DefId,
+) -> Vec<ObjectSafetyViolation> {
+    // Check assoc items for violations.
+    let mut violations: Vec<_> = tcx
+        .associated_items(trait_def_id)
+        .in_definition_order()
+        .flat_map(|&item| object_safety_violations_for_assoc_item(tcx, trait_def_id, item))
+        .collect();
+
+    // Check the trait itself.
+    if trait_has_sized_self(tcx, trait_def_id) {
+        // We don't want to include the requirement from `Sized` itself to be `Sized` in the list.
+        let spans = get_sized_bounds(tcx, trait_def_id);
+        violations.push(ObjectSafetyViolation::SizedSelf(spans));
+    }
+    let spans = predicates_reference_self(tcx, trait_def_id, false);
+    if !spans.is_empty() {
+        violations.push(ObjectSafetyViolation::SupertraitSelf(spans));
+    }
+    let spans = bounds_reference_self(tcx, trait_def_id);
+    if !spans.is_empty() {
+        violations.push(ObjectSafetyViolation::SupertraitSelf(spans));
+    }
+    let spans = super_predicates_have_non_lifetime_binders(tcx, trait_def_id);
+    if !spans.is_empty() {
+        violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans));
+    }
+
+    debug!(
+        "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
+        trait_def_id, violations
+    );
+
+    violations
+}
+
+/// Lint object-unsafe trait.
+fn lint_object_unsafe_trait(
+    tcx: TyCtxt<'_>,
+    span: Span,
+    trait_def_id: DefId,
+    violation: &ObjectSafetyViolation,
+) {
+    // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id.
+    // It's also hard to get a use site span, so we use the method definition span.
+    tcx.struct_span_lint_hir(
+        WHERE_CLAUSES_OBJECT_SAFETY,
+        hir::CRATE_HIR_ID,
+        span,
+        DelayDm(|| format!("the trait `{}` cannot be made into an object", tcx.def_path_str(trait_def_id))),
+        |err| {
+            let node = tcx.hir().get_if_local(trait_def_id);
+            let mut spans = MultiSpan::from_span(span);
+            if let Some(hir::Node::Item(item)) = node {
+                spans.push_span_label(
+                    item.ident.span,
+                    "this trait cannot be made into an object...",
+                );
+                spans.push_span_label(span, format!("...because {}", violation.error_msg()));
+            } else {
+                spans.push_span_label(
+                    span,
+                    format!(
+                        "the trait cannot be made into an object because {}",
+                        violation.error_msg()
+                    ),
+                );
+            };
+            err.span_note(
+                spans,
+                "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \
+                call to be resolvable dynamically; for more information visit \
+                <https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
+            );
+            if node.is_some() {
+                // Only provide the help if its a local trait, otherwise it's not
+                violation.solution().add_to(err);
+            }
+        },
+    );
+}
+
+fn sized_trait_bound_spans<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    bounds: hir::GenericBounds<'tcx>,
+) -> impl 'tcx + Iterator<Item = Span> {
+    bounds.iter().filter_map(move |b| match b {
+        hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None)
+            if trait_has_sized_self(
+                tcx,
+                trait_ref.trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()),
+            ) =>
+        {
+            // Fetch spans for supertraits that are `Sized`: `trait T: Super`
+            Some(trait_ref.span)
+        }
+        _ => None,
+    })
+}
+
+fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> {
+    tcx.hir()
+        .get_if_local(trait_def_id)
+        .and_then(|node| match node {
+            hir::Node::Item(hir::Item {
+                kind: hir::ItemKind::Trait(.., generics, bounds, _),
+                ..
+            }) => Some(
+                generics
+                    .predicates
+                    .iter()
+                    .filter_map(|pred| {
+                        match pred {
+                            hir::WherePredicate::BoundPredicate(pred)
+                                if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id =>
+                            {
+                                // Fetch spans for trait bounds that are Sized:
+                                // `trait T where Self: Pred`
+                                Some(sized_trait_bound_spans(tcx, pred.bounds))
+                            }
+                            _ => None,
+                        }
+                    })
+                    .flatten()
+                    // Fetch spans for supertraits that are `Sized`: `trait T: Super`.
+                    .chain(sized_trait_bound_spans(tcx, bounds))
+                    .collect::<SmallVec<[Span; 1]>>(),
+            ),
+            _ => None,
+        })
+        .unwrap_or_else(SmallVec::new)
+}
+
+fn predicates_reference_self(
+    tcx: TyCtxt<'_>,
+    trait_def_id: DefId,
+    supertraits_only: bool,
+) -> SmallVec<[Span; 1]> {
+    let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id));
+    let predicates = if supertraits_only {
+        tcx.super_predicates_of(trait_def_id)
+    } else {
+        tcx.predicates_of(trait_def_id)
+    };
+    predicates
+        .predicates
+        .iter()
+        .map(|&(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp))
+        .filter_map(|predicate| predicate_references_self(tcx, predicate))
+        .collect()
+}
+
+fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> {
+    tcx.associated_items(trait_def_id)
+        .in_definition_order()
+        .filter(|item| item.kind == ty::AssocKind::Type)
+        .flat_map(|item| tcx.explicit_item_bounds(item.def_id).instantiate_identity_iter_copied())
+        .filter_map(|c| predicate_references_self(tcx, c))
+        .collect()
+}
+
+fn predicate_references_self<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    (predicate, sp): (ty::Clause<'tcx>, Span),
+) -> Option<Span> {
+    let self_ty = tcx.types.self_param;
+    let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into());
+    match predicate.kind().skip_binder() {
+        ty::ClauseKind::Trait(ref data) => {
+            // In the case of a trait predicate, we can skip the "self" type.
+            data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp)
+        }
+        ty::ClauseKind::Projection(ref data) => {
+            // And similarly for projections. This should be redundant with
+            // the previous check because any projection should have a
+            // matching `Trait` predicate with the same inputs, but we do
+            // the check to be safe.
+            //
+            // It's also won't be redundant if we allow type-generic associated
+            // types for trait objects.
+            //
+            // Note that we *do* allow projection *outputs* to contain
+            // `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`),
+            // we just require the user to specify *both* outputs
+            // in the object type (i.e., `dyn Foo<Output=(), Result=()>`).
+            //
+            // This is ALT2 in issue #56288, see that for discussion of the
+            // possible alternatives.
+            data.projection_ty.args[1..].iter().any(has_self_ty).then_some(sp)
+        }
+        ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp),
+
+        ty::ClauseKind::WellFormed(..)
+        | ty::ClauseKind::TypeOutlives(..)
+        | ty::ClauseKind::RegionOutlives(..)
+        // FIXME(generic_const_exprs): this can mention `Self`
+        | ty::ClauseKind::ConstEvaluatable(..)
+         => None,
+    }
+}
+
+fn super_predicates_have_non_lifetime_binders(
+    tcx: TyCtxt<'_>,
+    trait_def_id: DefId,
+) -> SmallVec<[Span; 1]> {
+    // If non_lifetime_binders is disabled, then exit early
+    if !tcx.features().non_lifetime_binders {
+        return SmallVec::new();
+    }
+    tcx.super_predicates_of(trait_def_id)
+        .predicates
+        .iter()
+        .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(*span))
+        .collect()
+}
+
+fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
+    tcx.generics_require_sized_self(trait_def_id)
+}
+
+fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+    let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
+        return false; /* No Sized trait, can't require it! */
+    };
+
+    // Search for a predicate like `Self : Sized` amongst the trait bounds.
+    let predicates = tcx.predicates_of(def_id);
+    let predicates = predicates.instantiate_identity(tcx).predicates;
+    elaborate(tcx, predicates).any(|pred| match pred.kind().skip_binder() {
+        ty::ClauseKind::Trait(ref trait_pred) => {
+            trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0)
+        }
+        ty::ClauseKind::RegionOutlives(_)
+        | ty::ClauseKind::TypeOutlives(_)
+        | ty::ClauseKind::Projection(_)
+        | ty::ClauseKind::ConstArgHasType(_, _)
+        | ty::ClauseKind::WellFormed(_)
+        | ty::ClauseKind::ConstEvaluatable(_) => false,
+    })
+}
+
+/// Returns `Some(_)` if this item makes the containing trait not object safe.
+#[instrument(level = "debug", skip(tcx), ret)]
+pub fn object_safety_violations_for_assoc_item(
+    tcx: TyCtxt<'_>,
+    trait_def_id: DefId,
+    item: ty::AssocItem,
+) -> Vec<ObjectSafetyViolation> {
+    // Any item that has a `Self : Sized` requisite is otherwise
+    // exempt from the regulations.
+    if tcx.generics_require_sized_self(item.def_id) {
+        return Vec::new();
+    }
+
+    match item.kind {
+        // Associated consts are never object safe, as they can't have `where` bounds yet at all,
+        // and associated const bounds in trait objects aren't a thing yet either.
+        ty::AssocKind::Const => {
+            vec![ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span)]
+        }
+        ty::AssocKind::Fn => virtual_call_violations_for_method(tcx, trait_def_id, item)
+            .into_iter()
+            .map(|v| {
+                let node = tcx.hir().get_if_local(item.def_id);
+                // Get an accurate span depending on the violation.
+                let span = match (&v, node) {
+                    (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span,
+                    (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span,
+                    (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span,
+                    (MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
+                        node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span())
+                    }
+                    _ => item.ident(tcx).span,
+                };
+
+                ObjectSafetyViolation::Method(item.name, v, span)
+            })
+            .collect(),
+        // Associated types can only be object safe if they have `Self: Sized` bounds.
+        ty::AssocKind::Type => {
+            if !tcx.features().generic_associated_types_extended
+                && !tcx.generics_of(item.def_id).params.is_empty()
+                && !item.is_impl_trait_in_trait()
+            {
+                vec![ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span)]
+            } else {
+                // We will permit associated types if they are explicitly mentioned in the trait object.
+                // We can't check this here, as here we only check if it is guaranteed to not be possible.
+                Vec::new()
+            }
+        }
+    }
+}
+
+/// Returns `Some(_)` if this method cannot be called on a trait
+/// object; this does not necessarily imply that the enclosing trait
+/// is not object safe, because the method might have a where clause
+/// `Self:Sized`.
+fn virtual_call_violations_for_method<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_def_id: DefId,
+    method: ty::AssocItem,
+) -> Vec<MethodViolationCode> {
+    let sig = tcx.fn_sig(method.def_id).instantiate_identity();
+
+    // The method's first parameter must be named `self`
+    if !method.fn_has_self_parameter {
+        let sugg = if let Some(hir::Node::TraitItem(hir::TraitItem {
+            generics,
+            kind: hir::TraitItemKind::Fn(sig, _),
+            ..
+        })) = tcx.hir().get_if_local(method.def_id).as_ref()
+        {
+            let sm = tcx.sess.source_map();
+            Some((
+                (
+                    format!("&self{}", if sig.decl.inputs.is_empty() { "" } else { ", " }),
+                    sm.span_through_char(sig.span, '(').shrink_to_hi(),
+                ),
+                (
+                    format!("{} Self: Sized", generics.add_where_or_trailing_comma()),
+                    generics.tail_span_for_predicate_suggestion(),
+                ),
+            ))
+        } else {
+            None
+        };
+
+        // Not having `self` parameter messes up the later checks,
+        // so we need to return instead of pushing
+        return vec![MethodViolationCode::StaticMethod(sugg)];
+    }
+
+    let mut errors = Vec::new();
+
+    for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) {
+        if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) {
+            let span = if let Some(hir::Node::TraitItem(hir::TraitItem {
+                kind: hir::TraitItemKind::Fn(sig, _),
+                ..
+            })) = tcx.hir().get_if_local(method.def_id).as_ref()
+            {
+                Some(sig.decl.inputs[i].span)
+            } else {
+                None
+            };
+            errors.push(MethodViolationCode::ReferencesSelfInput(span));
+        }
+    }
+    if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
+        errors.push(MethodViolationCode::ReferencesSelfOutput);
+    }
+    if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
+        errors.push(code);
+    }
+
+    // We can't monomorphize things like `fn foo<A>(...)`.
+    let own_counts = tcx.generics_of(method.def_id).own_counts();
+    if own_counts.types > 0 || own_counts.consts > 0 {
+        errors.push(MethodViolationCode::Generic);
+    }
+
+    let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0));
+
+    // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on.
+    // However, this is already considered object-safe. We allow it as a special case here.
+    // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows
+    // `Receiver: Unsize<Receiver[Self => dyn Trait]>`.
+    if receiver_ty != tcx.types.self_param {
+        if !receiver_is_dispatchable(tcx, method, receiver_ty) {
+            let span = if let Some(hir::Node::TraitItem(hir::TraitItem {
+                kind: hir::TraitItemKind::Fn(sig, _),
+                ..
+            })) = tcx.hir().get_if_local(method.def_id).as_ref()
+            {
+                Some(sig.decl.inputs[0].span)
+            } else {
+                None
+            };
+            errors.push(MethodViolationCode::UndispatchableReceiver(span));
+        } else {
+            // Do sanity check to make sure the receiver actually has the layout of a pointer.
+
+            use rustc_target::abi::Abi;
+
+            let param_env = tcx.param_env(method.def_id);
+
+            let abi_of_ty = |ty: Ty<'tcx>| -> Option<Abi> {
+                match tcx.layout_of(param_env.and(ty)) {
+                    Ok(layout) => Some(layout.abi),
+                    Err(err) => {
+                        // #78372
+                        tcx.dcx().span_delayed_bug(
+                            tcx.def_span(method.def_id),
+                            format!("error: {err}\n while computing layout for type {ty:?}"),
+                        );
+                        None
+                    }
+                }
+            };
+
+            // e.g., `Rc<()>`
+            let unit_receiver_ty =
+                receiver_for_self_ty(tcx, receiver_ty, Ty::new_unit(tcx), method.def_id);
+
+            match abi_of_ty(unit_receiver_ty) {
+                Some(Abi::Scalar(..)) => (),
+                abi => {
+                    tcx.dcx().span_delayed_bug(
+                        tcx.def_span(method.def_id),
+                        format!(
+                            "receiver when `Self = ()` should have a Scalar ABI; found {abi:?}"
+                        ),
+                    );
+                }
+            }
+
+            let trait_object_ty = object_ty_for_trait(tcx, trait_def_id, tcx.lifetimes.re_static);
+
+            // e.g., `Rc<dyn Trait>`
+            let trait_object_receiver =
+                receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method.def_id);
+
+            match abi_of_ty(trait_object_receiver) {
+                Some(Abi::ScalarPair(..)) => (),
+                abi => {
+                    tcx.dcx().span_delayed_bug(
+                        tcx.def_span(method.def_id),
+                        format!(
+                            "receiver when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}"
+                        ),
+                    );
+                }
+            }
+        }
+    }
+
+    // NOTE: This check happens last, because it results in a lint, and not a
+    // hard error.
+    if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, span)| {
+        // dyn Trait is okay:
+        //
+        //     trait Trait {
+        //         fn f(&self) where Self: 'static;
+        //     }
+        //
+        // because a trait object can't claim to live longer than the concrete
+        // type. If the lifetime bound holds on dyn Trait then it's guaranteed
+        // to hold as well on the concrete type.
+        if pred.as_type_outlives_clause().is_some() {
+            return false;
+        }
+
+        // dyn Trait is okay:
+        //
+        //     auto trait AutoTrait {}
+        //
+        //     trait Trait {
+        //         fn f(&self) where Self: AutoTrait;
+        //     }
+        //
+        // because `impl AutoTrait for dyn Trait` is disallowed by coherence.
+        // Traits with a default impl are implemented for a trait object if and
+        // only if the autotrait is one of the trait object's trait bounds, like
+        // in `dyn Trait + AutoTrait`. This guarantees that trait objects only
+        // implement auto traits if the underlying type does as well.
+        if let ty::ClauseKind::Trait(ty::TraitPredicate {
+            trait_ref: pred_trait_ref,
+            polarity: ty::ImplPolarity::Positive,
+        }) = pred.kind().skip_binder()
+            && pred_trait_ref.self_ty() == tcx.types.self_param
+            && tcx.trait_is_auto(pred_trait_ref.def_id)
+        {
+            // Consider bounds like `Self: Bound<Self>`. Auto traits are not
+            // allowed to have generic parameters so `auto trait Bound<T> {}`
+            // would already have reported an error at the definition of the
+            // auto trait.
+            if pred_trait_ref.args.len() != 1 {
+                tcx.dcx().span_delayed_bug(span, "auto traits cannot have generic parameters");
+            }
+            return false;
+        }
+
+        contains_illegal_self_type_reference(tcx, trait_def_id, pred)
+    }) {
+        errors.push(MethodViolationCode::WhereClauseReferencesSelf);
+    }
+
+    errors
+}
+
+/// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`.
+/// For example, for `receiver_ty = Rc<Self>` and `self_ty = Foo`, returns `Rc<Foo>`.
+fn receiver_for_self_ty<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    receiver_ty: Ty<'tcx>,
+    self_ty: Ty<'tcx>,
+    method_def_id: DefId,
+) -> Ty<'tcx> {
+    debug!("receiver_for_self_ty({:?}, {:?}, {:?})", receiver_ty, self_ty, method_def_id);
+    let args = GenericArgs::for_item(tcx, method_def_id, |param, _| {
+        if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) }
+    });
+
+    let result = EarlyBinder::bind(receiver_ty).instantiate(tcx, args);
+    debug!(
+        "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}",
+        receiver_ty, self_ty, method_def_id, result
+    );
+    result
+}
+
+/// Creates the object type for the current trait. For example,
+/// if the current trait is `Deref`, then this will be
+/// `dyn Deref<Target = Self::Target> + 'static`.
+#[instrument(level = "trace", skip(tcx), ret)]
+fn object_ty_for_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_def_id: DefId,
+    lifetime: ty::Region<'tcx>,
+) -> Ty<'tcx> {
+    let trait_ref = ty::TraitRef::identity(tcx, trait_def_id);
+    debug!(?trait_ref);
+
+    let trait_predicate = ty::Binder::dummy(ty::ExistentialPredicate::Trait(
+        ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref),
+    ));
+    debug!(?trait_predicate);
+
+    let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx);
+    let mut elaborated_predicates: Vec<_> = elaborate(tcx, [pred])
+        .filter_map(|pred| {
+            debug!(?pred);
+            let pred = pred.to_opt_poly_projection_pred()?;
+            Some(pred.map_bound(|p| {
+                ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty(
+                    tcx, p,
+                ))
+            }))
+        })
+        .collect();
+    // NOTE: Since #37965, the existential predicates list has depended on the
+    // list of predicates to be sorted. This is mostly to enforce that the primary
+    // predicate comes first.
+    elaborated_predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
+    elaborated_predicates.dedup();
+
+    let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(
+        iter::once(trait_predicate).chain(elaborated_predicates),
+    );
+    debug!(?existential_predicates);
+
+    Ty::new_dynamic(tcx, existential_predicates, lifetime, ty::Dyn)
+}
+
+/// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a
+/// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type
+/// in the following way:
+/// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc<Self>`,
+/// - require the following bound:
+///
+///   ```ignore (not-rust)
+///   Receiver[Self => T]: DispatchFromDyn<Receiver[Self => dyn Trait]>
+///   ```
+///
+///   where `Foo[X => Y]` means "the same type as `Foo`, but with `X` replaced with `Y`"
+///   (substitution notation).
+///
+/// Some examples of receiver types and their required obligation:
+/// - `&'a mut self` requires `&'a mut Self: DispatchFromDyn<&'a mut dyn Trait>`,
+/// - `self: Rc<Self>` requires `Rc<Self>: DispatchFromDyn<Rc<dyn Trait>>`,
+/// - `self: Pin<Box<Self>>` requires `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<dyn Trait>>>`.
+///
+/// The only case where the receiver is not dispatchable, but is still a valid receiver
+/// type (just not object-safe), is when there is more than one level of pointer indirection.
+/// E.g., `self: &&Self`, `self: &Rc<Self>`, `self: Box<Box<Self>>`. In these cases, there
+/// is no way, or at least no inexpensive way, to coerce the receiver from the version where
+/// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type
+/// contained by the trait object, because the object that needs to be coerced is behind
+/// a pointer.
+///
+/// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result
+/// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch
+/// is stabilized, see tracking issue <https://github.com/rust-lang/rust/issues/43561>).
+/// Instead, we fudge a little by introducing a new type parameter `U` such that
+/// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`.
+/// Written as a chalk-style query:
+/// ```ignore (not-rust)
+/// forall (U: Trait + ?Sized) {
+///     if (Self: Unsize<U>) {
+///         Receiver: DispatchFromDyn<Receiver[Self => U]>
+///     }
+/// }
+/// ```
+/// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>`
+/// for `self: Rc<Self>`, this means `Rc<Self>: DispatchFromDyn<Rc<U>>`
+/// for `self: Pin<Box<Self>>`, this means `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<U>>>`
+//
+// FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this
+// fallback query: `Receiver: Unsize<Receiver[Self => U]>` to support receivers like
+// `self: Wrapper<Self>`.
+fn receiver_is_dispatchable<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    method: ty::AssocItem,
+    receiver_ty: Ty<'tcx>,
+) -> bool {
+    debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty);
+
+    let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait());
+    let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else {
+        debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits");
+        return false;
+    };
+
+    // the type `U` in the query
+    // use a bogus type parameter to mimic a forall(U) query using u32::MAX for now.
+    // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can
+    // replace this with `dyn Trait`
+    let unsized_self_ty: Ty<'tcx> =
+        Ty::new_param(tcx, u32::MAX, Symbol::intern("RustaceansAreAwesome"));
+
+    // `Receiver[Self => U]`
+    let unsized_receiver_ty =
+        receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id);
+
+    // create a modified param env, with `Self: Unsize<U>` and `U: Trait` added to caller bounds
+    // `U: ?Sized` is already implied here
+    let param_env = {
+        let param_env = tcx.param_env(method.def_id);
+
+        // Self: Unsize<U>
+        let unsize_predicate =
+            ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty])
+                .to_predicate(tcx);
+
+        // U: Trait<Arg1, ..., ArgN>
+        let trait_predicate = {
+            let trait_def_id = method.trait_container(tcx).unwrap();
+            let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
+                if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
+            });
+
+            ty::TraitRef::new(tcx, trait_def_id, args).to_predicate(tcx)
+        };
+
+        let caller_bounds =
+            param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]);
+
+        ty::ParamEnv::new(tcx.mk_clauses_from_iter(caller_bounds), param_env.reveal())
+    };
+
+    // Receiver: DispatchFromDyn<Receiver[Self => U]>
+    let obligation = {
+        let predicate =
+            ty::TraitRef::new(tcx, dispatch_from_dyn_did, [receiver_ty, unsized_receiver_ty]);
+
+        Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
+    };
+
+    let infcx = tcx.infer_ctxt().build();
+    // the receiver is dispatchable iff the obligation holds
+    infcx.predicate_must_hold_modulo_regions(&obligation)
+}
+
+fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
+    tcx: TyCtxt<'tcx>,
+    trait_def_id: DefId,
+    value: T,
+) -> bool {
+    // This is somewhat subtle. In general, we want to forbid
+    // references to `Self` in the argument and return types,
+    // since the value of `Self` is erased. However, there is one
+    // exception: it is ok to reference `Self` in order to access
+    // an associated type of the current trait, since we retain
+    // the value of those associated types in the object type
+    // itself.
+    //
+    // ```rust
+    // trait SuperTrait {
+    //     type X;
+    // }
+    //
+    // trait Trait : SuperTrait {
+    //     type Y;
+    //     fn foo(&self, x: Self) // bad
+    //     fn foo(&self) -> Self // bad
+    //     fn foo(&self) -> Option<Self> // bad
+    //     fn foo(&self) -> Self::Y // OK, desugars to next example
+    //     fn foo(&self) -> <Self as Trait>::Y // OK
+    //     fn foo(&self) -> Self::X // OK, desugars to next example
+    //     fn foo(&self) -> <Self as SuperTrait>::X // OK
+    // }
+    // ```
+    //
+    // However, it is not as simple as allowing `Self` in a projected
+    // type, because there are illegal ways to use `Self` as well:
+    //
+    // ```rust
+    // trait Trait : SuperTrait {
+    //     ...
+    //     fn foo(&self) -> <Self as SomeOtherTrait>::X;
+    // }
+    // ```
+    //
+    // Here we will not have the type of `X` recorded in the
+    // object type, and we cannot resolve `Self as SomeOtherTrait`
+    // without knowing what `Self` is.
+
+    struct IllegalSelfTypeVisitor<'tcx> {
+        tcx: TyCtxt<'tcx>,
+        trait_def_id: DefId,
+        supertraits: Option<Vec<DefId>>,
+    }
+
+    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
+        type BreakTy = ();
+
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            match t.kind() {
+                ty::Param(_) => {
+                    if t == self.tcx.types.self_param {
+                        ControlFlow::Break(())
+                    } else {
+                        ControlFlow::Continue(())
+                    }
+                }
+                ty::Alias(ty::Projection, ref data)
+                    if self.tcx.is_impl_trait_in_trait(data.def_id) =>
+                {
+                    // We'll deny these later in their own pass
+                    ControlFlow::Continue(())
+                }
+                ty::Alias(ty::Projection, ref data) => {
+                    // This is a projected type `<Foo as SomeTrait>::X`.
+
+                    // Compute supertraits of current trait lazily.
+                    if self.supertraits.is_none() {
+                        let trait_ref =
+                            ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id));
+                        self.supertraits = Some(
+                            traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(),
+                        );
+                    }
+
+                    // Determine whether the trait reference `Foo as
+                    // SomeTrait` is in fact a supertrait of the
+                    // current trait. In that case, this type is
+                    // legal, because the type `X` will be specified
+                    // in the object type. Note that we can just use
+                    // direct equality here because all of these types
+                    // are part of the formal parameter listing, and
+                    // hence there should be no inference variables.
+                    let is_supertrait_of_current_trait = self
+                        .supertraits
+                        .as_ref()
+                        .unwrap()
+                        .contains(&data.trait_ref(self.tcx).def_id);
+
+                    if is_supertrait_of_current_trait {
+                        ControlFlow::Continue(()) // do not walk contained types, do not report error, do collect $200
+                    } else {
+                        t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
+                    }
+                }
+                _ => t.super_visit_with(self), // walk contained types, if any
+            }
+        }
+
+        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+            // Constants can only influence object safety if they are generic and reference `Self`.
+            // This is only possible for unevaluated constants, so we walk these here.
+            self.tcx.expand_abstract_consts(ct).super_visit_with(self)
+        }
+    }
+
+    value
+        .visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None })
+        .is_break()
+}
+
+pub fn contains_illegal_impl_trait_in_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_def_id: DefId,
+    ty: ty::Binder<'tcx, Ty<'tcx>>,
+) -> Option<MethodViolationCode> {
+    // This would be caught below, but rendering the error as a separate
+    // `async-specific` message is better.
+    if tcx.asyncness(fn_def_id).is_async() {
+        return Some(MethodViolationCode::AsyncFn);
+    }
+
+    // FIXME(RPITIT): Perhaps we should use a visitor here?
+    ty.skip_binder().walk().find_map(|arg| {
+        if let ty::GenericArgKind::Type(ty) = arg.unpack()
+            && let ty::Alias(ty::Projection, proj) = ty.kind()
+            && tcx.is_impl_trait_in_trait(proj.def_id)
+        {
+            Some(MethodViolationCode::ReferencesImplTraitInTrait(tcx.def_span(proj.def_id)))
+        } else {
+            None
+        }
+    })
+}
+
+pub fn provide(providers: &mut Providers) {
+    *providers = Providers {
+        object_safety_violations,
+        check_is_object_safe,
+        generics_require_sized_self,
+        ..*providers
+    };
+}
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
new file mode 100644
index 00000000000..9749ff9cbc1
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -0,0 +1,132 @@
+use crate::infer::InferCtxt;
+use crate::traits::{ObligationCause, ObligationCtxt};
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_infer::infer::resolve::OpportunisticRegionResolver;
+use rustc_infer::infer::InferOk;
+use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints};
+use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt};
+use rustc_span::def_id::LocalDefId;
+
+pub use rustc_middle::traits::query::OutlivesBound;
+
+pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
+pub trait InferCtxtExt<'a, 'tcx> {
+    fn implied_outlives_bounds(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        body_id: LocalDefId,
+        ty: Ty<'tcx>,
+    ) -> Vec<OutlivesBound<'tcx>>;
+
+    fn implied_bounds_tys(
+        &'a self,
+        param_env: ty::ParamEnv<'tcx>,
+        body_id: LocalDefId,
+        tys: FxIndexSet<Ty<'tcx>>,
+    ) -> Bounds<'a, 'tcx>;
+}
+
+impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
+    /// Implied bounds are region relationships that we deduce
+    /// automatically. The idea is that (e.g.) a caller must check that a
+    /// function's argument types are well-formed immediately before
+    /// calling that fn, and hence the *callee* can assume that its
+    /// argument types are well-formed. This may imply certain relationships
+    /// between generic parameters. For example:
+    /// ```
+    /// fn foo<T>(x: &T) {}
+    /// ```
+    /// can only be called with a `'a` and `T` such that `&'a T` is WF.
+    /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
+    ///
+    /// # Parameters
+    ///
+    /// - `param_env`, the where-clauses in scope
+    /// - `body_id`, the body-id to use when normalizing assoc types.
+    ///   Note that this may cause outlives obligations to be injected
+    ///   into the inference context with this body-id.
+    /// - `ty`, the type that we are supposed to assume is WF.
+    #[instrument(level = "debug", skip(self, param_env, body_id), ret)]
+    fn implied_outlives_bounds(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        body_id: LocalDefId,
+        ty: Ty<'tcx>,
+    ) -> Vec<OutlivesBound<'tcx>> {
+        let ty = self.resolve_vars_if_possible(ty);
+        let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
+
+        // We do not expect existential variables in implied bounds.
+        // We may however encounter unconstrained lifetime variables
+        // in very rare cases.
+        //
+        // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for
+        // an example.
+        assert!(!ty.has_non_region_infer());
+
+        let mut canonical_var_values = OriginalQueryValues::default();
+        let canonical_ty =
+            self.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values);
+        let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else {
+            return vec![];
+        };
+
+        let mut constraints = QueryRegionConstraints::default();
+        let Ok(InferOk { value: mut bounds, obligations }) = self
+            .instantiate_nll_query_response_and_region_obligations(
+                &ObligationCause::dummy(),
+                param_env,
+                &canonical_var_values,
+                canonical_result,
+                &mut constraints,
+            )
+        else {
+            return vec![];
+        };
+        assert_eq!(&obligations, &[]);
+
+        // Because of #109628, we may have unexpected placeholders. Ignore them!
+        // FIXME(#109628): panic in this case once the issue is fixed.
+        bounds.retain(|bound| !bound.has_placeholders());
+
+        if !constraints.is_empty() {
+            let span = self.tcx.def_span(body_id);
+
+            debug!(?constraints);
+            if !constraints.member_constraints.is_empty() {
+                span_bug!(span, "{:#?}", constraints.member_constraints);
+            }
+
+            // Instantiation may have produced new inference variables and constraints on those
+            // variables. Process these constraints.
+            let ocx = ObligationCtxt::new(self);
+            let cause = ObligationCause::misc(span, body_id);
+            for &constraint in &constraints.outlives {
+                ocx.register_obligation(self.query_outlives_constraint_to_obligation(
+                    constraint,
+                    cause.clone(),
+                    param_env,
+                ));
+            }
+
+            let errors = ocx.select_all_or_error();
+            if !errors.is_empty() {
+                self.dcx().span_delayed_bug(
+                    span,
+                    "implied_outlives_bounds failed to solve obligations from instantiation",
+                );
+            }
+        };
+
+        bounds
+    }
+
+    fn implied_bounds_tys(
+        &'a self,
+        param_env: ParamEnv<'tcx>,
+        body_id: LocalDefId,
+        tys: FxIndexSet<Ty<'tcx>>,
+    ) -> Bounds<'a, 'tcx> {
+        tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
new file mode 100644
index 00000000000..dd4e69efe37
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -0,0 +1,2626 @@
+//! Code for projecting associated types out of trait references.
+
+use super::check_args_compatible;
+use super::specialization_graph;
+use super::translate_args;
+use super::util;
+use super::MismatchedProjectionTypes;
+use super::Obligation;
+use super::ObligationCause;
+use super::PredicateObligation;
+use super::Selection;
+use super::SelectionContext;
+use super::SelectionError;
+use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
+use rustc_middle::traits::BuiltinImplSource;
+use rustc_middle::traits::ImplSource;
+use rustc_middle::traits::ImplSourceUserDefinedData;
+
+use crate::errors::InherentProjectionNormalizationOverflow;
+use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use crate::infer::{BoundRegionConversionTime, InferCtxt, InferOk};
+use crate::traits::error_reporting::TypeErrCtxtExt as _;
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use crate::traits::select::ProjectionMatchesProjection;
+use rustc_data_structures::sso::SsoHashSet;
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir::def::DefKind;
+use rustc_hir::lang_items::LangItem;
+use rustc_infer::infer::at::At;
+use rustc_infer::infer::resolve::OpportunisticRegionResolver;
+use rustc_infer::infer::DefineOpaqueTypes;
+use rustc_infer::traits::FulfillmentError;
+use rustc_infer::traits::ObligationCauseCode;
+use rustc_infer::traits::TraitEngine;
+use rustc_middle::traits::select::OverflowError;
+use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt};
+use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt};
+use rustc_span::symbol::sym;
+
+use std::collections::BTreeMap;
+
+pub use rustc_middle::traits::Reveal;
+
+pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
+
+pub type ProjectionObligation<'tcx> = Obligation<'tcx, ty::ProjectionPredicate<'tcx>>;
+
+pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::AliasTy<'tcx>>;
+
+pub(super) struct InProgress;
+
+pub trait NormalizeExt<'tcx> {
+    /// Normalize a value using the `AssocTypeNormalizer`.
+    ///
+    /// This normalization should be used when the type contains inference variables or the
+    /// projection may be fallible.
+    fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> InferOk<'tcx, T>;
+
+    /// Deeply normalizes `value`, replacing all aliases which can by normalized in
+    /// the current environment. In the new solver this errors in case normalization
+    /// fails or is ambiguous. This only normalizes opaque types with `Reveal::All`.
+    ///
+    /// In the old solver this simply uses `normalizes` and adds the nested obligations
+    /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the
+    /// same goals in both a temporary and the shared context which negatively impacts
+    /// performance as these don't share caching.
+    ///
+    /// FIXME(-Znext-solver): This has the same behavior as `traits::fully_normalize`
+    /// in the new solver, but because of performance reasons, we currently reuse an
+    /// existing fulfillment context in the old solver. Once we also eagerly prove goals with
+    /// the old solver or have removed the old solver, remove `traits::fully_normalize` and
+    /// rename this function to `At::fully_normalize`.
+    fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+        self,
+        value: T,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Result<T, Vec<FulfillmentError<'tcx>>>;
+}
+
+impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> {
+    fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
+        if self.infcx.next_trait_solver() {
+            InferOk { value, obligations: Vec::new() }
+        } else {
+            let mut selcx = SelectionContext::new(self.infcx);
+            let Normalized { value, obligations } =
+                normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
+            InferOk { value, obligations }
+        }
+    }
+
+    fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+        self,
+        value: T,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+        if self.infcx.next_trait_solver() {
+            crate::solve::deeply_normalize(self, value)
+        } else {
+            let value = self
+                .normalize(value)
+                .into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
+            let errors = fulfill_cx.select_where_possible(self.infcx);
+            let value = self.infcx.resolve_vars_if_possible(value);
+            if errors.is_empty() { Ok(value) } else { Err(errors) }
+        }
+    }
+}
+
+/// When attempting to resolve `<T as TraitRef>::Name` ...
+#[derive(Debug)]
+pub enum ProjectionError<'tcx> {
+    /// ...we found multiple sources of information and couldn't resolve the ambiguity.
+    TooManyCandidates,
+
+    /// ...an error occurred matching `T : TraitRef`
+    TraitSelectionError(SelectionError<'tcx>),
+}
+
+#[derive(PartialEq, Eq, Debug)]
+enum ProjectionCandidate<'tcx> {
+    /// From a where-clause in the env or object type
+    ParamEnv(ty::PolyProjectionPredicate<'tcx>),
+
+    /// From the definition of `Trait` when you have something like
+    /// `<<A as Trait>::B as Trait2>::C`.
+    TraitDef(ty::PolyProjectionPredicate<'tcx>),
+
+    /// Bounds specified on an object type
+    Object(ty::PolyProjectionPredicate<'tcx>),
+
+    /// From an "impl" (or a "pseudo-impl" returned by select)
+    Select(Selection<'tcx>),
+}
+
+enum ProjectionCandidateSet<'tcx> {
+    None,
+    Single(ProjectionCandidate<'tcx>),
+    Ambiguous,
+    Error(SelectionError<'tcx>),
+}
+
+impl<'tcx> ProjectionCandidateSet<'tcx> {
+    fn mark_ambiguous(&mut self) {
+        *self = ProjectionCandidateSet::Ambiguous;
+    }
+
+    fn mark_error(&mut self, err: SelectionError<'tcx>) {
+        *self = ProjectionCandidateSet::Error(err);
+    }
+
+    // Returns true if the push was successful, or false if the candidate
+    // was discarded -- this could be because of ambiguity, or because
+    // a higher-priority candidate is already there.
+    fn push_candidate(&mut self, candidate: ProjectionCandidate<'tcx>) -> bool {
+        use self::ProjectionCandidate::*;
+        use self::ProjectionCandidateSet::*;
+
+        // This wacky variable is just used to try and
+        // make code readable and avoid confusing paths.
+        // It is assigned a "value" of `()` only on those
+        // paths in which we wish to convert `*self` to
+        // ambiguous (and return false, because the candidate
+        // was not used). On other paths, it is not assigned,
+        // and hence if those paths *could* reach the code that
+        // comes after the match, this fn would not compile.
+        let convert_to_ambiguous;
+
+        match self {
+            None => {
+                *self = Single(candidate);
+                return true;
+            }
+
+            Single(current) => {
+                // Duplicates can happen inside ParamEnv. In the case, we
+                // perform a lazy deduplication.
+                if current == &candidate {
+                    return false;
+                }
+
+                // Prefer where-clauses. As in select, if there are multiple
+                // candidates, we prefer where-clause candidates over impls. This
+                // may seem a bit surprising, since impls are the source of
+                // "truth" in some sense, but in fact some of the impls that SEEM
+                // applicable are not, because of nested obligations. Where
+                // clauses are the safer choice. See the comment on
+                // `select::SelectionCandidate` and #21974 for more details.
+                match (current, candidate) {
+                    (ParamEnv(..), ParamEnv(..)) => convert_to_ambiguous = (),
+                    (ParamEnv(..), _) => return false,
+                    (_, ParamEnv(..)) => bug!(
+                        "should never prefer non-param-env candidates over param-env candidates"
+                    ),
+                    (_, _) => convert_to_ambiguous = (),
+                }
+            }
+
+            Ambiguous | Error(..) => {
+                return false;
+            }
+        }
+
+        // We only ever get here when we moved from a single candidate
+        // to ambiguous.
+        let () = convert_to_ambiguous;
+        *self = Ambiguous;
+        false
+    }
+}
+
+/// States returned from `poly_project_and_unify_type`. Takes the place
+/// of the old return type, which was:
+/// ```ignore (not-rust)
+/// Result<
+///     Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
+///     MismatchedProjectionTypes<'tcx>,
+/// >
+/// ```
+pub(super) enum ProjectAndUnifyResult<'tcx> {
+    /// The projection bound holds subject to the given obligations. If the
+    /// projection cannot be normalized because the required trait bound does
+    /// not hold, this is returned, with `obligations` being a predicate that
+    /// cannot be proven.
+    Holds(Vec<PredicateObligation<'tcx>>),
+    /// The projection cannot be normalized due to ambiguity. Resolving some
+    /// inference variables in the projection may fix this.
+    FailedNormalization,
+    /// The project cannot be normalized because `poly_project_and_unify_type`
+    /// is called recursively while normalizing the same projection.
+    Recursive,
+    // the projection can be normalized, but is not equal to the expected type.
+    // Returns the type error that arose from the mismatch.
+    MismatchedProjectionTypes(MismatchedProjectionTypes<'tcx>),
+}
+
+/// Evaluates constraints of the form:
+/// ```ignore (not-rust)
+/// for<...> <T as Trait>::U == V
+/// ```
+/// If successful, this may result in additional obligations. Also returns
+/// the projection cache key used to track these additional obligations.
+#[instrument(level = "debug", skip(selcx))]
+pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &PolyProjectionObligation<'tcx>,
+) -> ProjectAndUnifyResult<'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 new_universe = infcx.universe();
+
+        let placeholder_obligation = obligation.with(infcx.tcx, placeholder_predicate);
+        match project_and_unify_type(selcx, &placeholder_obligation) {
+            ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e),
+            ProjectAndUnifyResult::Holds(obligations)
+                if old_universe != new_universe
+                    && selcx.tcx().features().generic_associated_types_extended =>
+            {
+                // If the `generic_associated_types_extended` feature is active, then we ignore any
+                // obligations references lifetimes from any universe greater than or equal to the
+                // universe just created. Otherwise, we can end up with something like `for<'a> I: 'a`,
+                // which isn't quite what we want. Ideally, we want either an implied
+                // `for<'a where I: 'a> I: 'a` or we want to "lazily" check these hold when we
+                // substitute concrete regions. There is design work to be done here; until then,
+                // however, this allows experimenting potential GAT features without running into
+                // well-formedness issues.
+                let new_obligations = obligations
+                    .into_iter()
+                    .filter(|obligation| {
+                        let mut visitor = MaxUniverse::new();
+                        obligation.predicate.visit_with(&mut visitor);
+                        visitor.max_universe() < new_universe
+                    })
+                    .collect();
+                Ok(ProjectAndUnifyResult::Holds(new_obligations))
+            }
+            other => Ok(other),
+        }
+    });
+
+    match r {
+        Ok(inner) => inner,
+        Err(err) => ProjectAndUnifyResult::MismatchedProjectionTypes(err),
+    }
+}
+
+/// Evaluates constraints of the form:
+/// ```ignore (not-rust)
+/// <T as Trait>::U == V
+/// ```
+/// If successful, this may result in additional obligations.
+///
+/// See [poly_project_and_unify_type] for an explanation of the return value.
+#[instrument(level = "debug", skip(selcx))]
+fn project_and_unify_type<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionObligation<'tcx>,
+) -> ProjectAndUnifyResult<'tcx> {
+    let mut obligations = vec![];
+
+    let infcx = selcx.infcx;
+    let normalized = match opt_normalize_projection_type(
+        selcx,
+        obligation.param_env,
+        obligation.predicate.projection_ty,
+        obligation.cause.clone(),
+        obligation.recursion_depth,
+        &mut obligations,
+    ) {
+        Ok(Some(n)) => n,
+        Ok(None) => return ProjectAndUnifyResult::FailedNormalization,
+        Err(InProgress) => return ProjectAndUnifyResult::Recursive,
+    };
+    debug!(?normalized, ?obligations, "project_and_unify_type result");
+    let actual = obligation.predicate.term;
+    // For an example where this is necessary see tests/ui/impl-trait/nested-return-type2.rs
+    // This allows users to omit re-mentioning all bounds on an associated type and just use an
+    // `impl Trait` for the assoc type to add more bounds.
+    let InferOk { value: actual, obligations: new } =
+        selcx.infcx.replace_opaque_types_with_inference_vars(
+            actual,
+            obligation.cause.body_id,
+            obligation.cause.span,
+            obligation.param_env,
+        );
+    obligations.extend(new);
+
+    // Need to define opaque types to support nested opaque types like `impl Fn() -> impl Trait`
+    match infcx.at(&obligation.cause, obligation.param_env).eq(
+        DefineOpaqueTypes::Yes,
+        normalized,
+        actual,
+    ) {
+        Ok(InferOk { obligations: inferred_obligations, value: () }) => {
+            obligations.extend(inferred_obligations);
+            ProjectAndUnifyResult::Holds(obligations)
+        }
+        Err(err) => {
+            debug!("equating types encountered error {:?}", err);
+            ProjectAndUnifyResult::MismatchedProjectionTypes(MismatchedProjectionTypes { err })
+        }
+    }
+}
+
+/// As `normalize`, but with a custom depth.
+pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    value: T,
+) -> Normalized<'tcx, T>
+where
+    T: TypeFoldable<TyCtxt<'tcx>>,
+{
+    let mut obligations = Vec::new();
+    let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations);
+    Normalized { value, obligations }
+}
+
+#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
+pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    value: T,
+    obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> T
+where
+    T: TypeFoldable<TyCtxt<'tcx>>,
+{
+    debug!(obligations.len = obligations.len());
+    let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations);
+    let result = ensure_sufficient_stack(|| normalizer.fold(value));
+    debug!(?result, obligations.len = normalizer.obligations.len());
+    debug!(?normalizer.obligations,);
+    result
+}
+
+#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
+pub(crate) fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    value: T,
+    obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> T
+where
+    T: TypeFoldable<TyCtxt<'tcx>>,
+{
+    debug!(obligations.len = obligations.len());
+    let mut normalizer = AssocTypeNormalizer::new_without_eager_inference_replacement(
+        selcx,
+        param_env,
+        cause,
+        depth,
+        obligations,
+    );
+    let result = ensure_sufficient_stack(|| normalizer.fold(value));
+    debug!(?result, obligations.len = normalizer.obligations.len());
+    debug!(?normalizer.obligations,);
+    result
+}
+
+pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
+    value: &T,
+    reveal: Reveal,
+) -> bool {
+    match reveal {
+        Reveal::UserFacing => value.has_type_flags(
+            ty::TypeFlags::HAS_TY_PROJECTION
+                | ty::TypeFlags::HAS_TY_INHERENT
+                | ty::TypeFlags::HAS_CT_PROJECTION,
+        ),
+        Reveal::All => value.has_type_flags(
+            ty::TypeFlags::HAS_TY_PROJECTION
+                | ty::TypeFlags::HAS_TY_INHERENT
+                | ty::TypeFlags::HAS_TY_OPAQUE
+                | ty::TypeFlags::HAS_CT_PROJECTION,
+        ),
+    }
+}
+
+struct AssocTypeNormalizer<'a, 'b, 'tcx> {
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+    depth: usize,
+    universes: Vec<Option<ty::UniverseIndex>>,
+    /// If true, when a projection is unable to be completed, an inference
+    /// variable will be created and an obligation registered to project to that
+    /// inference variable. Also, constants will be eagerly evaluated.
+    eager_inference_replacement: bool,
+}
+
+impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
+    fn new(
+        selcx: &'a mut SelectionContext<'b, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: ObligationCause<'tcx>,
+        depth: usize,
+        obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+    ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
+        debug_assert!(!selcx.infcx.next_trait_solver());
+        AssocTypeNormalizer {
+            selcx,
+            param_env,
+            cause,
+            obligations,
+            depth,
+            universes: vec![],
+            eager_inference_replacement: true,
+        }
+    }
+
+    fn new_without_eager_inference_replacement(
+        selcx: &'a mut SelectionContext<'b, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: ObligationCause<'tcx>,
+        depth: usize,
+        obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+    ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
+        AssocTypeNormalizer {
+            selcx,
+            param_env,
+            cause,
+            obligations,
+            depth,
+            universes: vec![],
+            eager_inference_replacement: false,
+        }
+    }
+
+    fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T {
+        let value = self.selcx.infcx.resolve_vars_if_possible(value);
+        debug!(?value);
+
+        assert!(
+            !value.has_escaping_bound_vars(),
+            "Normalizing {value:?} without wrapping in a `Binder`"
+        );
+
+        if !needs_normalization(&value, self.param_env.reveal()) {
+            value
+        } else {
+            value.fold_with(self)
+        }
+    }
+}
+
+impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.selcx.tcx()
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.universes.push(None);
+        let t = t.super_fold_with(self);
+        self.universes.pop();
+        t
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if !needs_normalization(&ty, self.param_env.reveal()) {
+            return ty;
+        }
+
+        let (kind, data) = match *ty.kind() {
+            ty::Alias(kind, alias_ty) => (kind, alias_ty),
+            _ => return ty.super_fold_with(self),
+        };
+
+        // We try to be a little clever here as a performance optimization in
+        // cases where there are nested projections under binders.
+        // For example:
+        // ```
+        // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>)
+        // ```
+        // We normalize the args on the projection before the projecting, but
+        // if we're naive, we'll
+        //   replace bound vars on inner, project inner, replace placeholders on inner,
+        //   replace bound vars on outer, project outer, replace placeholders on outer
+        //
+        // However, if we're a bit more clever, we can replace the bound vars
+        // on the entire type before normalizing nested projections, meaning we
+        //   replace bound vars on outer, project inner,
+        //   project outer, replace placeholders on outer
+        //
+        // This is possible because the inner `'a` will already be a placeholder
+        // when we need to normalize the inner projection
+        //
+        // On the other hand, this does add a bit of complexity, since we only
+        // replace bound vars if the current type is a `Projection` and we need
+        // to make sure we don't forget to fold the args regardless.
+
+        match kind {
+            ty::Opaque => {
+                // Only normalize `impl Trait` outside of type inference, usually in codegen.
+                match self.param_env.reveal() {
+                    Reveal::UserFacing => ty.super_fold_with(self),
+
+                    Reveal::All => {
+                        let recursion_limit = self.interner().recursion_limit();
+                        if !recursion_limit.value_within_limit(self.depth) {
+                            self.selcx.infcx.err_ctxt().report_overflow_error(
+                                &ty,
+                                self.cause.span,
+                                true,
+                                |_| {},
+                            );
+                        }
+
+                        let args = data.args.fold_with(self);
+                        let generic_ty = self.interner().type_of(data.def_id);
+                        let concrete_ty = generic_ty.instantiate(self.interner(), args);
+                        self.depth += 1;
+                        let folded_ty = self.fold_ty(concrete_ty);
+                        self.depth -= 1;
+                        folded_ty
+                    }
+                }
+            }
+
+            ty::Projection if !data.has_escaping_bound_vars() => {
+                // This branch is *mostly* just an optimization: when we don't
+                // have escaping bound vars, we don't need to replace them with
+                // placeholders (see branch below). *Also*, we know that we can
+                // register an obligation to *later* project, since we know
+                // there won't be bound vars there.
+                let data = data.fold_with(self);
+                let normalized_ty = if self.eager_inference_replacement {
+                    normalize_projection_type(
+                        self.selcx,
+                        self.param_env,
+                        data,
+                        self.cause.clone(),
+                        self.depth,
+                        self.obligations,
+                    )
+                } else {
+                    opt_normalize_projection_type(
+                        self.selcx,
+                        self.param_env,
+                        data,
+                        self.cause.clone(),
+                        self.depth,
+                        self.obligations,
+                    )
+                    .ok()
+                    .flatten()
+                    .unwrap_or_else(|| ty.super_fold_with(self).into())
+                };
+                debug!(
+                    ?self.depth,
+                    ?ty,
+                    ?normalized_ty,
+                    obligations.len = ?self.obligations.len(),
+                    "AssocTypeNormalizer: normalized type"
+                );
+                normalized_ty.ty().unwrap()
+            }
+
+            ty::Projection => {
+                // If there are escaping bound vars, we temporarily replace the
+                // bound vars with placeholders. Note though, that in the case
+                // that we still can't project for whatever reason (e.g. self
+                // type isn't known enough), we *can't* register an obligation
+                // and return an inference variable (since then that obligation
+                // would have bound vars and that's a can of worms). Instead,
+                // we just give up and fall back to pretending like we never tried!
+                //
+                // Note: this isn't necessarily the final approach here; we may
+                // want to figure out how to register obligations with escaping vars
+                // or handle this some other way.
+
+                let infcx = self.selcx.infcx;
+                let (data, mapped_regions, mapped_types, mapped_consts) =
+                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+                let data = data.fold_with(self);
+                let normalized_ty = opt_normalize_projection_type(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                )
+                .ok()
+                .flatten()
+                .map(|term| term.ty().unwrap())
+                .map(|normalized_ty| {
+                    PlaceholderReplacer::replace_placeholders(
+                        infcx,
+                        mapped_regions,
+                        mapped_types,
+                        mapped_consts,
+                        &self.universes,
+                        normalized_ty,
+                    )
+                })
+                .unwrap_or_else(|| ty.super_fold_with(self));
+
+                debug!(
+                    ?self.depth,
+                    ?ty,
+                    ?normalized_ty,
+                    obligations.len = ?self.obligations.len(),
+                    "AssocTypeNormalizer: normalized type"
+                );
+                normalized_ty
+            }
+            ty::Weak => {
+                let recursion_limit = self.interner().recursion_limit();
+                if !recursion_limit.value_within_limit(self.depth) {
+                    self.selcx.infcx.err_ctxt().report_overflow_error(
+                        &ty,
+                        self.cause.span,
+                        false,
+                        |diag| {
+                            diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow);
+                        },
+                    );
+                }
+
+                let infcx = self.selcx.infcx;
+                self.obligations.extend(
+                    infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map(
+                        |(mut predicate, span)| {
+                            if data.has_escaping_bound_vars() {
+                                (predicate, ..) = BoundVarReplacer::replace_bound_vars(
+                                    infcx,
+                                    &mut self.universes,
+                                    predicate,
+                                );
+                            }
+                            let mut cause = self.cause.clone();
+                            cause.map_code(|code| {
+                                ObligationCauseCode::TypeAlias(code, span, data.def_id)
+                            });
+                            Obligation::new(infcx.tcx, cause, self.param_env, predicate)
+                        },
+                    ),
+                );
+                self.depth += 1;
+                let res = infcx
+                    .tcx
+                    .type_of(data.def_id)
+                    .instantiate(infcx.tcx, data.args)
+                    .fold_with(self);
+                self.depth -= 1;
+                res
+            }
+
+            ty::Inherent if !data.has_escaping_bound_vars() => {
+                // This branch is *mostly* just an optimization: when we don't
+                // have escaping bound vars, we don't need to replace them with
+                // placeholders (see branch below). *Also*, we know that we can
+                // register an obligation to *later* project, since we know
+                // there won't be bound vars there.
+
+                let data = data.fold_with(self);
+
+                // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement`
+                // here like `ty::Projection`?
+                normalize_inherent_projection(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                )
+            }
+
+            ty::Inherent => {
+                let infcx = self.selcx.infcx;
+                let (data, mapped_regions, mapped_types, mapped_consts) =
+                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+                let data = data.fold_with(self);
+                let ty = normalize_inherent_projection(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                );
+
+                PlaceholderReplacer::replace_placeholders(
+                    infcx,
+                    mapped_regions,
+                    mapped_types,
+                    mapped_consts,
+                    &self.universes,
+                    ty,
+                )
+            }
+        }
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        let tcx = self.selcx.tcx();
+        if tcx.features().generic_const_exprs
+            || !needs_normalization(&constant, self.param_env.reveal())
+        {
+            constant
+        } else {
+            let constant = constant.super_fold_with(self);
+            debug!(?constant, ?self.param_env);
+            with_replaced_escaping_bound_vars(
+                self.selcx.infcx,
+                &mut self.universes,
+                constant,
+                |constant| constant.normalize(tcx, self.param_env),
+            )
+        }
+    }
+
+    #[inline]
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
+            p.super_fold_with(self)
+        } else {
+            p
+        }
+    }
+}
+
+pub struct BoundVarReplacer<'me, 'tcx> {
+    infcx: &'me InferCtxt<'tcx>,
+    // These three maps track the bound variable that were replaced by placeholders. It might be
+    // nice to remove these since we already have the `kind` in the placeholder; we really just need
+    // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
+    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    // The current depth relative to *this* folding, *not* the entire normalization. In other words,
+    // the depth of binders we've passed here.
+    current_index: ty::DebruijnIndex,
+    // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
+    // we don't actually create a universe until we see a bound var we have to replace.
+    universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+}
+
+/// Executes `f` on `value` after replacing all escaping bound variables with placeholders
+/// and then replaces these placeholders with the original bound variables in the result.
+///
+/// In most places, bound variables should be replaced right when entering a binder, making
+/// this function unnecessary. However, normalization currently does not do that, so we have
+/// to do this lazily.
+///
+/// You should not add any additional uses of this function, at least not without first
+/// discussing it with t-types.
+///
+/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during
+/// normalization as well, at which point this function will be unnecessary and can be removed.
+pub fn with_replaced_escaping_bound_vars<
+    'a,
+    'tcx,
+    T: TypeFoldable<TyCtxt<'tcx>>,
+    R: TypeFoldable<TyCtxt<'tcx>>,
+>(
+    infcx: &'a InferCtxt<'tcx>,
+    universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
+    value: T,
+    f: impl FnOnce(T) -> R,
+) -> R {
+    if value.has_escaping_bound_vars() {
+        let (value, mapped_regions, mapped_types, mapped_consts) =
+            BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value);
+        let result = f(value);
+        PlaceholderReplacer::replace_placeholders(
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            universe_indices,
+            result,
+        )
+    } else {
+        f(value)
+    }
+}
+
+impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
+    /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
+    /// use a binding level above `universe_indices.len()`, we fail.
+    pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
+        infcx: &'me InferCtxt<'tcx>,
+        universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+        value: T,
+    ) -> (
+        T,
+        BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+        BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+        BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    ) {
+        let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new();
+        let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new();
+        let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new();
+
+        let mut replacer = BoundVarReplacer {
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            current_index: ty::INNERMOST,
+            universe_indices,
+        };
+
+        let value = value.fold_with(&mut replacer);
+
+        (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
+    }
+
+    fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
+        let infcx = self.infcx;
+        let index =
+            self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1;
+        let universe = self.universe_indices[index].unwrap_or_else(|| {
+            for i in self.universe_indices.iter_mut().take(index + 1) {
+                *i = i.or_else(|| Some(infcx.create_next_universe()))
+            }
+            self.universe_indices[index].unwrap()
+        });
+        universe
+    }
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReBound(debruijn, _)
+                if debruijn.as_usize()
+                    >= self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!(
+                    "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderRegion { universe, bound: br };
+                self.mapped_regions.insert(p, br);
+                ty::Region::new_placeholder(self.infcx.tcx, p)
+            }
+            _ => r,
+        }
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match *t.kind() {
+            ty::Bound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!(
+                    "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderType { universe, bound: bound_ty };
+                self.mapped_types.insert(p, bound_ty);
+                Ty::new_placeholder(self.infcx.tcx, p)
+            }
+            _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
+            _ => t,
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        match ct.kind() {
+            ty::ConstKind::Bound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!(
+                    "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderConst { universe, bound: bound_const };
+                self.mapped_consts.insert(p, bound_const);
+                ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty())
+            }
+            _ => ct.super_fold_with(self),
+        }
+    }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
+}
+
+/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
+pub struct PlaceholderReplacer<'me, 'tcx> {
+    infcx: &'me InferCtxt<'tcx>,
+    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    universe_indices: &'me [Option<ty::UniverseIndex>],
+    current_index: ty::DebruijnIndex,
+}
+
+impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
+    pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>(
+        infcx: &'me InferCtxt<'tcx>,
+        mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+        mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+        mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+        universe_indices: &'me [Option<ty::UniverseIndex>],
+        value: T,
+    ) -> T {
+        let mut replacer = PlaceholderReplacer {
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            universe_indices,
+            current_index: ty::INNERMOST,
+        };
+        value.fold_with(&mut replacer)
+    }
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        if !t.has_placeholders() && !t.has_infer() {
+            return t;
+        }
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        let r1 = match *r0 {
+            ty::ReVar(vid) => self
+                .infcx
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
+                .opportunistic_resolve_var(self.infcx.tcx, vid),
+            _ => r0,
+        };
+
+        let r2 = match *r1 {
+            ty::RePlaceholder(p) => {
+                let replace_var = self.mapped_regions.get(&p);
+                match replace_var {
+                    Some(replace_var) => {
+                        let index = self
+                            .universe_indices
+                            .iter()
+                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                        let db = ty::DebruijnIndex::from_usize(
+                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                        );
+                        ty::Region::new_bound(self.interner(), db, *replace_var)
+                    }
+                    None => r1,
+                }
+            }
+            _ => r1,
+        };
+
+        debug!(?r0, ?r1, ?r2, "fold_region");
+
+        r2
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let ty = self.infcx.shallow_resolve(ty);
+        match *ty.kind() {
+            ty::Placeholder(p) => {
+                let replace_var = self.mapped_types.get(&p);
+                match replace_var {
+                    Some(replace_var) => {
+                        let index = self
+                            .universe_indices
+                            .iter()
+                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                        let db = ty::DebruijnIndex::from_usize(
+                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                        );
+                        Ty::new_bound(self.infcx.tcx, db, *replace_var)
+                    }
+                    None => {
+                        if ty.has_infer() {
+                            ty.super_fold_with(self)
+                        } else {
+                            ty
+                        }
+                    }
+                }
+            }
+
+            _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self),
+            _ => ty,
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        let ct = self.infcx.shallow_resolve(ct);
+        if let ty::ConstKind::Placeholder(p) = ct.kind() {
+            let replace_var = self.mapped_consts.get(&p);
+            match replace_var {
+                Some(replace_var) => {
+                    let index = self
+                        .universe_indices
+                        .iter()
+                        .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                        .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                    let db = ty::DebruijnIndex::from_usize(
+                        self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                    );
+                    ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty())
+                }
+                None => {
+                    if ct.has_infer() {
+                        ct.super_fold_with(self)
+                    } else {
+                        ct
+                    }
+                }
+            }
+        } else {
+            ct.super_fold_with(self)
+        }
+    }
+}
+
+/// The guts of `normalize`: normalize a specific projection like `<T
+/// as Trait>::Item`. The result is always a type (and possibly
+/// additional obligations). If ambiguity arises, which implies that
+/// there are unresolved type variables in the projection, we will
+/// substitute a fresh type variable `$X` and generate a new
+/// obligation `<T as Trait>::Item == $X` for later.
+pub fn normalize_projection_type<'a, 'b, 'tcx>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    projection_ty: ty::AliasTy<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> Term<'tcx> {
+    opt_normalize_projection_type(
+        selcx,
+        param_env,
+        projection_ty,
+        cause.clone(),
+        depth,
+        obligations,
+    )
+    .ok()
+    .flatten()
+    .unwrap_or_else(move || {
+        // if we bottom out in ambiguity, create a type variable
+        // and a deferred predicate to resolve this when more type
+        // information is available.
+
+        selcx.infcx.infer_projection(param_env, projection_ty, cause, depth + 1, obligations).into()
+    })
+}
+
+/// The guts of `normalize`: normalize a specific projection like `<T
+/// as Trait>::Item`. The result is always a type (and possibly
+/// additional obligations). Returns `None` in the case of ambiguity,
+/// which indicates that there are unbound type variables.
+///
+/// This function used to return `Option<NormalizedTy<'tcx>>`, which contains a
+/// `Ty<'tcx>` and an obligations vector. But that obligation vector was very
+/// often immediately appended to another obligations vector. So now this
+/// function takes an obligations vector and appends to it directly, which is
+/// slightly uglier but avoids the need for an extra short-lived allocation.
+#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
+fn opt_normalize_projection_type<'a, 'b, 'tcx>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    projection_ty: ty::AliasTy<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> Result<Option<Term<'tcx>>, InProgress> {
+    let infcx = selcx.infcx;
+    debug_assert!(!selcx.infcx.next_trait_solver());
+    // Don't use the projection cache in intercrate mode -
+    // the `infcx` may be re-used between intercrate in non-intercrate
+    // mode, which could lead to using incorrect cache results.
+    let use_cache = !selcx.is_intercrate();
+
+    let projection_ty = infcx.resolve_vars_if_possible(projection_ty);
+    let cache_key = ProjectionCacheKey::new(projection_ty);
+
+    // FIXME(#20304) For now, I am caching here, which is good, but it
+    // means we don't capture the type variables that are created in
+    // the case of ambiguity. Which means we may create a large stream
+    // of such variables. OTOH, if we move the caching up a level, we
+    // would not benefit from caching when proving `T: Trait<U=Foo>`
+    // bounds. It might be the case that we want two distinct caches,
+    // or else another kind of cache entry.
+
+    let cache_result = if use_cache {
+        infcx.inner.borrow_mut().projection_cache().try_start(cache_key)
+    } else {
+        Ok(())
+    };
+    match cache_result {
+        Ok(()) => debug!("no cache"),
+        Err(ProjectionCacheEntry::Ambiguous) => {
+            // If we found ambiguity the last time, that means we will continue
+            // to do so until some type in the key changes (and we know it
+            // hasn't, because we just fully resolved it).
+            debug!("found cache entry: ambiguous");
+            return Ok(None);
+        }
+        Err(ProjectionCacheEntry::InProgress) => {
+            // Under lazy normalization, this can arise when
+            // bootstrapping. That is, imagine an environment with a
+            // where-clause like `A::B == u32`. Now, if we are asked
+            // to normalize `A::B`, we will want to check the
+            // where-clauses in scope. So we will try to unify `A::B`
+            // with `A::B`, which can trigger a recursive
+            // normalization.
+
+            debug!("found cache entry: in-progress");
+
+            // Cache that normalizing this projection resulted in a cycle. This
+            // should ensure that, unless this happens within a snapshot that's
+            // rolled back, fulfillment or evaluation will notice the cycle.
+
+            if use_cache {
+                infcx.inner.borrow_mut().projection_cache().recur(cache_key);
+            }
+            return Err(InProgress);
+        }
+        Err(ProjectionCacheEntry::Recur) => {
+            debug!("recur cache");
+            return Err(InProgress);
+        }
+        Err(ProjectionCacheEntry::NormalizedTy { ty, complete: _ }) => {
+            // This is the hottest path in this function.
+            //
+            // If we find the value in the cache, then return it along
+            // with the obligations that went along with it. Note
+            // that, when using a fulfillment context, these
+            // obligations could in principle be ignored: they have
+            // already been registered when the cache entry was
+            // created (and hence the new ones will quickly be
+            // discarded as duplicated). But when doing trait
+            // evaluation this is not the case, and dropping the trait
+            // evaluations can causes ICEs (e.g., #43132).
+            debug!(?ty, "found normalized ty");
+            obligations.extend(ty.obligations);
+            return Ok(Some(ty.value));
+        }
+        Err(ProjectionCacheEntry::Error) => {
+            debug!("opt_normalize_projection_type: found error");
+            let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
+            obligations.extend(result.obligations);
+            return Ok(Some(result.value.into()));
+        }
+    }
+
+    let obligation =
+        Obligation::with_depth(selcx.tcx(), cause.clone(), depth, param_env, projection_ty);
+
+    match project(selcx, &obligation) {
+        Ok(Projected::Progress(Progress {
+            term: projected_term,
+            obligations: mut projected_obligations,
+        })) => {
+            // if projection succeeded, then what we get out of this
+            // is also non-normalized (consider: it was derived from
+            // an impl, where-clause etc) and hence we must
+            // re-normalize it
+
+            let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term);
+
+            let mut result = if projected_term.has_projections() {
+                let mut normalizer = AssocTypeNormalizer::new(
+                    selcx,
+                    param_env,
+                    cause,
+                    depth + 1,
+                    &mut projected_obligations,
+                );
+                let normalized_ty = normalizer.fold(projected_term);
+
+                Normalized { value: normalized_ty, obligations: projected_obligations }
+            } else {
+                Normalized { value: projected_term, obligations: projected_obligations }
+            };
+
+            let mut deduped = SsoHashSet::with_capacity(result.obligations.len());
+            result.obligations.retain(|obligation| deduped.insert(obligation.clone()));
+
+            if use_cache {
+                infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone());
+            }
+            obligations.extend(result.obligations);
+            Ok(Some(result.value))
+        }
+        Ok(Projected::NoProgress(projected_ty)) => {
+            let result = Normalized { value: projected_ty, obligations: vec![] };
+            if use_cache {
+                infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone());
+            }
+            // No need to extend `obligations`.
+            Ok(Some(result.value))
+        }
+        Err(ProjectionError::TooManyCandidates) => {
+            debug!("opt_normalize_projection_type: too many candidates");
+            if use_cache {
+                infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key);
+            }
+            Ok(None)
+        }
+        Err(ProjectionError::TraitSelectionError(_)) => {
+            debug!("opt_normalize_projection_type: ERROR");
+            // if we got an error processing the `T as Trait` part,
+            // just return `ty::err` but add the obligation `T :
+            // Trait`, which when processed will cause the error to be
+            // reported later
+
+            if use_cache {
+                infcx.inner.borrow_mut().projection_cache().error(cache_key);
+            }
+            let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
+            obligations.extend(result.obligations);
+            Ok(Some(result.value.into()))
+        }
+    }
+}
+
+/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
+/// hold. In various error cases, we cannot generate a valid
+/// normalized projection. Therefore, we create an inference variable
+/// return an associated obligation that, when fulfilled, will lead to
+/// an error.
+///
+/// Note that we used to return `Error` here, but that was quite
+/// dubious -- the premise was that an error would *eventually* be
+/// reported, when the obligation was processed. But in general once
+/// you see an `Error` you are supposed to be able to assume that an
+/// error *has been* reported, so that you can take whatever heuristic
+/// paths you want to take. To make things worse, it was possible for
+/// cycles to arise, where you basically had a setup like `<MyType<$0>
+/// as Trait>::Foo == $0`. Here, normalizing `<MyType<$0> as
+/// Trait>::Foo>` to `[type error]` would lead to an obligation of
+/// `<MyType<[type error]> as Trait>::Foo`. We are supposed to report
+/// an error for this obligation, but we legitimately should not,
+/// because it contains `[type error]`. Yuck! (See issue #29857 for
+/// one case where this arose.)
+fn normalize_to_error<'a, 'tcx>(
+    selcx: &mut SelectionContext<'a, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    projection_ty: ty::AliasTy<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+) -> NormalizedTy<'tcx> {
+    let trait_ref = ty::Binder::dummy(projection_ty.trait_ref(selcx.tcx()));
+    let trait_obligation = Obligation {
+        cause,
+        recursion_depth: depth,
+        param_env,
+        predicate: trait_ref.to_predicate(selcx.tcx()),
+    };
+    let tcx = selcx.infcx.tcx;
+    let new_value = selcx.infcx.next_ty_var(TypeVariableOrigin {
+        kind: TypeVariableOriginKind::NormalizeProjectionType,
+        span: tcx.def_span(projection_ty.def_id),
+    });
+    Normalized { value: new_value, obligations: vec![trait_obligation] }
+}
+
+/// Confirm and normalize the given inherent projection.
+#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
+pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    alias_ty: ty::AliasTy<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> Ty<'tcx> {
+    let tcx = selcx.tcx();
+
+    if !tcx.recursion_limit().value_within_limit(depth) {
+        // Halt compilation because it is important that overflows never be masked.
+        tcx.dcx().emit_fatal(InherentProjectionNormalizationOverflow {
+            span: cause.span,
+            ty: alias_ty.to_string(),
+        });
+    }
+
+    let args = compute_inherent_assoc_ty_args(
+        selcx,
+        param_env,
+        alias_ty,
+        cause.clone(),
+        depth,
+        obligations,
+    );
+
+    // Register the obligations arising from the impl and from the associated type itself.
+    let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, args);
+    for (predicate, span) in predicates {
+        let predicate = normalize_with_depth_to(
+            selcx,
+            param_env,
+            cause.clone(),
+            depth + 1,
+            predicate,
+            obligations,
+        );
+
+        let nested_cause = ObligationCause::new(
+            cause.span,
+            cause.body_id,
+            // FIXME(inherent_associated_types): Since we can't pass along the self type to the
+            // cause code, inherent projections will be printed with identity substitutions in
+            // diagnostics which is not ideal.
+            // Consider creating separate cause codes for this specific situation.
+            if span.is_dummy() {
+                super::ItemObligation(alias_ty.def_id)
+            } else {
+                super::BindingObligation(alias_ty.def_id, span)
+            },
+        );
+
+        obligations.push(Obligation::with_depth(
+            tcx,
+            nested_cause,
+            depth + 1,
+            param_env,
+            predicate,
+        ));
+    }
+
+    let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args);
+
+    let mut ty = selcx.infcx.resolve_vars_if_possible(ty);
+    if ty.has_projections() {
+        ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations);
+    }
+
+    ty
+}
+
+pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    alias_ty: ty::AliasTy<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> ty::GenericArgsRef<'tcx> {
+    let tcx = selcx.tcx();
+
+    let impl_def_id = tcx.parent(alias_ty.def_id);
+    let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id);
+
+    let mut impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args);
+    if !selcx.infcx.next_trait_solver() {
+        impl_ty = normalize_with_depth_to(
+            selcx,
+            param_env,
+            cause.clone(),
+            depth + 1,
+            impl_ty,
+            obligations,
+        );
+    }
+
+    // Infer the generic parameters of the impl by unifying the
+    // impl type with the self type of the projection.
+    let mut self_ty = alias_ty.self_ty();
+    if !selcx.infcx.next_trait_solver() {
+        self_ty = normalize_with_depth_to(
+            selcx,
+            param_env,
+            cause.clone(),
+            depth + 1,
+            self_ty,
+            obligations,
+        );
+    }
+
+    match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) {
+        Ok(mut ok) => obligations.append(&mut ok.obligations),
+        Err(_) => {
+            tcx.dcx().span_delayed_bug(
+                cause.span,
+                format!(
+                    "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not"
+                ),
+            );
+        }
+    }
+
+    alias_ty.rebase_inherent_args_onto_impl(impl_args, tcx)
+}
+
+enum Projected<'tcx> {
+    Progress(Progress<'tcx>),
+    NoProgress(ty::Term<'tcx>),
+}
+
+struct Progress<'tcx> {
+    term: ty::Term<'tcx>,
+    obligations: Vec<PredicateObligation<'tcx>>,
+}
+
+impl<'tcx> Progress<'tcx> {
+    fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
+        Progress { term: Ty::new_error(tcx, guar).into(), obligations: vec![] }
+    }
+
+    fn with_addl_obligations(mut self, mut obligations: Vec<PredicateObligation<'tcx>>) -> Self {
+        self.obligations.append(&mut obligations);
+        self
+    }
+}
+
+/// Computes the result of a projection type (if we can).
+///
+/// IMPORTANT:
+/// - `obligation` must be fully normalized
+#[instrument(level = "info", skip(selcx))]
+fn project<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
+    if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) {
+        // This should really be an immediate error, but some existing code
+        // relies on being able to recover from this.
+        return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow(
+            OverflowError::Canonical,
+        )));
+    }
+
+    if let Err(guar) = obligation.predicate.error_reported() {
+        return Ok(Projected::Progress(Progress::error(selcx.tcx(), guar)));
+    }
+
+    let mut candidates = ProjectionCandidateSet::None;
+
+    // Make sure that the following procedures are kept in order. ParamEnv
+    // needs to be first because it has highest priority, and Select checks
+    // the return value of push_candidate which assumes it's ran at last.
+    assemble_candidates_from_param_env(selcx, obligation, &mut candidates);
+
+    assemble_candidates_from_trait_def(selcx, obligation, &mut candidates);
+
+    assemble_candidates_from_object_ty(selcx, obligation, &mut candidates);
+
+    if let ProjectionCandidateSet::Single(ProjectionCandidate::Object(_)) = candidates {
+        // Avoid normalization cycle from selection (see
+        // `assemble_candidates_from_object_ty`).
+        // FIXME(lazy_normalization): Lazy normalization should save us from
+        // having to special case this.
+    } else {
+        assemble_candidates_from_impls(selcx, obligation, &mut candidates);
+    };
+
+    match candidates {
+        ProjectionCandidateSet::Single(candidate) => {
+            Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate)))
+        }
+        ProjectionCandidateSet::None => {
+            let tcx = selcx.tcx();
+            let term = match tcx.def_kind(obligation.predicate.def_id) {
+                DefKind::AssocTy => {
+                    Ty::new_projection(tcx, obligation.predicate.def_id, obligation.predicate.args)
+                        .into()
+                }
+                DefKind::AssocConst => ty::Const::new_unevaluated(
+                    tcx,
+                    ty::UnevaluatedConst::new(
+                        obligation.predicate.def_id,
+                        obligation.predicate.args,
+                    ),
+                    tcx.type_of(obligation.predicate.def_id)
+                        .instantiate(tcx, obligation.predicate.args),
+                )
+                .into(),
+                kind => {
+                    bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id))
+                }
+            };
+
+            Ok(Projected::NoProgress(term))
+        }
+        // Error occurred while trying to processing impls.
+        ProjectionCandidateSet::Error(e) => Err(ProjectionError::TraitSelectionError(e)),
+        // Inherent ambiguity that prevents us from even enumerating the
+        // candidates.
+        ProjectionCandidateSet::Ambiguous => Err(ProjectionError::TooManyCandidates),
+    }
+}
+
+/// The first thing we have to do is scan through the parameter
+/// environment to see whether there are any projection predicates
+/// there that can answer this question.
+fn assemble_candidates_from_param_env<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    candidate_set: &mut ProjectionCandidateSet<'tcx>,
+) {
+    assemble_candidates_from_predicates(
+        selcx,
+        obligation,
+        candidate_set,
+        ProjectionCandidate::ParamEnv,
+        obligation.param_env.caller_bounds().iter(),
+        false,
+    );
+}
+
+/// In the case of a nested projection like `<<A as Foo>::FooT as Bar>::BarT`, we may find
+/// that the definition of `Foo` has some clues:
+///
+/// ```ignore (illustrative)
+/// trait Foo {
+///     type FooT : Bar<BarT=i32>
+/// }
+/// ```
+///
+/// Here, for example, we could conclude that the result is `i32`.
+fn assemble_candidates_from_trait_def<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    candidate_set: &mut ProjectionCandidateSet<'tcx>,
+) {
+    debug!("assemble_candidates_from_trait_def(..)");
+
+    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,
+    };
+
+    assemble_candidates_from_predicates(
+        selcx,
+        obligation,
+        candidate_set,
+        ProjectionCandidate::TraitDef,
+        bounds.iter(),
+        true,
+    );
+}
+
+/// In the case of a trait object like
+/// `<dyn Iterator<Item = ()> as Iterator>::Item` we can use the existential
+/// predicate in the trait object.
+///
+/// We don't go through the select candidate for these bounds to avoid cycles:
+/// In the above case, `dyn Iterator<Item = ()>: Iterator` would create a
+/// nested obligation of `<dyn Iterator<Item = ()> as Iterator>::Item: Sized`,
+/// this then has to be normalized without having to prove
+/// `dyn Iterator<Item = ()>: Iterator` again.
+fn assemble_candidates_from_object_ty<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    candidate_set: &mut ProjectionCandidateSet<'tcx>,
+) {
+    debug!("assemble_candidates_from_object_ty(..)");
+
+    let tcx = selcx.tcx();
+
+    if !tcx.trait_def(obligation.predicate.trait_def_id(tcx)).implement_via_object {
+        return;
+    }
+
+    let self_ty = obligation.predicate.self_ty();
+    let object_ty = selcx.infcx.shallow_resolve(self_ty);
+    let data = match object_ty.kind() {
+        ty::Dynamic(data, ..) => data,
+        ty::Infer(ty::TyVar(_)) => {
+            // If the self-type is an inference variable, then it MAY wind up
+            // being an object type, so induce an ambiguity.
+            candidate_set.mark_ambiguous();
+            return;
+        }
+        _ => return,
+    };
+    let env_predicates = data
+        .projection_bounds()
+        .filter(|bound| bound.item_def_id() == obligation.predicate.def_id)
+        .map(|p| p.with_self_ty(tcx, object_ty).to_predicate(tcx));
+
+    assemble_candidates_from_predicates(
+        selcx,
+        obligation,
+        candidate_set,
+        ProjectionCandidate::Object,
+        env_predicates,
+        false,
+    );
+}
+
+#[instrument(
+    level = "debug",
+    skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates)
+)]
+fn assemble_candidates_from_predicates<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    candidate_set: &mut ProjectionCandidateSet<'tcx>,
+    ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionCandidate<'tcx>,
+    env_predicates: impl Iterator<Item = ty::Clause<'tcx>>,
+    potentially_unnormalized_candidates: bool,
+) {
+    let infcx = selcx.infcx;
+    for predicate in env_predicates {
+        let bound_predicate = predicate.kind();
+        if let ty::ClauseKind::Projection(data) = predicate.kind().skip_binder() {
+            let data = bound_predicate.rebind(data);
+            if data.projection_def_id() != obligation.predicate.def_id {
+                continue;
+            }
+
+            let is_match = infcx.probe(|_| {
+                selcx.match_projection_projections(
+                    obligation,
+                    data,
+                    potentially_unnormalized_candidates,
+                )
+            });
+
+            match is_match {
+                ProjectionMatchesProjection::Yes => {
+                    candidate_set.push_candidate(ctor(data));
+
+                    if potentially_unnormalized_candidates
+                        && !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;
+                    }
+                }
+                ProjectionMatchesProjection::Ambiguous => {
+                    candidate_set.mark_ambiguous();
+                }
+                ProjectionMatchesProjection::No => {}
+            }
+        }
+    }
+}
+
+#[instrument(level = "debug", skip(selcx, obligation, candidate_set))]
+fn assemble_candidates_from_impls<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    candidate_set: &mut ProjectionCandidateSet<'tcx>,
+) {
+    // If we are resolving `<T as TraitRef<...>>::Item == Type`,
+    // start out by selecting the predicate `T as TraitRef<...>`:
+    let trait_ref = obligation.predicate.trait_ref(selcx.tcx());
+    let trait_obligation = obligation.with(selcx.tcx(), trait_ref);
+    let _ = selcx.infcx.commit_if_ok(|_| {
+        let impl_source = match selcx.select(&trait_obligation) {
+            Ok(Some(impl_source)) => impl_source,
+            Ok(None) => {
+                candidate_set.mark_ambiguous();
+                return Err(());
+            }
+            Err(e) => {
+                debug!(error = ?e, "selection error");
+                candidate_set.mark_error(e);
+                return Err(());
+            }
+        };
+
+        let eligible = match &impl_source {
+            ImplSource::UserDefined(impl_data) => {
+                // We have to be careful when projecting out of an
+                // impl because of specialization. If we are not in
+                // codegen (i.e., projection mode is not "any"), and the
+                // impl's type is declared as default, then we disable
+                // projection (even if the trait ref is fully
+                // monomorphic). In the case where trait ref is not
+                // fully monomorphic (i.e., includes type parameters),
+                // this is because those type parameters may
+                // ultimately be bound to types from other crates that
+                // may have specialized impls we can't see. In the
+                // case where the trait ref IS fully monomorphic, this
+                // is a policy decision that we made in the RFC in
+                // order to preserve flexibility for the crate that
+                // defined the specializable impl to specialize later
+                // for existing types.
+                //
+                // In either case, we handle this by not adding a
+                // candidate for an impl if it contains a `default`
+                // type.
+                //
+                // NOTE: This should be kept in sync with the similar code in
+                // `rustc_ty_utils::instance::resolve_associated_item()`.
+                let node_item =
+                    specialization_graph::assoc_def(selcx.tcx(), impl_data.impl_def_id, obligation.predicate.def_id)
+                        .map_err(|ErrorGuaranteed { .. }| ())?;
+
+                if node_item.is_final() {
+                    // Non-specializable items are always projectable.
+                    true
+                } else {
+                    // Only reveal a specializable default if we're past type-checking
+                    // and the obligation is monomorphic, otherwise passes such as
+                    // transmute checking and polymorphic MIR optimizations could
+                    // get a result which isn't correct for all monomorphizations.
+                    if obligation.param_env.reveal() == Reveal::All {
+                        // NOTE(eddyb) inference variables can resolve to parameters, so
+                        // assume `poly_trait_ref` isn't monomorphic, if it contains any.
+                        let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(trait_ref);
+                        !poly_trait_ref.still_further_specializable()
+                    } else {
+                        debug!(
+                            assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id),
+                            ?obligation.predicate,
+                            "assemble_candidates_from_impls: not eligible due to default",
+                        );
+                        false
+                    }
+                }
+            }
+            ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
+                // While a builtin impl may be known to exist, the associated type may not yet
+                // be known. Any type with multiple potential associated types is therefore
+                // not eligible.
+                let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
+
+                let lang_items = selcx.tcx().lang_items();
+                if [
+                    lang_items.coroutine_trait(),
+                    lang_items.future_trait(),
+                    lang_items.iterator_trait(),
+                    lang_items.async_iterator_trait(),
+                    lang_items.fn_trait(),
+                    lang_items.fn_mut_trait(),
+                    lang_items.fn_once_trait(),
+                ].contains(&Some(trait_ref.def_id))
+                {
+                    true
+                }else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) {
+                    match self_ty.kind() {
+                        ty::Bool
+                        | ty::Char
+                        | ty::Int(_)
+                        | ty::Uint(_)
+                        | ty::Float(_)
+                        | ty::Adt(..)
+                        | ty::Foreign(_)
+                        | ty::Str
+                        | ty::Array(..)
+                        | ty::Slice(_)
+                        | ty::RawPtr(..)
+                        | ty::Ref(..)
+                        | ty::FnDef(..)
+                        | ty::FnPtr(..)
+                        | ty::Dynamic(..)
+                        | ty::Closure(..)
+                        | ty::Coroutine(..)
+                        | ty::CoroutineWitness(..)
+                        | ty::Never
+                        | ty::Tuple(..)
+                        // Integers and floats always have `u8` as their discriminant.
+                        | 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
+                        ty::Param(_)
+                        | ty::Alias(..)
+                        | ty::Bound(..)
+                        | ty::Placeholder(..)
+                        | ty::Infer(..)
+                        | ty::Error(_) => false,
+                    }
+                } else if lang_items.pointee_trait() == Some(trait_ref.def_id) {
+                    let tail = selcx.tcx().struct_tail_with_normalize(
+                        self_ty,
+                        |ty| {
+                            // We throw away any obligations we get from this, since we normalize
+                            // and confirm these obligations once again during confirmation
+                            normalize_with_depth(
+                                selcx,
+                                obligation.param_env,
+                                obligation.cause.clone(),
+                                obligation.recursion_depth + 1,
+                                ty,
+                            )
+                            .value
+                        },
+                        || {},
+                    );
+
+                    match tail.kind() {
+                        ty::Bool
+                        | ty::Char
+                        | ty::Int(_)
+                        | ty::Uint(_)
+                        | ty::Float(_)
+                        | ty::Str
+                        | ty::Array(..)
+                        | ty::Slice(_)
+                        | ty::RawPtr(..)
+                        | ty::Ref(..)
+                        | ty::FnDef(..)
+                        | ty::FnPtr(..)
+                        | ty::Dynamic(..)
+                        | ty::Closure(..)
+                        | ty::Coroutine(..)
+                        | ty::CoroutineWitness(..)
+                        | ty::Never
+                        // Extern types have unit metadata, according to RFC 2850
+                        | ty::Foreign(_)
+                        // If returned by `struct_tail_without_normalization` this is a unit struct
+                        // without any fields, or not a struct, and therefore is Sized.
+                        | ty::Adt(..)
+                        // If returned by `struct_tail_without_normalization` this is the empty tuple.
+                        | ty::Tuple(..)
+                        // 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
+                        ty::Param(_) | ty::Alias(..)
+                            if 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]),
+                                ),
+                            ) =>
+                        {
+                            true
+                        }
+
+                        // FIXME(compiler-errors): are Bound and Placeholder types ever known sized?
+                        ty::Param(_)
+                        | ty::Alias(..)
+                        | ty::Bound(..)
+                        | ty::Placeholder(..)
+                        | ty::Infer(..)
+                        | ty::Error(_) => {
+                            if tail.has_infer_types() {
+                                candidate_set.mark_ambiguous();
+                            }
+                            false
+                        }
+                    }
+                } else {
+                    bug!("unexpected builtin trait with associated type: {trait_ref:?}")
+                }
+            }
+            ImplSource::Param(..) => {
+                // This case tell us nothing about the value of an
+                // associated type. Consider:
+                //
+                // ```
+                // trait SomeTrait { type Foo; }
+                // fn foo<T:SomeTrait>(...) { }
+                // ```
+                //
+                // If the user writes `<T as SomeTrait>::Foo`, then the `T
+                // : SomeTrait` binding does not help us decide what the
+                // type `Foo` is (at least, not more specifically than
+                // what we already knew).
+                //
+                // But wait, you say! What about an example like this:
+                //
+                // ```
+                // fn bar<T:SomeTrait<Foo=usize>>(...) { ... }
+                // ```
+                //
+                // Doesn't the `T : SomeTrait<Foo=usize>` predicate help
+                // resolve `T::Foo`? And of course it does, but in fact
+                // that single predicate is desugared into two predicates
+                // in the compiler: a trait predicate (`T : SomeTrait`) and a
+                // projection. And the projection where clause is handled
+                // in `assemble_candidates_from_param_env`.
+                false
+            }
+            ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) => {
+                // Handled by the `Object` projection candidate. See
+                // `assemble_candidates_from_object_ty` for an explanation of
+                // why we special case object types.
+                false
+            }
+            ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
+            | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
+                // These traits have no associated types.
+                selcx.tcx().dcx().span_delayed_bug(
+                    obligation.cause.span,
+                    format!("Cannot project an associated type from `{impl_source:?}`"),
+                );
+                return Err(());
+            }
+        };
+
+        if eligible {
+            if candidate_set.push_candidate(ProjectionCandidate::Select(impl_source)) {
+                Ok(())
+            } else {
+                Err(())
+            }
+        } else {
+            Err(())
+        }
+    });
+}
+
+fn confirm_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    candidate: ProjectionCandidate<'tcx>,
+) -> Progress<'tcx> {
+    debug!(?obligation, ?candidate, "confirm_candidate");
+    let mut progress = match candidate {
+        ProjectionCandidate::ParamEnv(poly_projection)
+        | ProjectionCandidate::Object(poly_projection) => {
+            confirm_param_env_candidate(selcx, obligation, poly_projection, false)
+        }
+
+        ProjectionCandidate::TraitDef(poly_projection) => {
+            confirm_param_env_candidate(selcx, obligation, poly_projection, true)
+        }
+
+        ProjectionCandidate::Select(impl_source) => {
+            confirm_select_candidate(selcx, obligation, impl_source)
+        }
+    };
+
+    // When checking for cycle during evaluation, we compare predicates with
+    // "syntactic" equality. Since normalization generally introduces a type
+    // with new region variables, we need to resolve them to existing variables
+    // when possible for this to work. See `auto-trait-projection-recursion.rs`
+    // for a case where this matters.
+    if progress.term.has_infer_regions() {
+        progress.term = progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx));
+    }
+    progress
+}
+
+fn confirm_select_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    impl_source: Selection<'tcx>,
+) -> Progress<'tcx> {
+    match impl_source {
+        ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
+        ImplSource::Builtin(BuiltinImplSource::Misc, data) => {
+            let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx());
+            let lang_items = selcx.tcx().lang_items();
+            if lang_items.coroutine_trait() == Some(trait_def_id) {
+                confirm_coroutine_candidate(selcx, obligation, data)
+            } else if lang_items.future_trait() == Some(trait_def_id) {
+                confirm_future_candidate(selcx, obligation, data)
+            } else if lang_items.iterator_trait() == Some(trait_def_id) {
+                confirm_iterator_candidate(selcx, obligation, data)
+            } 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() {
+                    confirm_closure_candidate(selcx, obligation, data)
+                } else {
+                    confirm_fn_pointer_candidate(selcx, obligation, data)
+                }
+            } else {
+                confirm_builtin_candidate(selcx, obligation, data)
+            }
+        }
+        ImplSource::Builtin(BuiltinImplSource::Object { .. }, _)
+        | ImplSource::Param(..)
+        | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
+        | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
+            // we don't create Select candidates with this kind of resolution
+            span_bug!(
+                obligation.cause.span,
+                "Cannot project an associated type from `{:?}`",
+                impl_source
+            )
+        }
+    }
+}
+
+fn confirm_coroutine_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    nested: Vec<PredicateObligation<'tcx>>,
+) -> Progress<'tcx> {
+    let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
+    let ty::Coroutine(_, args) = self_ty.kind() else {
+        unreachable!(
+            "expected coroutine self type for built-in coroutine candidate, found {self_ty}"
+        )
+    };
+    let coroutine_sig = args.as_coroutine().sig();
+    let Normalized { value: coroutine_sig, obligations } = normalize_with_depth(
+        selcx,
+        obligation.param_env,
+        obligation.cause.clone(),
+        obligation.recursion_depth + 1,
+        coroutine_sig,
+    );
+
+    debug!(?obligation, ?coroutine_sig, ?obligations, "confirm_coroutine_candidate");
+
+    let tcx = selcx.tcx();
+
+    let coroutine_def_id = tcx.require_lang_item(LangItem::Coroutine, None);
+
+    let (trait_ref, yield_ty, return_ty) = super::util::coroutine_trait_ref_and_outputs(
+        tcx,
+        coroutine_def_id,
+        obligation.predicate.self_ty(),
+        coroutine_sig,
+    );
+
+    let name = tcx.associated_item(obligation.predicate.def_id).name;
+    let ty = if name == sym::Return {
+        return_ty
+    } else if name == sym::Yield {
+        yield_ty
+    } else {
+        span_bug!(
+            tcx.def_span(obligation.predicate.def_id),
+            "unexpected associated type: `Coroutine::{name}`"
+        );
+    };
+
+    let predicate = ty::ProjectionPredicate {
+        projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args),
+        term: ty.into(),
+    };
+
+    confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
+        .with_addl_obligations(nested)
+        .with_addl_obligations(obligations)
+}
+
+fn confirm_future_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    nested: Vec<PredicateObligation<'tcx>>,
+) -> Progress<'tcx> {
+    let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
+    let ty::Coroutine(_, args) = self_ty.kind() else {
+        unreachable!(
+            "expected coroutine self type for built-in async future candidate, found {self_ty}"
+        )
+    };
+    let coroutine_sig = args.as_coroutine().sig();
+    let Normalized { value: coroutine_sig, obligations } = normalize_with_depth(
+        selcx,
+        obligation.param_env,
+        obligation.cause.clone(),
+        obligation.recursion_depth + 1,
+        coroutine_sig,
+    );
+
+    debug!(?obligation, ?coroutine_sig, ?obligations, "confirm_future_candidate");
+
+    let tcx = selcx.tcx();
+    let fut_def_id = tcx.require_lang_item(LangItem::Future, None);
+
+    let (trait_ref, return_ty) = super::util::future_trait_ref_and_outputs(
+        tcx,
+        fut_def_id,
+        obligation.predicate.self_ty(),
+        coroutine_sig,
+    );
+
+    debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Output);
+
+    let predicate = ty::ProjectionPredicate {
+        projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args),
+        term: return_ty.into(),
+    };
+
+    confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
+        .with_addl_obligations(nested)
+        .with_addl_obligations(obligations)
+}
+
+fn confirm_iterator_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    nested: Vec<PredicateObligation<'tcx>>,
+) -> Progress<'tcx> {
+    let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
+    let ty::Coroutine(_, args) = self_ty.kind() else {
+        unreachable!("expected coroutine self type for built-in gen candidate, found {self_ty}")
+    };
+    let gen_sig = args.as_coroutine().sig();
+    let Normalized { value: gen_sig, obligations } = normalize_with_depth(
+        selcx,
+        obligation.param_env,
+        obligation.cause.clone(),
+        obligation.recursion_depth + 1,
+        gen_sig,
+    );
+
+    debug!(?obligation, ?gen_sig, ?obligations, "confirm_iterator_candidate");
+
+    let tcx = selcx.tcx();
+    let iter_def_id = tcx.require_lang_item(LangItem::Iterator, None);
+
+    let (trait_ref, yield_ty) = super::util::iterator_trait_ref_and_outputs(
+        tcx,
+        iter_def_id,
+        obligation.predicate.self_ty(),
+        gen_sig,
+    );
+
+    debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item);
+
+    let predicate = ty::ProjectionPredicate {
+        projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args),
+        term: yield_ty.into(),
+    };
+
+    confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
+        .with_addl_obligations(nested)
+        .with_addl_obligations(obligations)
+}
+
+fn confirm_async_iterator_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    nested: Vec<PredicateObligation<'tcx>>,
+) -> Progress<'tcx> {
+    let ty::Coroutine(_, args) = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
+    else {
+        unreachable!()
+    };
+    let gen_sig = args.as_coroutine().sig();
+    let Normalized { value: gen_sig, obligations } = normalize_with_depth(
+        selcx,
+        obligation.param_env,
+        obligation.cause.clone(),
+        obligation.recursion_depth + 1,
+        gen_sig,
+    );
+
+    debug!(?obligation, ?gen_sig, ?obligations, "confirm_async_iterator_candidate");
+
+    let tcx = selcx.tcx();
+    let iter_def_id = tcx.require_lang_item(LangItem::AsyncIterator, None);
+
+    let (trait_ref, yield_ty) = super::util::async_iterator_trait_ref_and_outputs(
+        tcx,
+        iter_def_id,
+        obligation.predicate.self_ty(),
+        gen_sig,
+    );
+
+    debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item);
+
+    let ty::Adt(_poll_adt, args) = *yield_ty.kind() else {
+        bug!();
+    };
+    let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else {
+        bug!();
+    };
+    let item_ty = args.type_at(0);
+
+    let predicate = ty::ProjectionPredicate {
+        projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args),
+        term: item_ty.into(),
+    };
+
+    confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
+        .with_addl_obligations(nested)
+        .with_addl_obligations(obligations)
+}
+
+fn confirm_builtin_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    data: Vec<PredicateObligation<'tcx>>,
+) -> Progress<'tcx> {
+    let tcx = selcx.tcx();
+    let self_ty = obligation.predicate.self_ty();
+    let args = tcx.mk_args(&[self_ty.into()]);
+    let lang_items = tcx.lang_items();
+    let item_def_id = obligation.predicate.def_id;
+    let trait_def_id = tcx.trait_of_item(item_def_id).unwrap();
+    let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
+        let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None);
+        assert_eq!(discriminant_def_id, item_def_id);
+
+        (self_ty.discriminant_ty(tcx).into(), Vec::new())
+    } else if lang_items.pointee_trait() == Some(trait_def_id) {
+        let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
+        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| {
+            normalize_with_depth_to(
+                selcx,
+                obligation.param_env,
+                obligation.cause.clone(),
+                obligation.recursion_depth + 1,
+                ty,
+                &mut obligations,
+            )
+        });
+        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);
+    };
+
+    let predicate =
+        ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, item_def_id, args), term };
+
+    confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
+        .with_addl_obligations(obligations)
+        .with_addl_obligations(data)
+}
+
+fn confirm_fn_pointer_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    nested: Vec<PredicateObligation<'tcx>>,
+) -> Progress<'tcx> {
+    let tcx = selcx.tcx();
+    let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
+    let sig = fn_type.fn_sig(tcx);
+    let Normalized { value: sig, obligations } = normalize_with_depth(
+        selcx,
+        obligation.param_env,
+        obligation.cause.clone(),
+        obligation.recursion_depth + 1,
+        sig,
+    );
+
+    let host_effect_param = match *fn_type.kind() {
+        ty::FnDef(def_id, args) => tcx
+            .generics_of(def_id)
+            .host_effect_index
+            .map_or(tcx.consts.true_, |idx| args.const_at(idx)),
+        ty::FnPtr(_) => tcx.consts.true_,
+        _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"),
+    };
+
+    confirm_callable_candidate(
+        selcx,
+        obligation,
+        sig,
+        util::TupleArgumentsFlag::Yes,
+        host_effect_param,
+    )
+    .with_addl_obligations(nested)
+    .with_addl_obligations(obligations)
+}
+
+fn confirm_closure_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    nested: Vec<PredicateObligation<'tcx>>,
+) -> Progress<'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 = args.as_closure().sig();
+    let Normalized { value: closure_sig, obligations } = normalize_with_depth(
+        selcx,
+        obligation.param_env,
+        obligation.cause.clone(),
+        obligation.recursion_depth + 1,
+        closure_sig,
+    );
+
+    debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate");
+
+    confirm_callable_candidate(
+        selcx,
+        obligation,
+        closure_sig,
+        util::TupleArgumentsFlag::No,
+        // FIXME(effects): This doesn't handle const closures correctly!
+        selcx.tcx().consts.true_,
+    )
+    .with_addl_obligations(nested)
+    .with_addl_obligations(obligations)
+}
+
+fn confirm_callable_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    fn_sig: ty::PolyFnSig<'tcx>,
+    flag: util::TupleArgumentsFlag,
+    fn_host_effect: ty::Const<'tcx>,
+) -> Progress<'tcx> {
+    let tcx = selcx.tcx();
+
+    debug!(?obligation, ?fn_sig, "confirm_callable_candidate");
+
+    let fn_once_def_id = tcx.require_lang_item(LangItem::FnOnce, None);
+    let fn_once_output_def_id = tcx.require_lang_item(LangItem::FnOnceOutput, None);
+
+    let predicate = super::util::closure_trait_ref_and_return_type(
+        tcx,
+        fn_once_def_id,
+        obligation.predicate.self_ty(),
+        fn_sig,
+        flag,
+        fn_host_effect,
+    )
+    .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate {
+        projection_ty: ty::AliasTy::new(tcx, fn_once_output_def_id, trait_ref.args),
+        term: ret_type.into(),
+    });
+
+    confirm_param_env_candidate(selcx, obligation, predicate, true)
+}
+
+fn confirm_param_env_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    poly_cache_entry: ty::PolyProjectionPredicate<'tcx>,
+    potentially_unnormalized_candidate: bool,
+) -> Progress<'tcx> {
+    let infcx = selcx.infcx;
+    let cause = &obligation.cause;
+    let param_env = obligation.param_env;
+
+    let cache_entry = infcx.instantiate_binder_with_fresh_vars(
+        cause.span,
+        BoundRegionConversionTime::HigherRankedType,
+        poly_cache_entry,
+    );
+
+    let cache_projection = cache_entry.projection_ty;
+    let mut nested_obligations = Vec::new();
+    let obligation_projection = obligation.predicate;
+    let obligation_projection = ensure_sufficient_stack(|| {
+        normalize_with_depth_to(
+            selcx,
+            obligation.param_env,
+            obligation.cause.clone(),
+            obligation.recursion_depth + 1,
+            obligation_projection,
+            &mut nested_obligations,
+        )
+    });
+    let cache_projection = if potentially_unnormalized_candidate {
+        ensure_sufficient_stack(|| {
+            normalize_with_depth_to(
+                selcx,
+                obligation.param_env,
+                obligation.cause.clone(),
+                obligation.recursion_depth + 1,
+                cache_projection,
+                &mut nested_obligations,
+            )
+        })
+    } else {
+        cache_projection
+    };
+
+    debug!(?cache_projection, ?obligation_projection);
+
+    match infcx.at(cause, param_env).eq(
+        DefineOpaqueTypes::No,
+        cache_projection,
+        obligation_projection,
+    ) {
+        Ok(InferOk { value: _, obligations }) => {
+            nested_obligations.extend(obligations);
+            assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations);
+            // FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take
+            // a term instead.
+            Progress { term: cache_entry.term, obligations: nested_obligations }
+        }
+        Err(e) => {
+            let msg = format!(
+                "Failed to unify obligation `{obligation:?}` with poly_projection `{poly_cache_entry:?}`: {e:?}",
+            );
+            debug!("confirm_param_env_candidate: {}", msg);
+            let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg);
+            Progress { term: err.into(), obligations: vec![] }
+        }
+    }
+}
+
+fn confirm_impl_candidate<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>,
+) -> Progress<'tcx> {
+    let tcx = selcx.tcx();
+
+    let ImplSourceUserDefinedData { impl_def_id, args, mut nested } = impl_impl_source;
+    let assoc_item_id = obligation.predicate.def_id;
+    let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
+
+    let param_env = obligation.param_env;
+    let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
+        Ok(assoc_ty) => assoc_ty,
+        Err(guar) => return Progress::error(tcx, guar),
+    };
+
+    if !assoc_ty.item.defaultness(tcx).has_value() {
+        // This means that the impl is missing a definition for the
+        // associated type. This error will be reported by the type
+        // checker method `check_impl_items_against_trait`, so here we
+        // just return Error.
+        debug!(
+            "confirm_impl_candidate: no associated type {:?} for {:?}",
+            assoc_ty.item.name, obligation.predicate
+        );
+        return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested };
+    }
+    // If we're trying to normalize `<Vec<u32> as X>::A<S>` using
+    //`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
+    //
+    // * `obligation.predicate.args` is `[Vec<u32>, S]`
+    // * `args` is `[u32]`
+    // * `args` ends up as `[u32, S]`
+    let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args);
+    let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node);
+    let ty = tcx.type_of(assoc_ty.item.def_id);
+    let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
+    let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
+        let did = assoc_ty.item.def_id;
+        let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did);
+        let uv = ty::UnevaluatedConst::new(did, identity_args);
+        ty.map_bound(|ty| ty::Const::new_unevaluated(tcx, uv, ty).into())
+    } else {
+        ty.map_bound(|ty| ty.into())
+    };
+    if !check_args_compatible(tcx, assoc_ty.item, args) {
+        let err = Ty::new_error_with_message(
+            tcx,
+            obligation.cause.span,
+            "impl item and trait item have different parameters",
+        );
+        Progress { term: err.into(), obligations: nested }
+    } else {
+        assoc_ty_own_obligations(selcx, obligation, &mut nested);
+        Progress { term: term.instantiate(tcx, args), obligations: nested }
+    }
+}
+
+// Get obligations corresponding to the predicates from the where-clause of the
+// associated type itself.
+fn assoc_ty_own_obligations<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    nested: &mut Vec<PredicateObligation<'tcx>>,
+) {
+    let tcx = selcx.tcx();
+    let predicates = tcx
+        .predicates_of(obligation.predicate.def_id)
+        .instantiate_own(tcx, obligation.predicate.args);
+    for (predicate, span) in predicates {
+        let normalized = normalize_with_depth_to(
+            selcx,
+            obligation.param_env,
+            obligation.cause.clone(),
+            obligation.recursion_depth + 1,
+            predicate,
+            nested,
+        );
+
+        let nested_cause = if matches!(
+            obligation.cause.code(),
+            super::CompareImplItemObligation { .. }
+                | super::CheckAssociatedTypeBounds { .. }
+                | super::AscribeUserTypeProvePredicate(..)
+        ) {
+            obligation.cause.clone()
+        } else if span.is_dummy() {
+            ObligationCause::new(
+                obligation.cause.span,
+                obligation.cause.body_id,
+                super::ItemObligation(obligation.predicate.def_id),
+            )
+        } else {
+            ObligationCause::new(
+                obligation.cause.span,
+                obligation.cause.body_id,
+                super::BindingObligation(obligation.predicate.def_id, span),
+            )
+        };
+        nested.push(Obligation::with_depth(
+            tcx,
+            nested_cause,
+            obligation.recursion_depth + 1,
+            obligation.param_env,
+            normalized,
+        ));
+    }
+}
+
+pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized {
+    fn from_poly_projection_predicate(
+        selcx: &mut SelectionContext<'cx, 'tcx>,
+        predicate: ty::PolyProjectionPredicate<'tcx>,
+    ) -> Option<Self>;
+}
+
+impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> {
+    fn from_poly_projection_predicate(
+        selcx: &mut SelectionContext<'cx, 'tcx>,
+        predicate: ty::PolyProjectionPredicate<'tcx>,
+    ) -> Option<Self> {
+        let infcx = selcx.infcx;
+        // We don't do cross-snapshot caching of obligations with escaping regions,
+        // so there's no cache key to use
+        predicate.no_bound_vars().map(|predicate| {
+            ProjectionCacheKey::new(
+                // We don't attempt to match up with a specific type-variable state
+                // from a specific call to `opt_normalize_projection_type` - if
+                // there's no precise match, the original cache entry is "stranded"
+                // anyway.
+                infcx.resolve_vars_if_possible(predicate.projection_ty),
+            )
+        })
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
new file mode 100644
index 00000000000..138bc6129f7
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -0,0 +1,340 @@
+use crate::traits::query::normalize::QueryNormalizeExt;
+use crate::traits::query::NoSolution;
+use crate::traits::{Normalized, ObligationCause, ObligationCtxt};
+
+use rustc_data_structures::fx::FxHashSet;
+use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult};
+use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt};
+use rustc_span::{Span, DUMMY_SP};
+
+/// This returns true if the type `ty` is "trivial" for
+/// dropck-outlives -- that is, if it doesn't require any types to
+/// outlive. This is similar but not *quite* the same as the
+/// `needs_drop` test in the compiler already -- that is, for every
+/// type T for which this function return true, needs-drop would
+/// return `false`. But the reverse does not hold: in particular,
+/// `needs_drop` returns false for `PhantomData`, but it is not
+/// trivial for dropck-outlives.
+///
+/// Note also that `needs_drop` requires a "global" type (i.e., one
+/// with erased regions), but this function does not.
+///
+// FIXME(@lcnr): remove this module and move this function somewhere else.
+pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.kind() {
+        // None of these types have a destructor and hence they do not
+        // require anything in particular to outlive the dtor's
+        // execution.
+        ty::Infer(ty::FreshIntTy(_))
+        | ty::Infer(ty::FreshFloatTy(_))
+        | ty::Bool
+        | ty::Int(_)
+        | ty::Uint(_)
+        | ty::Float(_)
+        | ty::Never
+        | ty::FnDef(..)
+        | ty::FnPtr(_)
+        | ty::Char
+        | ty::CoroutineWitness(..)
+        | ty::RawPtr(_)
+        | ty::Ref(..)
+        | ty::Str
+        | ty::Foreign(..)
+        | ty::Error(_) => true,
+
+        // [T; N] and [T] have same properties as T.
+        ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty),
+
+        // (T1..Tn) and closures have same properties as T1..Tn --
+        // check if *all* of them are trivial.
+        ty::Tuple(tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t)),
+        ty::Closure(_, args) => trivial_dropck_outlives(tcx, args.as_closure().tupled_upvars_ty()),
+
+        ty::Adt(def, _) => {
+            if Some(def.did()) == tcx.lang_items().manually_drop() {
+                // `ManuallyDrop` never has a dtor.
+                true
+            } else {
+                // Other types might. Moreover, PhantomData doesn't
+                // have a dtor, but it is considered to own its
+                // content, so it is non-trivial. Unions can have `impl Drop`,
+                // and hence are non-trivial as well.
+                false
+            }
+        }
+
+        // The following *might* require a destructor: needs deeper inspection.
+        ty::Dynamic(..)
+        | ty::Alias(..)
+        | ty::Param(_)
+        | ty::Placeholder(..)
+        | ty::Infer(_)
+        | ty::Bound(..)
+        | ty::Coroutine(..) => false,
+    }
+}
+
+pub fn compute_dropck_outlives_inner<'tcx>(
+    ocx: &ObligationCtxt<'_, 'tcx>,
+    goal: ParamEnvAnd<'tcx, Ty<'tcx>>,
+) -> Result<DropckOutlivesResult<'tcx>, NoSolution> {
+    let tcx = ocx.infcx.tcx;
+    let ParamEnvAnd { param_env, value: for_ty } = goal;
+
+    let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] };
+
+    // A stack of types left to process. Each round, we pop
+    // something from the stack and invoke
+    // `dtorck_constraint_for_ty_inner`. This may produce new types that
+    // have to be pushed on the stack. This continues until we have explored
+    // all the reachable types from the type `for_ty`.
+    //
+    // Example: Imagine that we have the following code:
+    //
+    // ```rust
+    // struct A {
+    //     value: B,
+    //     children: Vec<A>,
+    // }
+    //
+    // struct B {
+    //     value: u32
+    // }
+    //
+    // fn f() {
+    //   let a: A = ...;
+    //   ..
+    // } // here, `a` is dropped
+    // ```
+    //
+    // at the point where `a` is dropped, we need to figure out
+    // which types inside of `a` contain region data that may be
+    // accessed by any destructors in `a`. We begin by pushing `A`
+    // onto the stack, as that is the type of `a`. We will then
+    // invoke `dtorck_constraint_for_ty_inner` which will expand `A`
+    // into the types of its fields `(B, Vec<A>)`. These will get
+    // pushed onto the stack. Eventually, expanding `Vec<A>` will
+    // lead to us trying to push `A` a second time -- to prevent
+    // infinite recursion, we notice that `A` was already pushed
+    // once and stop.
+    let mut ty_stack = vec![(for_ty, 0)];
+
+    // Set used to detect infinite recursion.
+    let mut ty_set = FxHashSet::default();
+
+    let cause = ObligationCause::dummy();
+    let mut constraints = DropckConstraint::empty();
+    while let Some((ty, depth)) = ty_stack.pop() {
+        debug!(
+            "{} kinds, {} overflows, {} ty_stack",
+            result.kinds.len(),
+            result.overflows.len(),
+            ty_stack.len()
+        );
+        dtorck_constraint_for_ty_inner(tcx, param_env, DUMMY_SP, depth, ty, &mut constraints)?;
+
+        // "outlives" represent types/regions that may be touched
+        // by a destructor.
+        result.kinds.append(&mut constraints.outlives);
+        result.overflows.append(&mut constraints.overflows);
+
+        // If we have even one overflow, we should stop trying to evaluate further --
+        // chances are, the subsequent overflows for this evaluation won't provide useful
+        // information and will just decrease the speed at which we can emit these errors
+        // (since we'll be printing for just that much longer for the often enormous types
+        // that result here).
+        if !result.overflows.is_empty() {
+            break;
+        }
+
+        // dtorck types are "types that will get dropped but which
+        // do not themselves define a destructor", more or less. We have
+        // to push them onto the stack to be expanded.
+        for ty in constraints.dtorck_types.drain(..) {
+            let Normalized { value: ty, obligations } =
+                ocx.infcx.at(&cause, param_env).query_normalize(ty)?;
+            ocx.register_obligations(obligations);
+
+            debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
+
+            match ty.kind() {
+                // All parameters live for the duration of the
+                // function.
+                ty::Param(..) => {}
+
+                // A projection that we couldn't resolve - it
+                // might have a destructor.
+                ty::Alias(..) => {
+                    result.kinds.push(ty.into());
+                }
+
+                _ => {
+                    if ty_set.insert(ty) {
+                        ty_stack.push((ty, depth + 1));
+                    }
+                }
+            }
+        }
+    }
+
+    debug!("dropck_outlives: result = {:#?}", result);
+    Ok(result)
+}
+
+/// Returns a set of constraints that needs to be satisfied in
+/// order for `ty` to be valid for destruction.
+#[instrument(level = "debug", skip(tcx, param_env, span, constraints))]
+pub fn dtorck_constraint_for_ty_inner<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    span: Span,
+    depth: usize,
+    ty: Ty<'tcx>,
+    constraints: &mut DropckConstraint<'tcx>,
+) -> Result<(), NoSolution> {
+    if !tcx.recursion_limit().value_within_limit(depth) {
+        constraints.overflows.push(ty);
+        return Ok(());
+    }
+
+    if trivial_dropck_outlives(tcx, ty) {
+        return Ok(());
+    }
+
+    match ty.kind() {
+        ty::Bool
+        | ty::Char
+        | ty::Int(_)
+        | ty::Uint(_)
+        | ty::Float(_)
+        | ty::Str
+        | ty::Never
+        | ty::Foreign(..)
+        | ty::RawPtr(..)
+        | ty::Ref(..)
+        | ty::FnDef(..)
+        | ty::FnPtr(_)
+        | ty::CoroutineWitness(..) => {
+            // these types never have a destructor
+        }
+
+        ty::Array(ety, _) | ty::Slice(ety) => {
+            // single-element containers, behave like their element
+            rustc_data_structures::stack::ensure_sufficient_stack(|| {
+                dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, *ety, constraints)
+            })?;
+        }
+
+        ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
+            for ty in tys.iter() {
+                dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, ty, constraints)?;
+            }
+            Ok::<_, NoSolution>(())
+        })?,
+
+        ty::Closure(_, args) => {
+            if !args.as_closure().is_valid() {
+                // By the time this code runs, all type variables ought to
+                // be fully resolved.
+
+                tcx.dcx().span_delayed_bug(
+                    span,
+                    format!("upvar_tys for closure not found. Expected capture information for closure {ty}",),
+                );
+                return Err(NoSolution);
+            }
+
+            rustc_data_structures::stack::ensure_sufficient_stack(|| {
+                for ty in args.as_closure().upvar_tys() {
+                    dtorck_constraint_for_ty_inner(
+                        tcx,
+                        param_env,
+                        span,
+                        depth + 1,
+                        ty,
+                        constraints,
+                    )?;
+                }
+                Ok::<_, NoSolution>(())
+            })?
+        }
+
+        ty::Coroutine(_, args) => {
+            // rust-lang/rust#49918: types can be constructed, stored
+            // in the interior, and sit idle when coroutine yields
+            // (and is subsequently dropped).
+            //
+            // It would be nice to descend into interior of a
+            // coroutine to determine what effects dropping it might
+            // have (by looking at any drop effects associated with
+            // its interior).
+            //
+            // However, the interior's representation uses things like
+            // CoroutineWitness that explicitly assume they are not
+            // traversed in such a manner. So instead, we will
+            // simplify things for now by treating all coroutines as
+            // if they were like trait objects, where its upvars must
+            // all be alive for the coroutine's (potential)
+            // destructor.
+            //
+            // In particular, skipping over `_interior` is safe
+            // because any side-effects from dropping `_interior` can
+            // only take place through references with lifetimes
+            // derived from lifetimes attached to the upvars and resume
+            // argument, and we *do* incorporate those here.
+            let args = args.as_coroutine();
+            if !args.is_valid() {
+                // By the time this code runs, all type variables ought to
+                // be fully resolved.
+                tcx.dcx().span_delayed_bug(
+                    span,
+                    format!("upvar_tys for coroutine not found. Expected capture information for coroutine {ty}",),
+                );
+                return Err(NoSolution);
+            }
+
+            // While we conservatively assume that all coroutines require drop
+            // to avoid query cycles during MIR building, we can check the actual
+            // witness during borrowck to avoid unnecessary liveness constraints.
+            if args.witness().needs_drop(tcx, tcx.erase_regions(param_env)) {
+                constraints.outlives.extend(args.upvar_tys().iter().map(ty::GenericArg::from));
+                constraints.outlives.push(args.resume_ty().into());
+            }
+        }
+
+        ty::Adt(def, args) => {
+            let DropckConstraint { dtorck_types, outlives, overflows } =
+                tcx.at(span).adt_dtorck_constraint(def.did())?;
+            // FIXME: we can try to recursively `dtorck_constraint_on_ty`
+            // there, but that needs some way to handle cycles.
+            constraints
+                .dtorck_types
+                .extend(dtorck_types.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args)));
+            constraints
+                .outlives
+                .extend(outlives.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args)));
+            constraints
+                .overflows
+                .extend(overflows.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args)));
+        }
+
+        // Objects must be alive in order for their destructor
+        // to be called.
+        ty::Dynamic(..) => {
+            constraints.outlives.push(ty.into());
+        }
+
+        // Types that can't be resolved. Pass them forward.
+        ty::Alias(..) | ty::Param(..) => {
+            constraints.dtorck_types.push(ty);
+        }
+
+        ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => {
+            // By the time this code runs, all type variables ought to
+            // be fully resolved.
+            return Err(NoSolution);
+        }
+    }
+
+    Ok(())
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
new file mode 100644
index 00000000000..d812d537d8c
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -0,0 +1,129 @@
+use rustc_infer::traits::{TraitEngine, TraitEngineExt};
+
+use crate::infer::canonical::OriginalQueryValues;
+use crate::infer::InferCtxt;
+use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext};
+
+pub trait InferCtxtExt<'tcx> {
+    fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool;
+
+    fn predicate_must_hold_considering_regions(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> bool;
+
+    fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'tcx>) -> bool;
+
+    fn evaluate_obligation(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> Result<EvaluationResult, OverflowError>;
+
+    // Helper function that canonicalizes and runs the query. If an
+    // overflow results, we re-run it in the local context so we can
+    // report a nice error.
+    /*crate*/
+    fn evaluate_obligation_no_overflow(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> EvaluationResult;
+}
+
+impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
+    /// Evaluates whether the predicate can be satisfied (by any means)
+    /// in the given `ParamEnv`.
+    fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool {
+        self.evaluate_obligation_no_overflow(obligation).may_apply()
+    }
+
+    /// Evaluates whether the predicate can be satisfied in the given
+    /// `ParamEnv`, and returns `false` if not certain. However, this is
+    /// not entirely accurate if inference variables are involved.
+    ///
+    /// This version may conservatively fail when outlives obligations
+    /// are required.
+    fn predicate_must_hold_considering_regions(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> bool {
+        self.evaluate_obligation_no_overflow(obligation).must_apply_considering_regions()
+    }
+
+    /// Evaluates whether the predicate can be satisfied in the given
+    /// `ParamEnv`, and returns `false` if not certain. However, this is
+    /// not entirely accurate if inference variables are involved.
+    ///
+    /// This version ignores all outlives constraints.
+    fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'tcx>) -> bool {
+        self.evaluate_obligation_no_overflow(obligation).must_apply_modulo_regions()
+    }
+
+    /// Evaluate a given predicate, capturing overflow and propagating it back.
+    fn evaluate_obligation(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        let mut _orig_values = OriginalQueryValues::default();
+
+        let param_env = obligation.param_env;
+
+        if self.next_trait_solver() {
+            self.probe(|snapshot| {
+                let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self);
+                fulfill_cx.register_predicate_obligation(self, obligation.clone());
+                // True errors
+                // FIXME(-Znext-solver): Overflows are reported as ambig here, is that OK?
+                if !fulfill_cx.select_where_possible(self).is_empty() {
+                    Ok(EvaluationResult::EvaluatedToErr)
+                } else if !fulfill_cx.select_all_or_error(self).is_empty() {
+                    Ok(EvaluationResult::EvaluatedToAmbig)
+                } else if self.opaque_types_added_in_snapshot(snapshot) {
+                    Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes)
+                } else if self.region_constraints_added_in_snapshot(snapshot) {
+                    Ok(EvaluationResult::EvaluatedToOkModuloRegions)
+                } else {
+                    Ok(EvaluationResult::EvaluatedToOk)
+                }
+            })
+        } else {
+            assert!(!self.intercrate);
+            let c_pred = self.canonicalize_query_keep_static(
+                param_env.and(obligation.predicate),
+                &mut _orig_values,
+            );
+            self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred)
+        }
+    }
+
+    // Helper function that canonicalizes and runs the query. If an
+    // overflow results, we re-run it in the local context so we can
+    // report a nice error.
+    fn evaluate_obligation_no_overflow(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> EvaluationResult {
+        // Run canonical query. If overflow occurs, rerun from scratch but this time
+        // in standard trait query mode so that overflow is handled appropriately
+        // within `SelectionContext`.
+        match self.evaluate_obligation(obligation) {
+            Ok(result) => result,
+            Err(OverflowError::Canonical) => {
+                let mut selcx = SelectionContext::new(self);
+                selcx.evaluate_root_obligation(obligation).unwrap_or_else(|r| match r {
+                    OverflowError::Canonical => {
+                        span_bug!(
+                            obligation.cause.span,
+                            "Overflow should be caught earlier in standard query mode: {:?}, {:?}",
+                            obligation,
+                            r,
+                        )
+                    }
+                    OverflowError::ErrorReporting => EvaluationResult::EvaluatedToErr,
+                    OverflowError::Error(_) => EvaluationResult::EvaluatedToErr,
+                })
+            }
+            Err(OverflowError::ErrorReporting) => EvaluationResult::EvaluatedToErr,
+            Err(OverflowError::Error(_)) => EvaluationResult::EvaluatedToErr,
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/method_autoderef.rs b/compiler/rustc_trait_selection/src/traits/query/method_autoderef.rs
new file mode 100644
index 00000000000..3c0ebec9335
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/method_autoderef.rs
@@ -0,0 +1,3 @@
+pub use rustc_middle::traits::query::{
+    CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult,
+};
diff --git a/compiler/rustc_trait_selection/src/traits/query/mod.rs b/compiler/rustc_trait_selection/src/traits/query/mod.rs
new file mode 100644
index 00000000000..ef349367813
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/mod.rs
@@ -0,0 +1,14 @@
+//! Experimental types for the trait query interface. The methods
+//! defined in this module are all based on **canonicalization**,
+//! which makes a canonical query by replacing unbound inference
+//! variables and regions, so that results can be reused more broadly.
+//! The providers for the queries defined here can be found in
+//! `rustc_traits`.
+
+pub mod dropck_outlives;
+pub mod evaluate_obligation;
+pub mod method_autoderef;
+pub mod normalize;
+pub mod type_op;
+
+pub use rustc_middle::traits::query::*;
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
new file mode 100644
index 00000000000..e8867187a40
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -0,0 +1,371 @@
+//! Code for the 'normalization' query. This consists of a wrapper
+//! which folds deeply, invoking the underlying
+//! `normalize_projection_ty` query when it encounters projections.
+
+use crate::infer::at::At;
+use crate::infer::canonical::OriginalQueryValues;
+use crate::infer::{InferCtxt, InferOk};
+use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+use crate::traits::{ObligationCause, PredicateObligation, Reveal};
+use rustc_data_structures::sso::SsoHashMap;
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_infer::traits::Normalized;
+use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
+use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
+use rustc_span::DUMMY_SP;
+
+use std::ops::ControlFlow;
+
+use super::NoSolution;
+
+pub use rustc_middle::traits::query::NormalizationResult;
+
+pub trait QueryNormalizeExt<'tcx> {
+    /// Normalize a value using the `QueryNormalizer`.
+    ///
+    /// This normalization should *only* be used when the projection does not
+    /// have possible ambiguity or may not be well-formed.
+    ///
+    /// After codegen, when lifetimes do not matter, it is preferable to instead
+    /// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
+    fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+    where
+        T: TypeFoldable<TyCtxt<'tcx>>;
+}
+
+impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
+    /// Normalize `value` in the context of the inference context,
+    /// yielding a resulting type, or an error if `value` cannot be
+    /// normalized. If you don't care about regions, you should prefer
+    /// `normalize_erasing_regions`, which is more efficient.
+    ///
+    /// If the normalization succeeds and is unambiguous, returns back
+    /// the normalized value along with various outlives relations (in
+    /// the form of obligations that must be discharged).
+    ///
+    /// N.B., this will *eventually* be the main means of
+    /// normalizing, but for now should be used only when we actually
+    /// know that normalization will succeed, since error reporting
+    /// and other details are still "under development".
+    fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+    where
+        T: TypeFoldable<TyCtxt<'tcx>>,
+    {
+        debug!(
+            "normalize::<{}>(value={:?}, param_env={:?}, cause={:?})",
+            std::any::type_name::<T>(),
+            value,
+            self.param_env,
+            self.cause,
+        );
+
+        // This is actually a consequence by the way `normalize_erasing_regions` works currently.
+        // Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
+        // through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
+        // with trying to normalize with escaping bound vars.
+        //
+        // Here, we just add the universes that we *would* have created had we passed through the binders.
+        //
+        // We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
+        // The rest of the code is already set up to be lazy about replacing bound vars,
+        // and only when we actually have to normalize.
+        let universes = if value.has_escaping_bound_vars() {
+            let mut max_visitor =
+                MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
+            value.visit_with(&mut max_visitor);
+            vec![None; max_visitor.escaping]
+        } else {
+            vec![]
+        };
+
+        if self.infcx.next_trait_solver() {
+            match crate::solve::deeply_normalize_with_skipped_universes(self, value, universes) {
+                Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
+                Err(_errors) => {
+                    return Err(NoSolution);
+                }
+            }
+        }
+
+        if !needs_normalization(&value, self.param_env.reveal()) {
+            return Ok(Normalized { value, obligations: vec![] });
+        }
+
+        let mut normalizer = QueryNormalizer {
+            infcx: self.infcx,
+            cause: self.cause,
+            param_env: self.param_env,
+            obligations: vec![],
+            cache: SsoHashMap::new(),
+            anon_depth: 0,
+            universes,
+        };
+
+        let result = value.try_fold_with(&mut normalizer);
+        info!(
+            "normalize::<{}>: result={:?} with {} obligations",
+            std::any::type_name::<T>(),
+            result,
+            normalizer.obligations.len(),
+        );
+        debug!(
+            "normalize::<{}>: obligations={:?}",
+            std::any::type_name::<T>(),
+            normalizer.obligations,
+        );
+        result.map(|value| Normalized { value, obligations: normalizer.obligations })
+    }
+}
+
+// Visitor to find the maximum escaping bound var
+struct MaxEscapingBoundVarVisitor {
+    // The index which would count as escaping
+    outer_index: ty::DebruijnIndex,
+    escaping: usize,
+}
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: &ty::Binder<'tcx, T>,
+    ) -> ControlFlow<Self::BreakTy> {
+        self.outer_index.shift_in(1);
+        let result = t.super_visit_with(self);
+        self.outer_index.shift_out(1);
+        result
+    }
+
+    #[inline]
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        if t.outer_exclusive_binder() > self.outer_index {
+            self.escaping = self
+                .escaping
+                .max(t.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
+        }
+        ControlFlow::Continue(())
+    }
+
+    #[inline]
+    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        match *r {
+            ty::ReBound(debruijn, _) if debruijn > self.outer_index => {
+                self.escaping =
+                    self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
+            }
+            _ => {}
+        }
+        ControlFlow::Continue(())
+    }
+
+    fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        if ct.outer_exclusive_binder() > self.outer_index {
+            self.escaping = self
+                .escaping
+                .max(ct.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
+        }
+        ControlFlow::Continue(())
+    }
+}
+
+struct QueryNormalizer<'cx, 'tcx> {
+    infcx: &'cx InferCtxt<'tcx>,
+    cause: &'cx ObligationCause<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    obligations: Vec<PredicateObligation<'tcx>>,
+    cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
+    anon_depth: usize,
+    universes: Vec<Option<ty::UniverseIndex>>,
+}
+
+impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> {
+    type Error = NoSolution;
+
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> Result<ty::Binder<'tcx, T>, Self::Error> {
+        self.universes.push(None);
+        let t = t.try_super_fold_with(self);
+        self.universes.pop();
+        t
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
+        if !needs_normalization(&ty, self.param_env.reveal()) {
+            return Ok(ty);
+        }
+
+        if let Some(ty) = self.cache.get(&ty) {
+            return Ok(*ty);
+        }
+
+        let (kind, data) = match *ty.kind() {
+            ty::Alias(kind, data) => (kind, data),
+            _ => {
+                let res = ty.try_super_fold_with(self)?;
+                self.cache.insert(ty, res);
+                return Ok(res);
+            }
+        };
+
+        // See note in `rustc_trait_selection::traits::project` about why we
+        // wait to fold the args.
+
+        // Wrap this in a closure so we don't accidentally return from the outer function
+        let res = match kind {
+            ty::Opaque => {
+                // Only normalize `impl Trait` outside of type inference, usually in codegen.
+                match self.param_env.reveal() {
+                    Reveal::UserFacing => ty.try_super_fold_with(self)?,
+
+                    Reveal::All => {
+                        let args = data.args.try_fold_with(self)?;
+                        let recursion_limit = self.interner().recursion_limit();
+
+                        if !recursion_limit.value_within_limit(self.anon_depth) {
+                            let guar = self
+                                .infcx
+                                .err_ctxt()
+                                .build_overflow_error(&ty, self.cause.span, true)
+                                .delay_as_bug();
+                            return Ok(Ty::new_error(self.interner(), guar));
+                        }
+
+                        let generic_ty = self.interner().type_of(data.def_id);
+                        let concrete_ty = generic_ty.instantiate(self.interner(), args);
+                        self.anon_depth += 1;
+                        if concrete_ty == ty {
+                            bug!(
+                                "infinite recursion generic_ty: {:#?}, args: {:#?}, \
+                                 concrete_ty: {:#?}, ty: {:#?}",
+                                generic_ty,
+                                args,
+                                concrete_ty,
+                                ty
+                            );
+                        }
+                        let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty));
+                        self.anon_depth -= 1;
+                        folded_ty?
+                    }
+                }
+            }
+
+            ty::Projection | ty::Inherent | ty::Weak => {
+                // See note in `rustc_trait_selection::traits::project`
+
+                let infcx = self.infcx;
+                let tcx = infcx.tcx;
+                // Just an optimization: When we don't have escaping bound vars,
+                // we don't need to replace them with placeholders.
+                let (data, maps) = if data.has_escaping_bound_vars() {
+                    let (data, mapped_regions, mapped_types, mapped_consts) =
+                        BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+                    (data, Some((mapped_regions, mapped_types, mapped_consts)))
+                } else {
+                    (data, None)
+                };
+                let data = data.try_fold_with(self)?;
+
+                let mut orig_values = OriginalQueryValues::default();
+                // HACK(matthewjasper) `'static` is special-cased in selection,
+                // so we cannot canonicalize it.
+                let c_data = infcx
+                    .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
+                debug!("QueryNormalizer: c_data = {:#?}", c_data);
+                debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
+                let result = match kind {
+                    ty::Projection => tcx.normalize_projection_ty(c_data),
+                    ty::Weak => tcx.normalize_weak_ty(c_data),
+                    ty::Inherent => tcx.normalize_inherent_projection_ty(c_data),
+                    kind => unreachable!("did not expect {kind:?} due to match arm above"),
+                }?;
+                // We don't expect ambiguity.
+                if !result.value.is_proven() {
+                    // Rustdoc normalizes possibly not well-formed types, so only
+                    // treat this as a bug if we're not in rustdoc.
+                    if !tcx.sess.opts.actually_rustdoc {
+                        tcx.dcx().span_delayed_bug(
+                            DUMMY_SP,
+                            format!("unexpected ambiguity: {c_data:?} {result:?}"),
+                        );
+                    }
+                    return Err(NoSolution);
+                }
+                let InferOk { value: result, obligations } = infcx
+                    .instantiate_query_response_and_region_obligations(
+                        self.cause,
+                        self.param_env,
+                        &orig_values,
+                        result,
+                    )?;
+                debug!("QueryNormalizer: result = {:#?}", result);
+                debug!("QueryNormalizer: obligations = {:#?}", obligations);
+                self.obligations.extend(obligations);
+                let res = if let Some((mapped_regions, mapped_types, mapped_consts)) = maps {
+                    PlaceholderReplacer::replace_placeholders(
+                        infcx,
+                        mapped_regions,
+                        mapped_types,
+                        mapped_consts,
+                        &self.universes,
+                        result.normalized_ty,
+                    )
+                } else {
+                    result.normalized_ty
+                };
+                // `tcx.normalize_projection_ty` may normalize to a type that still has
+                // unevaluated consts, so keep normalizing here if that's the case.
+                // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type
+                // and we need to continue folding it to reveal the TAIT behind it.
+                if res != ty
+                    && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak)
+                {
+                    res.try_fold_with(self)?
+                } else {
+                    res
+                }
+            }
+        };
+
+        self.cache.insert(ty, res);
+        Ok(res)
+    }
+
+    fn try_fold_const(
+        &mut self,
+        constant: ty::Const<'tcx>,
+    ) -> Result<ty::Const<'tcx>, Self::Error> {
+        if !needs_normalization(&constant, self.param_env.reveal()) {
+            return Ok(constant);
+        }
+
+        let constant = constant.try_super_fold_with(self)?;
+        debug!(?constant, ?self.param_env);
+        Ok(crate::traits::project::with_replaced_escaping_bound_vars(
+            self.infcx,
+            &mut self.universes,
+            constant,
+            |constant| constant.normalize(self.infcx.tcx, self.param_env),
+        ))
+    }
+
+    #[inline]
+    fn try_fold_predicate(
+        &mut self,
+        p: ty::Predicate<'tcx>,
+    ) -> Result<ty::Predicate<'tcx>, Self::Error> {
+        if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
+            p.try_super_fold_with(self)
+        } else {
+            Ok(p)
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
new file mode 100644
index 00000000000..152ceeee869
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
@@ -0,0 +1,143 @@
+use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
+use crate::traits::ObligationCtxt;
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
+use rustc_infer::traits::Obligation;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
+use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserArgs, UserSelfTy, UserType};
+
+pub use rustc_middle::traits::query::type_op::AscribeUserType;
+use rustc_span::{Span, DUMMY_SP};
+
+impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> {
+    type QueryResponse = ();
+
+    fn try_fast_path(
+        _tcx: TyCtxt<'tcx>,
+        _key: &ParamEnvAnd<'tcx, Self>,
+    ) -> Option<Self::QueryResponse> {
+        None
+    }
+
+    fn perform_query(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
+        tcx.type_op_ascribe_user_type(canonicalized)
+    }
+
+    fn perform_locally_with_next_solver(
+        ocx: &ObligationCtxt<'_, 'tcx>,
+        key: ParamEnvAnd<'tcx, Self>,
+    ) -> Result<Self::QueryResponse, NoSolution> {
+        type_op_ascribe_user_type_with_span(ocx, key, None)
+    }
+}
+
+/// The core of the `type_op_ascribe_user_type` query: for diagnostics purposes in NLL HRTB errors,
+/// this query can be re-run to better track the span of the obligation cause, and improve the error
+/// message. Do not call directly unless you're in that very specific context.
+pub fn type_op_ascribe_user_type_with_span<'tcx>(
+    ocx: &ObligationCtxt<'_, 'tcx>,
+    key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>,
+    span: Option<Span>,
+) -> Result<(), NoSolution> {
+    let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts();
+    debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty);
+    let span = span.unwrap_or(DUMMY_SP);
+    match user_ty {
+        UserType::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?,
+        UserType::TypeOf(def_id, user_args) => {
+            relate_mir_and_user_args(ocx, param_env, span, mir_ty, def_id, user_args)?
+        }
+    };
+    Ok(())
+}
+
+#[instrument(level = "debug", skip(ocx, param_env, span))]
+fn relate_mir_and_user_ty<'tcx>(
+    ocx: &ObligationCtxt<'_, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    span: Span,
+    mir_ty: Ty<'tcx>,
+    user_ty: Ty<'tcx>,
+) -> Result<(), NoSolution> {
+    let cause = ObligationCause::dummy_with_span(span);
+    let user_ty = ocx.normalize(&cause, param_env, user_ty);
+    ocx.eq(&cause, param_env, mir_ty, user_ty)?;
+
+    // FIXME(#104764): We should check well-formedness before normalization.
+    let predicate =
+        ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(user_ty.into())));
+    ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
+    Ok(())
+}
+
+#[instrument(level = "debug", skip(ocx, param_env, span))]
+fn relate_mir_and_user_args<'tcx>(
+    ocx: &ObligationCtxt<'_, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    span: Span,
+    mir_ty: Ty<'tcx>,
+    def_id: DefId,
+    user_args: UserArgs<'tcx>,
+) -> Result<(), NoSolution> {
+    let UserArgs { user_self_ty, args } = user_args;
+    let tcx = ocx.infcx.tcx;
+    let cause = ObligationCause::dummy_with_span(span);
+
+    let ty = tcx.type_of(def_id).instantiate(tcx, args);
+    let ty = ocx.normalize(&cause, param_env, ty);
+    debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
+
+    ocx.eq(&cause, param_env, mir_ty, ty)?;
+
+    // Prove the predicates coming along with `def_id`.
+    //
+    // Also, normalize the `instantiated_predicates`
+    // because otherwise we wind up with duplicate "type
+    // outlives" error messages.
+    let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args);
+
+    debug!(?instantiated_predicates);
+    for (instantiated_predicate, predicate_span) in instantiated_predicates {
+        let span = if span == DUMMY_SP { predicate_span } else { span };
+        let cause = ObligationCause::new(
+            span,
+            CRATE_DEF_ID,
+            ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span),
+        );
+        let instantiated_predicate =
+            ocx.normalize(&cause.clone(), param_env, instantiated_predicate);
+
+        ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate));
+    }
+
+    if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
+        let self_ty = ocx.normalize(&cause, param_env, self_ty);
+        let impl_self_ty = tcx.type_of(impl_def_id).instantiate(tcx, args);
+        let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
+
+        ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
+        let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+            impl_self_ty.into(),
+        )));
+        ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
+    }
+
+    // In addition to proving the predicates, we have to
+    // prove that `ty` is well-formed -- this is because
+    // the WF of `ty` is predicated on the args being
+    // well-formed, and we haven't proven *that*. We don't
+    // want to prove the WF of types from  `args` directly because they
+    // haven't been normalized.
+    //
+    // FIXME(nmatsakis): Well, perhaps we should normalize
+    // them?  This would only be relevant if some input
+    // type were ill-formed but did not appear in `ty`,
+    // which...could happen with normalization...
+    let predicate =
+        ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty.into())));
+    ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate));
+    Ok(())
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
new file mode 100644
index 00000000000..f4baae2711f
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
@@ -0,0 +1,127 @@
+use crate::infer::canonical::query_response;
+use crate::infer::InferCtxt;
+use crate::traits::query::type_op::TypeOpOutput;
+use crate::traits::ObligationCtxt;
+use rustc_errors::ErrorGuaranteed;
+use rustc_infer::infer::region_constraints::RegionConstraintData;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::ty::{TyCtxt, TypeFoldable};
+use rustc_span::{Span, DUMMY_SP};
+
+use std::fmt;
+
+pub struct CustomTypeOp<F> {
+    closure: F,
+    description: &'static str,
+}
+
+impl<F> CustomTypeOp<F> {
+    pub fn new<'tcx, R>(closure: F, description: &'static str) -> Self
+    where
+        F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
+    {
+        CustomTypeOp { closure, description }
+    }
+}
+
+impl<'tcx, F, R> super::TypeOp<'tcx> for CustomTypeOp<F>
+where
+    F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
+    R: fmt::Debug + TypeFoldable<TyCtxt<'tcx>>,
+{
+    type Output = R;
+    /// We can't do any custom error reporting for `CustomTypeOp`, so
+    /// we can use `!` to enforce that the implementation never provides it.
+    type ErrorInfo = !;
+
+    /// Processes the operation and all resulting obligations,
+    /// returning the final result along with any region constraints
+    /// (they will be given over to the NLL region solver).
+    fn fully_perform(
+        self,
+        infcx: &InferCtxt<'tcx>,
+        span: Span,
+    ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
+        if cfg!(debug_assertions) {
+            info!("fully_perform({:?})", self);
+        }
+
+        Ok(scrape_region_constraints(infcx, self.closure, self.description, span)?.0)
+    }
+}
+
+impl<F> fmt::Debug for CustomTypeOp<F> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.description.fmt(f)
+    }
+}
+
+/// Executes `op` and then scrapes out all the "old style" region
+/// constraints that result, creating query-region-constraints.
+pub fn scrape_region_constraints<'tcx, Op, R>(
+    infcx: &InferCtxt<'tcx>,
+    op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
+    name: &'static str,
+    span: Span,
+) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed>
+where
+    R: TypeFoldable<TyCtxt<'tcx>>,
+    Op: super::TypeOp<'tcx, Output = R>,
+{
+    // During NLL, we expect that nobody will register region
+    // obligations **except** as part of a custom type op (and, at the
+    // end of each custom type op, we scrape out the region
+    // obligations that resulted). So this vector should be empty on
+    // entry.
+    let pre_obligations = infcx.take_registered_region_obligations();
+    assert!(
+        pre_obligations.is_empty(),
+        "scrape_region_constraints: incoming region obligations = {pre_obligations:#?}",
+    );
+
+    let value = infcx.commit_if_ok(|_| {
+        let ocx = ObligationCtxt::new(infcx);
+        let value = op(&ocx).map_err(|_| {
+            infcx.dcx().span_delayed_bug(span, format!("error performing operation: {name}"))
+        })?;
+        let errors = ocx.select_all_or_error();
+        if errors.is_empty() {
+            Ok(value)
+        } else {
+            Err(infcx.dcx().span_delayed_bug(
+                DUMMY_SP,
+                format!("errors selecting obligation during MIR typeck: {errors:?}"),
+            ))
+        }
+    })?;
+
+    // Next trait solver performs operations locally, and normalize goals should resolve vars.
+    let value = infcx.resolve_vars_if_possible(value);
+
+    let region_obligations = infcx.take_registered_region_obligations();
+    let region_constraint_data = infcx.take_and_reset_region_constraints();
+    let region_constraints = query_response::make_query_region_constraints(
+        infcx.tcx,
+        region_obligations
+            .iter()
+            .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category()))
+            .map(|(ty, r, cc)| (infcx.resolve_vars_if_possible(ty), r, cc)),
+        &region_constraint_data,
+    );
+
+    if region_constraints.is_empty() {
+        Ok((
+            TypeOpOutput { output: value, constraints: None, error_info: None },
+            region_constraint_data,
+        ))
+    } else {
+        Ok((
+            TypeOpOutput {
+                output: value,
+                constraints: Some(infcx.tcx.arena.alloc(region_constraints)),
+                error_info: None,
+            },
+            region_constraint_data,
+        ))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs
new file mode 100644
index 00000000000..57e649f3e43
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs
@@ -0,0 +1,33 @@
+use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
+use crate::traits::ObligationCtxt;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
+
+pub use rustc_middle::traits::query::type_op::Eq;
+
+impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> {
+    type QueryResponse = ();
+
+    fn try_fast_path(
+        _tcx: TyCtxt<'tcx>,
+        key: &ParamEnvAnd<'tcx, Eq<'tcx>>,
+    ) -> Option<Self::QueryResponse> {
+        if key.value.a == key.value.b { Some(()) } else { None }
+    }
+
+    fn perform_query(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
+        tcx.type_op_eq(canonicalized)
+    }
+
+    fn perform_locally_with_next_solver(
+        ocx: &ObligationCtxt<'_, 'tcx>,
+        key: ParamEnvAnd<'tcx, Self>,
+    ) -> Result<Self::QueryResponse, NoSolution> {
+        ocx.eq(&ObligationCause::dummy(), key.param_env, key.value.a, key.value.b)?;
+        Ok(())
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
new file mode 100644
index 00000000000..ba6ed298774
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -0,0 +1,232 @@
+use crate::solve;
+use crate::traits::query::NoSolution;
+use crate::traits::wf;
+use crate::traits::ObligationCtxt;
+
+use rustc_infer::infer::canonical::Canonical;
+use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
+use rustc_infer::traits::query::OutlivesBound;
+use rustc_middle::infer::canonical::CanonicalQueryResponse;
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
+use rustc_span::def_id::CRATE_DEF_ID;
+use rustc_span::DUMMY_SP;
+use smallvec::{smallvec, SmallVec};
+
+#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
+pub struct ImpliedOutlivesBounds<'tcx> {
+    pub ty: Ty<'tcx>,
+}
+
+impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
+    type QueryResponse = Vec<OutlivesBound<'tcx>>;
+
+    fn try_fast_path(
+        _tcx: TyCtxt<'tcx>,
+        key: &ParamEnvAnd<'tcx, Self>,
+    ) -> Option<Self::QueryResponse> {
+        // Don't go into the query for things that can't possibly have lifetimes.
+        match key.value.ty.kind() {
+            ty::Tuple(elems) if elems.is_empty() => Some(vec![]),
+            ty::Never | ty::Str | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
+                Some(vec![])
+            }
+            _ => None,
+        }
+    }
+
+    fn perform_query(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
+        // FIXME this `unchecked_map` is only necessary because the
+        // query is defined as taking a `ParamEnvAnd<Ty>`; it should
+        // take an `ImpliedOutlivesBounds` instead
+        let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| {
+            let ImpliedOutlivesBounds { ty } = value;
+            param_env.and(ty)
+        });
+
+        tcx.implied_outlives_bounds(canonicalized)
+    }
+
+    fn perform_locally_with_next_solver(
+        ocx: &ObligationCtxt<'_, 'tcx>,
+        key: ParamEnvAnd<'tcx, Self>,
+    ) -> Result<Self::QueryResponse, NoSolution> {
+        compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty)
+    }
+}
+
+pub fn compute_implied_outlives_bounds_inner<'tcx>(
+    ocx: &ObligationCtxt<'_, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    ty: Ty<'tcx>,
+) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
+    let tcx = ocx.infcx.tcx;
+
+    // Sometimes when we ask what it takes for T: WF, we get back that
+    // U: WF is required; in that case, we push U onto this stack and
+    // process it next. Because the resulting predicates aren't always
+    // guaranteed to be a subset of the original type, so we need to store the
+    // WF args we've computed in a set.
+    let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
+    let mut wf_args = vec![ty.into()];
+
+    let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> =
+        vec![];
+
+    while let Some(arg) = wf_args.pop() {
+        if !checked_wf_args.insert(arg) {
+            continue;
+        }
+
+        // Compute the obligations for `arg` to be well-formed. If `arg` is
+        // an unresolved inference variable, just substituted an empty set
+        // -- because the return type here is going to be things we *add*
+        // to the environment, it's always ok for this set to be smaller
+        // than the ultimate set. (Note: normally there won't be
+        // unresolved inference variables here anyway, but there might be
+        // during typeck under some circumstances.)
+        //
+        // FIXME(@lcnr): It's not really "always fine", having fewer implied
+        // bounds can be backward incompatible, e.g. #101951 was caused by
+        // us not dealing with inference vars in `TypeOutlives` predicates.
+        let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP)
+            .unwrap_or_default();
+
+        for obligation in obligations {
+            debug!(?obligation);
+            assert!(!obligation.has_escaping_bound_vars());
+
+            // While these predicates should all be implied by other parts of
+            // the program, they are still relevant as they may constrain
+            // inference variables, which is necessary to add the correct
+            // implied bounds in some cases, mostly when dealing with projections.
+            //
+            // Another important point here: we only register `Projection`
+            // predicates, since otherwise we might register outlives
+            // predicates containing inference variables, and we don't
+            // learn anything new from those.
+            if obligation.predicate.has_non_region_infer() {
+                match obligation.predicate.kind().skip_binder() {
+                    ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
+                    | ty::PredicateKind::AliasRelate(..) => {
+                        ocx.register_obligation(obligation.clone());
+                    }
+                    _ => {}
+                }
+            }
+
+            let pred = match obligation.predicate.kind().no_bound_vars() {
+                None => continue,
+                Some(pred) => pred,
+            };
+            match pred {
+                // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
+                // if we ever support that
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
+                | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
+                | ty::PredicateKind::Subtype(..)
+                | ty::PredicateKind::Coerce(..)
+                | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
+                | ty::PredicateKind::ObjectSafe(..)
+                | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
+                | ty::PredicateKind::ConstEquate(..)
+                | ty::PredicateKind::Ambiguous
+                | ty::PredicateKind::NormalizesTo(..)
+                | ty::PredicateKind::AliasRelate(..) => {}
+
+                // We need to search through *all* WellFormed predicates
+                ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+                    wf_args.push(arg);
+                }
+
+                // We need to register region relationships
+                ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(
+                    ty::OutlivesPredicate(r_a, r_b),
+                )) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)),
+
+                ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
+                    ty_a,
+                    r_b,
+                ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)),
+            }
+        }
+    }
+
+    // This call to `select_all_or_error` is necessary to constrain inference variables, which we
+    // use further down when computing the implied bounds.
+    match ocx.select_all_or_error().as_slice() {
+        [] => (),
+        _ => return Err(NoSolution),
+    }
+
+    // We lazily compute the outlives components as
+    // `select_all_or_error` constrains inference variables.
+    let mut implied_bounds = Vec::new();
+    for ty::OutlivesPredicate(a, r_b) in outlives_bounds {
+        match a.unpack() {
+            ty::GenericArgKind::Lifetime(r_a) => {
+                implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a))
+            }
+            ty::GenericArgKind::Type(ty_a) => {
+                let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
+                // Need to manually normalize in the new solver as `wf::obligations` does not.
+                if ocx.infcx.next_trait_solver() {
+                    ty_a = solve::deeply_normalize(
+                        ocx.infcx.at(&ObligationCause::dummy(), param_env),
+                        ty_a,
+                    )
+                    .map_err(|_errs| NoSolution)?;
+                }
+                let mut components = smallvec![];
+                push_outlives_components(tcx, ty_a, &mut components);
+                implied_bounds.extend(implied_bounds_from_components(r_b, components))
+            }
+            ty::GenericArgKind::Const(_) => {
+                unreachable!("consts do not participate in outlives bounds")
+            }
+        }
+    }
+
+    Ok(implied_bounds)
+}
+
+/// When we have an implied bound that `T: 'a`, we can further break
+/// this down to determine what relationships would have to hold for
+/// `T: 'a` to hold. We get to assume that the caller has validated
+/// those relationships.
+fn implied_bounds_from_components<'tcx>(
+    sub_region: ty::Region<'tcx>,
+    sup_components: SmallVec<[Component<'tcx>; 4]>,
+) -> Vec<OutlivesBound<'tcx>> {
+    sup_components
+        .into_iter()
+        .filter_map(|component| {
+            match component {
+                Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
+                Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
+                Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
+                Component::Placeholder(_p) => {
+                    // FIXME(non_lifetime_binders): Placeholders don't currently
+                    // imply anything for outlives, though they could easily.
+                    None
+                }
+                Component::EscapingAlias(_) =>
+                // If the projection has escaping regions, don't
+                // try to infer any implied bounds even for its
+                // free components. This is conservative, because
+                // the caller will still have to prove that those
+                // free components outlive `sub_region`. But the
+                // idea is that the WAY that the caller proves
+                // that may change in the future and we want to
+                // give ourselves room to get smarter here.
+                {
+                    None
+                }
+                Component::UnresolvedInferenceVariable(..) => None,
+            }
+        })
+        .collect()
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
new file mode 100644
index 00000000000..cab2a62ed7e
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -0,0 +1,208 @@
+use crate::infer::canonical::{
+    Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints,
+};
+use crate::infer::{InferCtxt, InferOk};
+use crate::traits::{ObligationCause, ObligationCtxt};
+use rustc_errors::ErrorGuaranteed;
+use rustc_infer::infer::canonical::Certainty;
+use rustc_infer::traits::PredicateObligations;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
+use rustc_span::Span;
+use std::fmt;
+
+pub mod ascribe_user_type;
+pub mod custom;
+pub mod eq;
+pub mod implied_outlives_bounds;
+pub mod normalize;
+pub mod outlives;
+pub mod prove_predicate;
+pub mod subtype;
+
+pub use rustc_middle::traits::query::type_op::*;
+
+use self::custom::scrape_region_constraints;
+
+/// "Type ops" are used in NLL to perform some particular action and
+/// extract out the resulting region constraints (or an error if it
+/// cannot be completed).
+pub trait TypeOp<'tcx>: Sized + fmt::Debug {
+    type Output: fmt::Debug;
+    type ErrorInfo;
+
+    /// Processes the operation and all resulting obligations,
+    /// returning the final result along with any region constraints
+    /// (they will be given over to the NLL region solver).
+    fn fully_perform(
+        self,
+        infcx: &InferCtxt<'tcx>,
+        span: Span,
+    ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>;
+}
+
+/// The output from performing a type op
+pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> {
+    /// The output from the type op.
+    pub output: Op::Output,
+    /// Any region constraints from performing the type op.
+    pub constraints: Option<&'tcx QueryRegionConstraints<'tcx>>,
+    /// Used for error reporting to be able to rerun the query
+    pub error_info: Option<Op::ErrorInfo>,
+}
+
+/// "Query type ops" are type ops that are implemented using a
+/// [canonical query][c]. The `Self` type here contains the kernel of
+/// information needed to do the operation -- `TypeOp` is actually
+/// implemented for `ParamEnvAnd<Self>`, since we always need to bring
+/// along a parameter environment as well. For query type-ops, we will
+/// first canonicalize the key and then invoke the query on the tcx,
+/// which produces the resulting query region constraints.
+///
+/// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
+pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 'tcx {
+    type QueryResponse: TypeFoldable<TyCtxt<'tcx>>;
+
+    /// Give query the option for a simple fast path that never
+    /// actually hits the tcx cache lookup etc. Return `Some(r)` with
+    /// a final result or `None` to do the full path.
+    fn try_fast_path(
+        tcx: TyCtxt<'tcx>,
+        key: &ParamEnvAnd<'tcx, Self>,
+    ) -> Option<Self::QueryResponse>;
+
+    /// Performs the actual query with the canonicalized key -- the
+    /// real work happens here. This method is not given an `infcx`
+    /// because it shouldn't need one -- and if it had access to one,
+    /// it might do things like invoke `sub_regions`, which would be
+    /// bad, because it would create subregion relationships that are
+    /// not captured in the return value.
+    fn perform_query(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution>;
+
+    /// In the new trait solver, we already do caching in the solver itself,
+    /// so there's no need to canonicalize and cache via the query system.
+    /// Additionally, even if we were to canonicalize, we'd still need to
+    /// make sure to feed it predefined opaque types and the defining anchor
+    /// and that would require duplicating all of the tcx queries. Instead,
+    /// just perform these ops locally.
+    fn perform_locally_with_next_solver(
+        ocx: &ObligationCtxt<'_, 'tcx>,
+        key: ParamEnvAnd<'tcx, Self>,
+    ) -> Result<Self::QueryResponse, NoSolution>;
+
+    fn fully_perform_into(
+        query_key: ParamEnvAnd<'tcx, Self>,
+        infcx: &InferCtxt<'tcx>,
+        output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
+    ) -> Result<
+        (
+            Self::QueryResponse,
+            Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>,
+            PredicateObligations<'tcx>,
+            Certainty,
+        ),
+        NoSolution,
+    > {
+        if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) {
+            return Ok((result, None, vec![], Certainty::Proven));
+        }
+
+        // FIXME(#33684) -- We need to use
+        // `canonicalize_query_keep_static` here because of things
+        // like the subtype query, which go awry around
+        // `'static` otherwise.
+        let mut canonical_var_values = OriginalQueryValues::default();
+        let old_param_env = query_key.param_env;
+        let canonical_self =
+            infcx.canonicalize_query_keep_static(query_key, &mut canonical_var_values);
+        let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?;
+
+        let InferOk { value, obligations } = infcx
+            .instantiate_nll_query_response_and_region_obligations(
+                &ObligationCause::dummy(),
+                old_param_env,
+                &canonical_var_values,
+                canonical_result,
+                output_query_region_constraints,
+            )?;
+
+        Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty))
+    }
+}
+
+impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q>
+where
+    Q: QueryTypeOp<'tcx>,
+{
+    type Output = Q::QueryResponse;
+    type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>;
+
+    fn fully_perform(
+        self,
+        infcx: &InferCtxt<'tcx>,
+        span: Span,
+    ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
+        if infcx.next_trait_solver() {
+            return Ok(scrape_region_constraints(
+                infcx,
+                |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self),
+                "query type op",
+                span,
+            )?
+            .0);
+        }
+
+        let mut region_constraints = QueryRegionConstraints::default();
+        let (output, error_info, mut obligations, _) =
+            Q::fully_perform_into(self, infcx, &mut region_constraints).map_err(|_| {
+                infcx.dcx().span_delayed_bug(span, format!("error performing {self:?}"))
+            })?;
+
+        // Typically, instantiating NLL query results does not
+        // create obligations. However, in some cases there
+        // are unresolved type variables, and unify them *can*
+        // create obligations. In that case, we have to go
+        // fulfill them. We do this via a (recursive) query.
+        while !obligations.is_empty() {
+            trace!("{:#?}", obligations);
+            let mut progress = false;
+            for obligation in std::mem::take(&mut obligations) {
+                let obligation = infcx.resolve_vars_if_possible(obligation);
+                match ProvePredicate::fully_perform_into(
+                    obligation.param_env.and(ProvePredicate::new(obligation.predicate)),
+                    infcx,
+                    &mut region_constraints,
+                ) {
+                    Ok(((), _, new, certainty)) => {
+                        obligations.extend(new);
+                        progress = true;
+                        if let Certainty::Ambiguous = certainty {
+                            obligations.push(obligation);
+                        }
+                    }
+                    Err(_) => obligations.push(obligation),
+                }
+            }
+            if !progress {
+                return Err(infcx.dcx().span_delayed_bug(
+                    span,
+                    format!("ambiguity processing {obligations:?} from {self:?}"),
+                ));
+            }
+        }
+
+        Ok(TypeOpOutput {
+            output,
+            constraints: if region_constraints.is_empty() {
+                None
+            } else {
+                Some(infcx.tcx.arena.alloc(region_constraints))
+            },
+            error_info,
+        })
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
new file mode 100644
index 00000000000..3b33f6e6144
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
@@ -0,0 +1,78 @@
+use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
+use crate::traits::ObligationCtxt;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
+use std::fmt;
+
+pub use rustc_middle::traits::query::type_op::Normalize;
+
+impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize<T>
+where
+    T: Normalizable<'tcx> + 'tcx,
+{
+    type QueryResponse = T;
+
+    fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<T> {
+        if !key.value.value.has_projections() { Some(key.value.value) } else { None }
+    }
+
+    fn perform_query(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
+        T::type_op_method(tcx, canonicalized)
+    }
+
+    fn perform_locally_with_next_solver(
+        ocx: &ObligationCtxt<'_, 'tcx>,
+        key: ParamEnvAnd<'tcx, Self>,
+    ) -> Result<Self::QueryResponse, NoSolution> {
+        // FIXME(-Znext-solver): shouldn't be using old normalizer
+        Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value))
+    }
+}
+
+pub trait Normalizable<'tcx>: fmt::Debug + TypeFoldable<TyCtxt<'tcx>> + Lift<'tcx> + Copy {
+    fn type_op_method(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution>;
+}
+
+impl<'tcx> Normalizable<'tcx> for Ty<'tcx> {
+    fn type_op_method(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
+        tcx.type_op_normalize_ty(canonicalized)
+    }
+}
+
+impl<'tcx> Normalizable<'tcx> for ty::Clause<'tcx> {
+    fn type_op_method(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
+        tcx.type_op_normalize_clause(canonicalized)
+    }
+}
+
+impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> {
+    fn type_op_method(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
+        tcx.type_op_normalize_poly_fn_sig(canonicalized)
+    }
+}
+
+impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> {
+    fn type_op_method(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
+        tcx.type_op_normalize_fn_sig(canonicalized)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
new file mode 100644
index 00000000000..07587e37411
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
@@ -0,0 +1,51 @@
+use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
+use crate::traits::query::dropck_outlives::{
+    compute_dropck_outlives_inner, trivial_dropck_outlives,
+};
+use crate::traits::ObligationCtxt;
+use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution};
+use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
+
+#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
+pub struct DropckOutlives<'tcx> {
+    dropped_ty: Ty<'tcx>,
+}
+
+impl<'tcx> DropckOutlives<'tcx> {
+    pub fn new(dropped_ty: Ty<'tcx>) -> Self {
+        DropckOutlives { dropped_ty }
+    }
+}
+
+impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> {
+    type QueryResponse = DropckOutlivesResult<'tcx>;
+
+    fn try_fast_path(
+        tcx: TyCtxt<'tcx>,
+        key: &ParamEnvAnd<'tcx, Self>,
+    ) -> Option<Self::QueryResponse> {
+        trivial_dropck_outlives(tcx, key.value.dropped_ty).then(DropckOutlivesResult::default)
+    }
+
+    fn perform_query(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
+        // FIXME convert to the type expected by the `dropck_outlives`
+        // query. This should eventually be fixed by changing the
+        // *underlying query*.
+        let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| {
+            let DropckOutlives { dropped_ty } = value;
+            param_env.and(dropped_ty)
+        });
+
+        tcx.dropck_outlives(canonicalized)
+    }
+
+    fn perform_locally_with_next_solver(
+        ocx: &ObligationCtxt<'_, 'tcx>,
+        key: ParamEnvAnd<'tcx, Self>,
+    ) -> Result<Self::QueryResponse, NoSolution> {
+        compute_dropck_outlives_inner(ocx, key.param_env.and(key.value.dropped_ty))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
new file mode 100644
index 00000000000..e21ede47f6d
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
@@ -0,0 +1,55 @@
+use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
+use crate::traits::ObligationCtxt;
+use rustc_infer::traits::Obligation;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt};
+
+pub use rustc_middle::traits::query::type_op::ProvePredicate;
+
+impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
+    type QueryResponse = ();
+
+    fn try_fast_path(
+        tcx: TyCtxt<'tcx>,
+        key: &ParamEnvAnd<'tcx, Self>,
+    ) -> Option<Self::QueryResponse> {
+        // Proving Sized, very often on "obviously sized" types like
+        // `&T`, accounts for about 60% percentage of the predicates
+        // we have to prove. No need to canonicalize and all that for
+        // such cases.
+        if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
+            key.value.predicate.kind().skip_binder()
+        {
+            if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
+                if trait_ref.def_id() == sized_def_id {
+                    if trait_ref.self_ty().is_trivially_sized(tcx) {
+                        return Some(());
+                    }
+                }
+            }
+        }
+
+        None
+    }
+
+    fn perform_query(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
+        tcx.type_op_prove_predicate(canonicalized)
+    }
+
+    fn perform_locally_with_next_solver(
+        ocx: &ObligationCtxt<'_, 'tcx>,
+        key: ParamEnvAnd<'tcx, Self>,
+    ) -> Result<Self::QueryResponse, NoSolution> {
+        ocx.register_obligation(Obligation::new(
+            ocx.infcx.tcx,
+            ObligationCause::dummy(),
+            key.param_env,
+            key.value.predicate,
+        ));
+        Ok(())
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs
new file mode 100644
index 00000000000..ae11b0825bd
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs
@@ -0,0 +1,30 @@
+use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
+use crate::traits::ObligationCtxt;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
+
+pub use rustc_middle::traits::query::type_op::Subtype;
+
+impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> {
+    type QueryResponse = ();
+
+    fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> {
+        if key.value.sub == key.value.sup { Some(()) } else { None }
+    }
+
+    fn perform_query(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
+        tcx.type_op_subtype(canonicalized)
+    }
+
+    fn perform_locally_with_next_solver(
+        ocx: &ObligationCtxt<'_, 'tcx>,
+        key: ParamEnvAnd<'tcx, Self>,
+    ) -> Result<Self::QueryResponse, NoSolution> {
+        ocx.sub(&ObligationCause::dummy(), key.param_env, key.value.sub, key.value.sup)?;
+        Ok(())
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
new file mode 100644
index 00000000000..54b91ab1d4d
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -0,0 +1,1107 @@
+//! Candidate assembly.
+//!
+//! The selection process begins by examining all in-scope impls,
+//! caller obligations, and so forth and assembling a list of
+//! candidates. See the [rustc dev guide] for more details.
+//!
+//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
+
+use hir::def_id::DefId;
+use hir::LangItem;
+use rustc_hir as hir;
+use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_middle::ty::{self, Ty, TypeVisitableExt};
+
+use crate::traits::util;
+
+use super::BuiltinImplConditions;
+use super::SelectionCandidate::*;
+use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
+
+impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
+    #[instrument(skip(self, stack), level = "debug")]
+    pub(super) fn assemble_candidates<'o>(
+        &mut self,
+        stack: &TraitObligationStack<'o, 'tcx>,
+    ) -> Result<SelectionCandidateSet<'tcx>, SelectionError<'tcx>> {
+        let TraitObligationStack { obligation, .. } = *stack;
+        let obligation = &Obligation {
+            param_env: obligation.param_env,
+            cause: obligation.cause.clone(),
+            recursion_depth: obligation.recursion_depth,
+            predicate: self.infcx.resolve_vars_if_possible(obligation.predicate),
+        };
+
+        if obligation.predicate.skip_binder().self_ty().is_ty_var() {
+            debug!(ty = ?obligation.predicate.skip_binder().self_ty(), "ambiguous inference var or opaque type");
+            // Self is a type variable (e.g., `_: AsRef<str>`).
+            //
+            // This is somewhat problematic, as the current scheme can't really
+            // handle it turning to be a projection. This does end up as truly
+            // ambiguous in most cases anyway.
+            //
+            // Take the fast path out - this also improves
+            // performance by preventing assemble_candidates_from_impls from
+            // matching every impl for this trait.
+            return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true });
+        }
+
+        let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false };
+
+        // Negative trait predicates have different rules than positive trait predicates.
+        if obligation.polarity() == ty::ImplPolarity::Negative {
+            self.assemble_candidates_for_trait_alias(obligation, &mut candidates);
+            self.assemble_candidates_from_impls(obligation, &mut candidates);
+            self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?;
+        } else {
+            self.assemble_candidates_for_trait_alias(obligation, &mut candidates);
+
+            // Other bounds. Consider both in-scope bounds from fn decl
+            // and applicable impls. There is a certain set of precedence rules here.
+            let def_id = obligation.predicate.def_id();
+            let lang_items = self.tcx().lang_items();
+
+            if lang_items.copy_trait() == Some(def_id) {
+                debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty());
+
+                // User-defined copy impls are permitted, but only for
+                // structs and enums.
+                self.assemble_candidates_from_impls(obligation, &mut candidates);
+
+                // For other types, we'll use the builtin rules.
+                let copy_conditions = self.copy_clone_conditions(obligation);
+                self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates);
+            } else if lang_items.discriminant_kind_trait() == Some(def_id) {
+                // `DiscriminantKind` is automatically implemented for every type.
+                candidates.vec.push(BuiltinCandidate { has_nested: false });
+            } else if lang_items.pointee_trait() == Some(def_id) {
+                // `Pointee` is automatically implemented for every type.
+                candidates.vec.push(BuiltinCandidate { has_nested: false });
+            } else if lang_items.sized_trait() == Some(def_id) {
+                // Sized is never implementable by end-users, it is
+                // always automatically computed.
+                let sized_conditions = self.sized_conditions(obligation);
+                self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates);
+            } else if lang_items.unsize_trait() == Some(def_id) {
+                self.assemble_candidates_for_unsizing(obligation, &mut candidates);
+            } else if lang_items.destruct_trait() == Some(def_id) {
+                self.assemble_const_destruct_candidates(obligation, &mut candidates);
+            } else if lang_items.transmute_trait() == Some(def_id) {
+                // User-defined transmutability impls are permitted.
+                self.assemble_candidates_from_impls(obligation, &mut candidates);
+                self.assemble_candidates_for_transmutability(obligation, &mut candidates);
+            } else if lang_items.tuple_trait() == Some(def_id) {
+                self.assemble_candidate_for_tuple(obligation, &mut candidates);
+            } else if lang_items.pointer_like() == Some(def_id) {
+                self.assemble_candidate_for_pointer_like(obligation, &mut candidates);
+            } else if lang_items.fn_ptr_trait() == Some(def_id) {
+                self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
+            } else {
+                if lang_items.clone_trait() == Some(def_id) {
+                    // Same builtin conditions as `Copy`, i.e., every type which has builtin support
+                    // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone`
+                    // types have builtin support for `Clone`.
+                    let clone_conditions = self.copy_clone_conditions(obligation);
+                    self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates);
+                }
+
+                if lang_items.coroutine_trait() == Some(def_id) {
+                    self.assemble_coroutine_candidates(obligation, &mut candidates);
+                } else if lang_items.future_trait() == Some(def_id) {
+                    self.assemble_future_candidates(obligation, &mut candidates);
+                } else if lang_items.iterator_trait() == Some(def_id) {
+                    self.assemble_iterator_candidates(obligation, &mut candidates);
+                } else if lang_items.async_iterator_trait() == Some(def_id) {
+                    self.assemble_async_iterator_candidates(obligation, &mut candidates);
+                }
+
+                self.assemble_closure_candidates(obligation, &mut candidates);
+                self.assemble_fn_pointer_candidates(obligation, &mut candidates);
+                self.assemble_candidates_from_impls(obligation, &mut candidates);
+                self.assemble_candidates_from_object_ty(obligation, &mut candidates);
+            }
+
+            self.assemble_candidates_from_projected_tys(obligation, &mut candidates);
+            self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?;
+            self.assemble_candidates_from_auto_impls(obligation, &mut candidates);
+        }
+        debug!("candidate list size: {}", candidates.vec.len());
+        Ok(candidates)
+    }
+
+    #[instrument(level = "debug", skip(self, candidates))]
+    fn assemble_candidates_from_projected_tys(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // Before we go into the whole placeholder thing, just
+        // quickly check if the self-type is a projection at all.
+        match obligation.predicate.skip_binder().trait_ref.self_ty().kind() {
+            // Excluding IATs and type aliases here as they don't have meaningful item bounds.
+            ty::Alias(ty::Projection | ty::Opaque, _) => {}
+            ty::Infer(ty::TyVar(_)) => {
+                span_bug!(
+                    obligation.cause.span,
+                    "Self=_ should have been handled by assemble_candidates"
+                );
+            }
+            _ => return,
+        }
+
+        let result = self
+            .infcx
+            .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
+
+        candidates.vec.extend(result.into_iter().map(|idx| ProjectionCandidate(idx)));
+    }
+
+    /// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller
+    /// supplied to find out whether it is listed among them.
+    ///
+    /// Never affects the inference environment.
+    #[instrument(level = "debug", skip(self, stack, candidates))]
+    fn assemble_candidates_from_caller_bounds<'o>(
+        &mut self,
+        stack: &TraitObligationStack<'o, 'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) -> Result<(), SelectionError<'tcx>> {
+        debug!(?stack.obligation);
+
+        let all_bounds = stack
+            .obligation
+            .param_env
+            .caller_bounds()
+            .iter()
+            .filter(|p| !p.references_error())
+            .filter_map(|p| p.as_trait_clause());
+
+        // Micro-optimization: filter out predicates relating to different traits.
+        let matching_bounds =
+            all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id());
+
+        // Keep only those bounds which may apply, and propagate overflow if it occurs.
+        for bound in matching_bounds {
+            if bound.skip_binder().polarity != stack.obligation.predicate.skip_binder().polarity {
+                continue;
+            }
+
+            // FIXME(oli-obk): it is suspicious that we are dropping the constness and
+            // polarity here.
+            let wc = self.where_clause_may_apply(stack, bound.map_bound(|t| t.trait_ref))?;
+            if wc.may_apply() {
+                candidates.vec.push(ParamCandidate(bound));
+            }
+        }
+
+        Ok(())
+    }
+
+    fn assemble_coroutine_candidates(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // Okay to skip binder because the args on coroutine types never
+        // touch bound regions, they just capture the in-scope
+        // type/region parameters.
+        let self_ty = obligation.self_ty().skip_binder();
+        match self_ty.kind() {
+            // `async`/`gen` constructs get lowered to a special kind of coroutine that
+            // should *not* `impl Coroutine`.
+            ty::Coroutine(did, ..) if self.tcx().is_general_coroutine(*did) => {
+                debug!(?self_ty, ?obligation, "assemble_coroutine_candidates",);
+
+                candidates.vec.push(CoroutineCandidate);
+            }
+            ty::Infer(ty::TyVar(_)) => {
+                debug!("assemble_coroutine_candidates: ambiguous self-type");
+                candidates.ambiguous = true;
+            }
+            _ => {}
+        }
+    }
+
+    fn assemble_future_candidates(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        let self_ty = obligation.self_ty().skip_binder();
+        if let ty::Coroutine(did, ..) = self_ty.kind() {
+            // async constructs get lowered to a special kind of coroutine that
+            // should directly `impl Future`.
+            if self.tcx().coroutine_is_async(*did) {
+                debug!(?self_ty, ?obligation, "assemble_future_candidates",);
+
+                candidates.vec.push(FutureCandidate);
+            }
+        }
+    }
+
+    fn assemble_iterator_candidates(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        let self_ty = obligation.self_ty().skip_binder();
+        if let ty::Coroutine(did, ..) = self_ty.kind() {
+            // gen constructs get lowered to a special kind of coroutine that
+            // should directly `impl Iterator`.
+            if self.tcx().coroutine_is_gen(*did) {
+                debug!(?self_ty, ?obligation, "assemble_iterator_candidates",);
+
+                candidates.vec.push(IteratorCandidate);
+            }
+        }
+    }
+
+    fn assemble_async_iterator_candidates(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        let self_ty = obligation.self_ty().skip_binder();
+        if let ty::Coroutine(did, args) = *self_ty.kind() {
+            // gen constructs get lowered to a special kind of coroutine that
+            // should directly `impl AsyncIterator`.
+            if self.tcx().coroutine_is_async_gen(did) {
+                debug!(?self_ty, ?obligation, "assemble_iterator_candidates",);
+
+                // Can only confirm this candidate if we have constrained
+                // the `Yield` type to at least `Poll<Option<?0>>`..
+                let ty::Adt(_poll_def, args) = *args.as_coroutine().yield_ty().kind() else {
+                    candidates.ambiguous = true;
+                    return;
+                };
+                let ty::Adt(_option_def, _) = *args.type_at(0).kind() else {
+                    candidates.ambiguous = true;
+                    return;
+                };
+
+                candidates.vec.push(AsyncIteratorCandidate);
+            }
+        }
+    }
+
+    /// Checks for the artificial impl that the compiler will create for an obligation like `X :
+    /// FnMut<..>` where `X` is a closure type.
+    ///
+    /// Note: the type parameters on a closure candidate are modeled as *output* type
+    /// parameters and hence do not affect whether this trait is a match or not. They will be
+    /// unified during the confirmation step.
+    fn assemble_closure_candidates(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        let Some(kind) = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()) else {
+            return;
+        };
+
+        // Okay to skip binder because the args on closure types never
+        // touch bound regions, they just capture the in-scope
+        // type/region parameters
+        match *obligation.self_ty().skip_binder().kind() {
+            ty::Closure(def_id, closure_args) => {
+                let is_const = self.tcx().is_const_fn_raw(def_id);
+                debug!(?kind, ?obligation, "assemble_unboxed_candidates");
+                match self.infcx.closure_kind(closure_args) {
+                    Some(closure_kind) => {
+                        debug!(?closure_kind, "assemble_unboxed_candidates");
+                        if closure_kind.extends(kind) {
+                            candidates.vec.push(ClosureCandidate { is_const });
+                        }
+                    }
+                    None => {
+                        if kind == ty::ClosureKind::FnOnce {
+                            candidates.vec.push(ClosureCandidate { is_const });
+                        } else {
+                            candidates.ambiguous = true;
+                        }
+                    }
+                }
+            }
+            ty::Infer(ty::TyVar(_)) => {
+                debug!("assemble_unboxed_closure_candidates: ambiguous self-type");
+                candidates.ambiguous = true;
+            }
+            _ => {}
+        }
+    }
+
+    /// Implements one of the `Fn()` family for a fn pointer.
+    fn assemble_fn_pointer_candidates(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // We provide impl of all fn traits for fn pointers.
+        if !self.tcx().is_fn_trait(obligation.predicate.def_id()) {
+            return;
+        }
+
+        // Keep this function in sync with extract_tupled_inputs_and_output_from_callable
+        // until the old solver (and thus this function) is removed.
+
+        // Okay to skip binder because what we are inspecting doesn't involve bound regions.
+        let self_ty = obligation.self_ty().skip_binder();
+        match *self_ty.kind() {
+            ty::Infer(ty::TyVar(_)) => {
+                debug!("assemble_fn_pointer_candidates: ambiguous self-type");
+                candidates.ambiguous = true; // Could wind up being a fn() type.
+            }
+            // Provide an impl, but only for suitable `fn` pointers.
+            ty::FnPtr(sig) => {
+                if sig.is_fn_trait_compatible() {
+                    candidates
+                        .vec
+                        .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ });
+                }
+            }
+            // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
+            ty::FnDef(def_id, args) => {
+                let tcx = self.tcx();
+                if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible()
+                    && tcx.codegen_fn_attrs(def_id).target_features.is_empty()
+                {
+                    candidates.vec.push(FnPointerCandidate {
+                        fn_host_effect: tcx
+                            .generics_of(def_id)
+                            .host_effect_index
+                            .map_or(tcx.consts.true_, |idx| args.const_at(idx)),
+                    });
+                }
+            }
+            _ => {}
+        }
+    }
+
+    /// Searches for impls that might apply to `obligation`.
+    #[instrument(level = "debug", skip(self, candidates))]
+    fn assemble_candidates_from_impls(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // Essentially any user-written impl will match with an error type,
+        // so creating `ImplCandidates` isn't useful. However, we might
+        // end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized`)
+        // This helps us avoid overflow: see issue #72839
+        // Since compilation is already guaranteed to fail, this is just
+        // to try to show the 'nicest' possible errors to the user.
+        // We don't check for errors in the `ParamEnv` - in practice,
+        // it seems to cause us to be overly aggressive in deciding
+        // to give up searching for candidates, leading to spurious errors.
+        if obligation.predicate.references_error() {
+            return;
+        }
+
+        let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
+        let obligation_args = obligation.predicate.skip_binder().trait_ref.args;
+        self.tcx().for_each_relevant_impl(
+            obligation.predicate.def_id(),
+            obligation.predicate.skip_binder().trait_ref.self_ty(),
+            |impl_def_id| {
+                // Before we create the substitutions and everything, first
+                // consider a "quick reject". This avoids creating more types
+                // and so forth that we need to.
+                let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
+                if !drcx.args_may_unify(obligation_args, impl_trait_ref.skip_binder().args) {
+                    return;
+                }
+                if self.reject_fn_ptr_impls(
+                    impl_def_id,
+                    obligation,
+                    impl_trait_ref.skip_binder().self_ty(),
+                ) {
+                    return;
+                }
+
+                self.infcx.probe(|_| {
+                    if let Ok(_args) = self.match_impl(impl_def_id, impl_trait_ref, obligation) {
+                        candidates.vec.push(ImplCandidate(impl_def_id));
+                    }
+                });
+            },
+        );
+    }
+
+    /// The various `impl<T: FnPtr> Trait for T` in libcore are more like builtin impls for all function items
+    /// and function pointers and less like blanket impls. Rejecting them when they can't possibly apply (because
+    /// the obligation's self-type does not implement `FnPtr`) avoids reporting that the self type does not implement
+    /// `FnPtr`, when we wanted to report that it doesn't implement `Trait`.
+    #[instrument(level = "trace", skip(self), ret)]
+    fn reject_fn_ptr_impls(
+        &mut self,
+        impl_def_id: DefId,
+        obligation: &PolyTraitObligation<'tcx>,
+        impl_self_ty: Ty<'tcx>,
+    ) -> bool {
+        // Let `impl<T: FnPtr> Trait for Vec<T>` go through the normal rejection path.
+        if !matches!(impl_self_ty.kind(), ty::Param(..)) {
+            return false;
+        }
+        let Some(fn_ptr_trait) = self.tcx().lang_items().fn_ptr_trait() else {
+            return false;
+        };
+
+        for &(predicate, _) in self.tcx().predicates_of(impl_def_id).predicates {
+            let ty::ClauseKind::Trait(pred) = predicate.kind().skip_binder() else { continue };
+            if fn_ptr_trait != pred.trait_ref.def_id {
+                continue;
+            }
+            trace!(?pred);
+            // Not the bound we're looking for
+            if pred.self_ty() != impl_self_ty {
+                continue;
+            }
+
+            match obligation.self_ty().skip_binder().kind() {
+                // Fast path to avoid evaluating an obligation that trivially holds.
+                // There may be more bounds, but these are checked by the regular path.
+                ty::FnPtr(..) => return false,
+
+                // These may potentially implement `FnPtr`
+                ty::Placeholder(..)
+                | ty::Dynamic(_, _, _)
+                | ty::Alias(_, _)
+                | ty::Infer(_)
+                | ty::Param(..)
+                | ty::Bound(_, _) => {}
+
+                // These can't possibly implement `FnPtr` as they are concrete types
+                // and not `FnPtr`
+                ty::Bool
+                | ty::Char
+                | ty::Int(_)
+                | ty::Uint(_)
+                | ty::Float(_)
+                | ty::Adt(_, _)
+                | ty::Foreign(_)
+                | ty::Str
+                | ty::Array(_, _)
+                | ty::Slice(_)
+                | ty::RawPtr(_)
+                | ty::Ref(_, _, _)
+                | ty::Closure(_, _)
+                | ty::Coroutine(_, _)
+                | ty::CoroutineWitness(..)
+                | ty::Never
+                | ty::Tuple(_)
+                | ty::Error(_) => return true,
+                // FIXME: Function definitions could actually implement `FnPtr` by
+                // casting the ZST function def to a function pointer.
+                ty::FnDef(_, _) => return true,
+            }
+
+            // Generic params can implement `FnPtr` if the predicate
+            // holds within its own environment.
+            let obligation = Obligation::new(
+                self.tcx(),
+                obligation.cause.clone(),
+                obligation.param_env,
+                self.tcx().mk_predicate(obligation.predicate.map_bound(|mut pred| {
+                    pred.trait_ref =
+                        ty::TraitRef::new(self.tcx(), fn_ptr_trait, [pred.trait_ref.self_ty()]);
+                    ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
+                })),
+            );
+            if let Ok(r) = self.evaluate_root_obligation(&obligation) {
+                if !r.may_apply() {
+                    return true;
+                }
+            }
+        }
+        false
+    }
+
+    fn assemble_candidates_from_auto_impls(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // Okay to skip binder here because the tests we do below do not involve bound regions.
+        let self_ty = obligation.self_ty().skip_binder();
+        debug!(?self_ty, "assemble_candidates_from_auto_impls");
+
+        let def_id = obligation.predicate.def_id();
+
+        if self.tcx().trait_is_auto(def_id) {
+            match *self_ty.kind() {
+                ty::Dynamic(..) => {
+                    // For object types, we don't know what the closed
+                    // over types are. This means we conservatively
+                    // say nothing; a candidate may be added by
+                    // `assemble_candidates_from_object_ty`.
+                }
+                ty::Foreign(..) => {
+                    // Since the contents of foreign types is unknown,
+                    // we don't add any `..` impl. Default traits could
+                    // still be provided by a manual implementation for
+                    // this trait and type.
+                }
+                ty::Param(..)
+                | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
+                | ty::Placeholder(..)
+                | ty::Bound(..) => {
+                    // In these cases, we don't know what the actual
+                    // type is. Therefore, we cannot break it down
+                    // into its constituent types. So we don't
+                    // consider the `..` impl but instead just add no
+                    // candidates: this means that typeck will only
+                    // succeed if there is another reason to believe
+                    // that this obligation holds. That could be a
+                    // where-clause or, in the case of an object type,
+                    // it could be that the object type lists the
+                    // trait (e.g., `Foo+Send : Send`). See
+                    // `ui/typeck/typeck-default-trait-impl-send-param.rs`
+                    // for an example of a test case that exercises
+                    // this path.
+                }
+                ty::Infer(ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_)) => {
+                    // The auto impl might apply; we don't know.
+                    candidates.ambiguous = true;
+                }
+                ty::Coroutine(coroutine_def_id, _)
+                    if self.tcx().lang_items().unpin_trait() == Some(def_id) =>
+                {
+                    match self.tcx().coroutine_movability(coroutine_def_id) {
+                        hir::Movability::Static => {
+                            // Immovable coroutines are never `Unpin`, so
+                            // suppress the normal auto-impl candidate for it.
+                        }
+                        hir::Movability::Movable => {
+                            // Movable coroutines are always `Unpin`, so add an
+                            // unconditional builtin candidate.
+                            candidates.vec.push(BuiltinCandidate { has_nested: false });
+                        }
+                    }
+                }
+
+                ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+                    bug!(
+                        "asked to assemble auto trait candidates of unexpected type: {:?}",
+                        self_ty
+                    );
+                }
+
+                ty::Alias(ty::Opaque, _) => {
+                    if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(_))) {
+                        // We do not generate an auto impl candidate for `impl Trait`s which already
+                        // reference our auto trait.
+                        //
+                        // For example during candidate assembly for `impl Send: Send`, we don't have
+                        // to look at the constituent types for this opaque types to figure out that this
+                        // trivially holds.
+                        //
+                        // Note that this is only sound as projection candidates of opaque types
+                        // are always applicable for auto traits.
+                    } else if self.infcx.intercrate {
+                        // We do not emit auto trait candidates for opaque types in coherence.
+                        // Doing so can result in weird dependency cycles.
+                        candidates.ambiguous = true;
+                    } else {
+                        candidates.vec.push(AutoImplCandidate)
+                    }
+                }
+
+                ty::Bool
+                | ty::Char
+                | ty::Int(_)
+                | ty::Uint(_)
+                | ty::Float(_)
+                | ty::Str
+                | ty::Array(_, _)
+                | ty::Slice(_)
+                | ty::Adt(..)
+                | ty::RawPtr(_)
+                | ty::Ref(..)
+                | ty::FnDef(..)
+                | ty::FnPtr(_)
+                | ty::Closure(_, _)
+                | ty::Coroutine(..)
+                | ty::Never
+                | ty::Tuple(_)
+                | ty::CoroutineWitness(..) => {
+                    // Only consider auto impls if there are no manual impls for the root of `self_ty`.
+                    //
+                    // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
+                    // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls
+                    // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined.
+                    //
+                    // Generally, we have to guarantee that for all `SimplifiedType`s the only crate
+                    // which may define impls for that type is either the crate defining the type
+                    // or the trait. This should be guaranteed by the orphan check.
+                    let mut has_impl = false;
+                    self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true);
+                    if !has_impl {
+                        candidates.vec.push(AutoImplCandidate)
+                    }
+                }
+                ty::Error(_) => {} // do not add an auto trait impl for `ty::Error` for now.
+            }
+        }
+    }
+
+    /// Searches for impls that might apply to `obligation`.
+    fn assemble_candidates_from_object_ty(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        debug!(
+            self_ty = ?obligation.self_ty().skip_binder(),
+            "assemble_candidates_from_object_ty",
+        );
+
+        if !self.tcx().trait_def(obligation.predicate.def_id()).implement_via_object {
+            return;
+        }
+
+        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, \
+                             pushing candidate"
+                        );
+                        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)
+                        } else {
+                            return;
+                        }
+                    } else {
+                        // Only auto trait bounds exist.
+                        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()
+                    })
+                })
+                .map(|(idx, _)| ObjectCandidate(idx));
+
+            candidates.vec.extend(candidate_supertraits);
+        })
+    }
+
+    /// Searches for unsizing that might apply to `obligation`.
+    fn assemble_candidates_for_unsizing(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // We currently never consider higher-ranked obligations e.g.
+        // `for<'a> &'a T: Unsize<Trait+'a>` to be implemented. This is not
+        // because they are a priori invalid, and we could potentially add support
+        // for them later, it's just that there isn't really a strong need for it.
+        // A `T: Unsize<U>` obligation is always used as part of a `T: CoerceUnsize<U>`
+        // impl, and those are generally applied to concrete types.
+        //
+        // That said, one might try to write a fn with a where clause like
+        //     for<'a> Foo<'a, T>: Unsize<Foo<'a, Trait>>
+        // where the `'a` is kind of orthogonal to the relevant part of the `Unsize`.
+        // Still, you'd be more likely to write that where clause as
+        //     T: Trait
+        // so it seems ok if we (conservatively) fail to accept that `Unsize`
+        // obligation above. Should be possible to extend this in the future.
+        let Some(source) = obligation.self_ty().no_bound_vars() else {
+            // Don't add any candidates if there are bound regions.
+            return;
+        };
+        let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
+
+        debug!(?source, ?target, "assemble_candidates_for_unsizing");
+
+        match (source.kind(), target.kind()) {
+            // Trait+Kx+'a -> Trait+Ky+'b (upcasts).
+            (&ty::Dynamic(a_data, a_region, ty::Dyn), &ty::Dynamic(b_data, b_region, ty::Dyn)) => {
+                // Upcast coercions permit several things:
+                //
+                // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
+                // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b`
+                // 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar`
+                //
+                // Note that neither of the first two of these changes requires any
+                // change at runtime. The third needs to change pointer metadata at runtime.
+                //
+                // We always perform upcasting coercions when we can because of reason
+                // #2 (region bounds).
+                let auto_traits_compatible = b_data
+                    .auto_traits()
+                    // All of a's auto traits need to be in b's auto traits.
+                    .all(|b| a_data.auto_traits().any(|a| a == b));
+                if auto_traits_compatible {
+                    let principal_def_id_a = a_data.principal_def_id();
+                    let principal_def_id_b = b_data.principal_def_id();
+                    if principal_def_id_a == principal_def_id_b {
+                        // no cyclic
+                        candidates.vec.push(BuiltinUnsizeCandidate);
+                    } else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
+                        // not casual unsizing, now check whether this is trait upcasting coercion.
+                        let principal_a = a_data.principal().unwrap();
+                        let target_trait_did = principal_def_id_b.unwrap();
+                        let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
+
+                        for (idx, upcast_trait_ref) in
+                            util::supertraits(self.tcx(), source_trait_ref).enumerate()
+                        {
+                            self.infcx.probe(|_| {
+                                if upcast_trait_ref.def_id() == target_trait_did
+                                    && let Ok(nested) = self.match_upcast_principal(
+                                        obligation,
+                                        upcast_trait_ref,
+                                        a_data,
+                                        b_data,
+                                        a_region,
+                                        b_region,
+                                    )
+                                {
+                                    if nested.is_none() {
+                                        candidates.ambiguous = true;
+                                    }
+                                    candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
+                                }
+                            })
+                        }
+                    }
+                }
+            }
+
+            // `T` -> `Trait`
+            (_, &ty::Dynamic(_, _, ty::Dyn)) => {
+                candidates.vec.push(BuiltinUnsizeCandidate);
+            }
+
+            // Ambiguous handling is below `T` -> `Trait`, because inference
+            // variables can still implement `Unsize<Trait>` and nested
+            // obligations will have the final say (likely deferred).
+            (&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => {
+                debug!("assemble_candidates_for_unsizing: ambiguous");
+                candidates.ambiguous = true;
+            }
+
+            // `[T; n]` -> `[T]`
+            (&ty::Array(..), &ty::Slice(_)) => {
+                candidates.vec.push(BuiltinUnsizeCandidate);
+            }
+
+            // `Struct<T>` -> `Struct<U>`
+            (&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => {
+                if def_id_a == def_id_b {
+                    candidates.vec.push(BuiltinUnsizeCandidate);
+                }
+            }
+
+            // `(.., T)` -> `(.., U)`
+            (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => {
+                if tys_a.len() == tys_b.len() {
+                    candidates.vec.push(BuiltinUnsizeCandidate);
+                }
+            }
+
+            _ => {}
+        };
+    }
+
+    #[instrument(level = "debug", skip(self, obligation, candidates))]
+    fn assemble_candidates_for_transmutability(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        if obligation.predicate.has_non_region_param() {
+            return;
+        }
+
+        if obligation.has_non_region_infer() {
+            candidates.ambiguous = true;
+            return;
+        }
+
+        candidates.vec.push(TransmutabilityCandidate);
+    }
+
+    #[instrument(level = "debug", skip(self, obligation, candidates))]
+    fn assemble_candidates_for_trait_alias(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // Okay to skip binder here because the tests we do below do not involve bound regions.
+        let self_ty = obligation.self_ty().skip_binder();
+        debug!(?self_ty);
+
+        let def_id = obligation.predicate.def_id();
+
+        if self.tcx().is_trait_alias(def_id) {
+            candidates.vec.push(TraitAliasCandidate);
+        }
+    }
+
+    /// Assembles the trait which are built-in to the language itself:
+    /// `Copy`, `Clone` and `Sized`.
+    #[instrument(level = "debug", skip(self, candidates))]
+    fn assemble_builtin_bound_candidates(
+        &mut self,
+        conditions: BuiltinImplConditions<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        match conditions {
+            BuiltinImplConditions::Where(nested) => {
+                candidates
+                    .vec
+                    .push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() });
+            }
+            BuiltinImplConditions::None => {}
+            BuiltinImplConditions::Ambiguous => {
+                candidates.ambiguous = true;
+            }
+        }
+    }
+
+    fn assemble_const_destruct_candidates(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // If the predicate is `~const Destruct` in a non-const environment, we don't actually need
+        // to check anything. We'll short-circuit checking any obligations in confirmation, too.
+        let Some(host_effect_index) =
+            self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index
+        else {
+            candidates.vec.push(BuiltinCandidate { has_nested: false });
+            return;
+        };
+        // If the obligation has `host = true`, then the obligation is non-const and it's always
+        // trivially implemented.
+        if obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index)
+            == self.tcx().consts.true_
+        {
+            candidates.vec.push(BuiltinCandidate { has_nested: false });
+            return;
+        }
+
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
+        match self_ty.skip_binder().kind() {
+            ty::Alias(..)
+            | ty::Dynamic(..)
+            | ty::Error(_)
+            | ty::Bound(..)
+            | ty::Param(_)
+            | ty::Placeholder(_) => {
+                // We don't know if these are `~const Destruct`, at least
+                // not structurally... so don't push a candidate.
+            }
+
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Infer(ty::IntVar(_))
+            | ty::Infer(ty::FloatVar(_))
+            | ty::Str
+            | ty::RawPtr(_)
+            | ty::Ref(..)
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::Never
+            | ty::Foreign(_)
+            | ty::Array(..)
+            | ty::Slice(_)
+            | ty::Closure(..)
+            | ty::Coroutine(..)
+            | ty::Tuple(_)
+            | ty::CoroutineWitness(..) => {
+                // These are built-in, and cannot have a custom `impl const Destruct`.
+                candidates.vec.push(ConstDestructCandidate(None));
+            }
+
+            ty::Adt(..) => {
+                let mut relevant_impl = None;
+                self.tcx().for_each_relevant_impl(
+                    self.tcx().require_lang_item(LangItem::Drop, None),
+                    obligation.predicate.skip_binder().trait_ref.self_ty(),
+                    |impl_def_id| {
+                        if let Some(old_impl_def_id) = relevant_impl {
+                            self.tcx()
+                                .dcx()
+                                .struct_span_err(
+                                    self.tcx().def_span(impl_def_id),
+                                    "multiple drop impls found",
+                                )
+                                .span_note(self.tcx().def_span(old_impl_def_id), "other impl here")
+                                .delay_as_bug();
+                        }
+
+                        relevant_impl = Some(impl_def_id);
+                    },
+                );
+
+                if let Some(impl_def_id) = relevant_impl {
+                    // Check that `impl Drop` is actually const, if there is a custom impl
+                    if self.tcx().constness(impl_def_id) == hir::Constness::Const {
+                        candidates.vec.push(ConstDestructCandidate(Some(impl_def_id)));
+                    }
+                } else {
+                    // Otherwise check the ADT like a built-in type (structurally)
+                    candidates.vec.push(ConstDestructCandidate(None));
+                }
+            }
+
+            ty::Infer(_) => {
+                candidates.ambiguous = true;
+            }
+        }
+    }
+
+    fn assemble_candidate_for_tuple(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
+        match self_ty.kind() {
+            ty::Tuple(_) => {
+                candidates.vec.push(BuiltinCandidate { has_nested: false });
+            }
+            ty::Infer(ty::TyVar(_)) => {
+                candidates.ambiguous = true;
+            }
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Closure(_, _)
+            | ty::Coroutine(_, _)
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Alias(..)
+            | ty::Param(_)
+            | ty::Bound(_, _)
+            | ty::Error(_)
+            | ty::Infer(_)
+            | ty::Placeholder(_) => {}
+        }
+    }
+
+    fn assemble_candidate_for_pointer_like(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // The regions of a type don't affect the size of the type
+        let tcx = self.tcx();
+        let self_ty = tcx.instantiate_bound_regions_with_erased(obligation.predicate.self_ty());
+        // We should erase regions from both the param-env and type, since both
+        // may have infer regions. Specifically, after canonicalizing and instantiating,
+        // early bound regions turn into region vars in both the new and old solver.
+        let key = tcx.erase_regions(obligation.param_env.and(self_ty));
+        // But if there are inference variables, we have to wait until it's resolved.
+        if key.has_non_region_infer() {
+            candidates.ambiguous = true;
+            return;
+        }
+
+        if let Ok(layout) = tcx.layout_of(key)
+            && layout.layout.is_pointer_like(&tcx.data_layout)
+        {
+            candidates.vec.push(BuiltinCandidate { has_nested: false });
+        }
+    }
+
+    fn assemble_candidates_for_fn_ptr_trait(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
+
+        match self_ty.skip_binder().kind() {
+            ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }),
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(..)
+            | ty::Foreign(..)
+            | ty::Str
+            | ty::Array(..)
+            | ty::Slice(_)
+            | ty::RawPtr(_)
+            | ty::Ref(..)
+            | ty::FnDef(..)
+            | ty::Placeholder(..)
+            | ty::Dynamic(..)
+            | ty::Closure(..)
+            | ty::Coroutine(..)
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Tuple(..)
+            | ty::Alias(..)
+            | ty::Param(..)
+            | ty::Bound(..)
+            | ty::Error(_)
+            | ty::Infer(
+                ty::InferTy::IntVar(_)
+                | ty::InferTy::FloatVar(_)
+                | ty::InferTy::FreshIntTy(_)
+                | ty::InferTy::FreshFloatTy(_),
+            ) => {}
+            ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)) => {
+                candidates.ambiguous = true;
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
new file mode 100644
index 00000000000..e20bb06d777
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -0,0 +1,1370 @@
+//! Confirmation.
+//!
+//! Confirmation unifies the output type parameters of the trait
+//! with the values found in the obligation, possibly yielding a
+//! type error. See the [rustc dev guide] for more details.
+//!
+//! [rustc dev guide]:
+//! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation
+use rustc_ast::Mutability;
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_hir::lang_items::LangItem;
+use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType;
+use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
+use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch};
+use rustc_middle::ty::{
+    self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
+    TraitPredicate, Ty, TyCtxt, TypeVisitableExt,
+};
+use rustc_span::def_id::DefId;
+
+use crate::traits::project::{normalize_with_depth, normalize_with_depth_to};
+use crate::traits::util::{self, closure_trait_ref_and_return_type};
+use crate::traits::vtable::{
+    count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset,
+    VtblSegment,
+};
+use crate::traits::{
+    BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
+    ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause,
+    OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection,
+    SelectionError, TraitNotObjectSafe, Unimplemented,
+};
+
+use super::BuiltinImplConditions;
+use super::SelectionCandidate::{self, *};
+use super::SelectionContext;
+
+use std::iter;
+use std::ops::ControlFlow;
+
+impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
+    #[instrument(level = "debug", skip(self))]
+    pub(super) fn confirm_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidate: SelectionCandidate<'tcx>,
+    ) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
+        let mut impl_src = match candidate {
+            BuiltinCandidate { has_nested } => {
+                let data = self.confirm_builtin_candidate(obligation, has_nested);
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
+            }
+
+            TransmutabilityCandidate => {
+                let data = self.confirm_transmutability_candidate(obligation)?;
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
+            }
+
+            ParamCandidate(param) => {
+                let obligations =
+                    self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
+                ImplSource::Param(obligations)
+            }
+
+            ImplCandidate(impl_def_id) => {
+                ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id))
+            }
+
+            AutoImplCandidate => {
+                let data = self.confirm_auto_impl_candidate(obligation)?;
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
+            }
+
+            ProjectionCandidate(idx) => {
+                let obligations = self.confirm_projection_candidate(obligation, idx)?;
+                ImplSource::Param(obligations)
+            }
+
+            ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?,
+
+            ClosureCandidate { .. } => {
+                let vtable_closure = self.confirm_closure_candidate(obligation)?;
+                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure)
+            }
+
+            CoroutineCandidate => {
+                let vtable_coroutine = self.confirm_coroutine_candidate(obligation)?;
+                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_coroutine)
+            }
+
+            FutureCandidate => {
+                let vtable_future = self.confirm_future_candidate(obligation)?;
+                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future)
+            }
+
+            IteratorCandidate => {
+                let vtable_iterator = self.confirm_iterator_candidate(obligation)?;
+                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
+            }
+
+            AsyncIteratorCandidate => {
+                let vtable_iterator = self.confirm_async_iterator_candidate(obligation)?;
+                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
+            }
+
+            FnPointerCandidate { fn_host_effect } => {
+                let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?;
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
+            }
+
+            TraitAliasCandidate => {
+                let data = self.confirm_trait_alias_candidate(obligation);
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
+            }
+
+            BuiltinObjectCandidate => {
+                // This indicates something like `Trait + Send: Send`. In this case, we know that
+                // this holds because that's what the object type is telling us, and there's really
+                // no additional obligations to prove and no types in particular to unify, etc.
+                ImplSource::Builtin(BuiltinImplSource::Misc, Vec::new())
+            }
+
+            BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?,
+
+            TraitUpcastingUnsizeCandidate(idx) => {
+                self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?
+            }
+
+            ConstDestructCandidate(def_id) => {
+                let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
+            }
+        };
+
+        // The obligations returned by confirmation are recursively evaluated
+        // so we need to make sure they have the correct depth.
+        for subobligation in impl_src.borrow_nested_obligations_mut() {
+            subobligation.set_depth_from_parent(obligation.recursion_depth);
+        }
+
+        Ok(impl_src)
+    }
+
+    fn confirm_projection_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        idx: usize,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        let tcx = self.tcx();
+
+        let trait_predicate = self.infcx.shallow_resolve(obligation.predicate);
+        let placeholder_trait_predicate =
+            self.infcx.instantiate_binder_with_placeholders(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 = 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,
+            obligation.param_env,
+            obligation.cause.clone(),
+            obligation.recursion_depth + 1,
+            candidate,
+            &mut obligations,
+        );
+
+        obligations.extend(self.infcx.commit_if_ok(|_| {
+            self.infcx
+                .at(&obligation.cause, obligation.param_env)
+                .sup(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate)
+                .map(|InferOk { obligations, .. }| obligations)
+                .map_err(|_| Unimplemented)
+        })?);
+
+        if let ty::Alias(ty::Projection, ..) = placeholder_self_ty.kind() {
+            let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
+            for (predicate, _) in predicates {
+                let normalized = normalize_with_depth_to(
+                    self,
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    predicate,
+                    &mut obligations,
+                );
+                obligations.push(Obligation::with_depth(
+                    self.tcx(),
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.param_env,
+                    normalized,
+                ));
+            }
+        }
+
+        Ok(obligations)
+    }
+
+    fn confirm_param_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        param: ty::PolyTraitRef<'tcx>,
+    ) -> Vec<PredicateObligation<'tcx>> {
+        debug!(?obligation, ?param, "confirm_param_candidate");
+
+        // During evaluation, we already checked that this
+        // where-clause trait-ref could be unified with the obligation
+        // trait-ref. Repeat that unification now without any
+        // transactional boundary; it should not fail.
+        match self.match_where_clause_trait_ref(obligation, param) {
+            Ok(obligations) => obligations,
+            Err(()) => {
+                bug!(
+                    "Where clause `{:?}` was applicable to `{:?}` but now is not",
+                    param,
+                    obligation
+                );
+            }
+        }
+    }
+
+    fn confirm_builtin_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        has_nested: bool,
+    ) -> Vec<PredicateObligation<'tcx>> {
+        debug!(?obligation, ?has_nested, "confirm_builtin_candidate");
+
+        let lang_items = self.tcx().lang_items();
+        let obligations = if has_nested {
+            let trait_def = obligation.predicate.def_id();
+            let conditions = if Some(trait_def) == lang_items.sized_trait() {
+                self.sized_conditions(obligation)
+            } else if Some(trait_def) == lang_items.copy_trait() {
+                self.copy_clone_conditions(obligation)
+            } else if Some(trait_def) == lang_items.clone_trait() {
+                self.copy_clone_conditions(obligation)
+            } else {
+                bug!("unexpected builtin trait {:?}", trait_def)
+            };
+            let BuiltinImplConditions::Where(nested) = conditions else {
+                bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation);
+            };
+
+            let cause = obligation.derived_cause(BuiltinDerivedObligation);
+            self.collect_predicates_for_types(
+                obligation.param_env,
+                cause,
+                obligation.recursion_depth + 1,
+                trait_def,
+                nested,
+            )
+        } else {
+            vec![]
+        };
+
+        debug!(?obligations);
+
+        obligations
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn confirm_transmutability_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        use rustc_transmute::{Answer, Condition};
+        #[instrument(level = "debug", skip(tcx, obligation, predicate))]
+        fn flatten_answer_tree<'tcx>(
+            tcx: TyCtxt<'tcx>,
+            obligation: &PolyTraitObligation<'tcx>,
+            predicate: TraitPredicate<'tcx>,
+            cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
+        ) -> Vec<PredicateObligation<'tcx>> {
+            match cond {
+                // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
+                // Not possible until the trait solver supports disjunctions of obligations
+                Condition::IfAll(conds) | Condition::IfAny(conds) => conds
+                    .into_iter()
+                    .flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
+                    .collect(),
+                Condition::IfTransmutable { src, dst } => {
+                    let trait_def_id = obligation.predicate.def_id();
+                    let scope = predicate.trait_ref.args.type_at(2);
+                    let assume_const = predicate.trait_ref.args.const_at(3);
+                    let make_obl = |from_ty, to_ty| {
+                        let trait_ref1 = ty::TraitRef::new(
+                            tcx,
+                            trait_def_id,
+                            [
+                                ty::GenericArg::from(to_ty),
+                                ty::GenericArg::from(from_ty),
+                                ty::GenericArg::from(scope),
+                                ty::GenericArg::from(assume_const),
+                            ],
+                        );
+                        Obligation::with_depth(
+                            tcx,
+                            obligation.cause.clone(),
+                            obligation.recursion_depth + 1,
+                            obligation.param_env,
+                            trait_ref1,
+                        )
+                    };
+
+                    // If Dst is mutable, check bidirectionally.
+                    // For example, transmuting bool -> u8 is OK as long as you can't update that u8
+                    // to be > 1, because you could later transmute the u8 back to a bool and get UB.
+                    match dst.mutability {
+                        Mutability::Not => vec![make_obl(src.ty, dst.ty)],
+                        Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)],
+                    }
+                }
+            }
+        }
+
+        // We erase regions here because transmutability calls layout queries,
+        // which does not handle inference regions and doesn't particularly
+        // care about other regions. Erasing late-bound regions is equivalent
+        // to instantiating the binder with placeholders then erasing those
+        // placeholder regions.
+        let predicate = self
+            .tcx()
+            .erase_regions(self.tcx().instantiate_bound_regions_with_erased(obligation.predicate));
+
+        let Some(assume) = rustc_transmute::Assume::from_const(
+            self.infcx.tcx,
+            obligation.param_env,
+            predicate.trait_ref.args.const_at(3),
+        ) else {
+            return Err(Unimplemented);
+        };
+
+        let dst = predicate.trait_ref.args.type_at(0);
+        let src = predicate.trait_ref.args.type_at(1);
+        debug!(?src, ?dst);
+        let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
+        let maybe_transmutable = transmute_env.is_transmutable(
+            obligation.cause.clone(),
+            rustc_transmute::Types { dst, src },
+            predicate.trait_ref.args.type_at(2),
+            assume,
+        );
+
+        let fully_flattened = match maybe_transmutable {
+            Answer::No(_) => Err(Unimplemented)?,
+            Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond),
+            Answer::Yes => vec![],
+        };
+
+        debug!(?fully_flattened);
+        Ok(fully_flattened)
+    }
+
+    /// This handles the case where an `auto trait Foo` impl is being used.
+    /// The idea is that the impl applies to `X : Foo` if the following conditions are met:
+    ///
+    /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds
+    /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds.
+    fn confirm_auto_impl_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        debug!(?obligation, "confirm_auto_impl_candidate");
+
+        let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty());
+        let types = self.constituent_types_for_ty(self_ty)?;
+        Ok(self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types))
+    }
+
+    /// See `confirm_auto_impl_candidate`.
+    fn vtable_auto_impl(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        trait_def_id: DefId,
+        nested: ty::Binder<'tcx, Vec<Ty<'tcx>>>,
+    ) -> Vec<PredicateObligation<'tcx>> {
+        debug!(?nested, "vtable_auto_impl");
+        ensure_sufficient_stack(|| {
+            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_obligations: Vec<PredicateObligation<'_>> = self.impl_or_trait_obligations(
+                &cause,
+                obligation.recursion_depth + 1,
+                obligation.param_env,
+                trait_def_id,
+                trait_ref.args,
+                obligation.predicate,
+            );
+
+            let mut obligations = self.collect_predicates_for_types(
+                obligation.param_env,
+                cause,
+                obligation.recursion_depth + 1,
+                trait_def_id,
+                nested,
+            );
+
+            // Adds the predicates from the trait. Note that this contains a `Self: Trait`
+            // predicate as usual. It won't have any effect since auto traits are coinductive.
+            obligations.extend(trait_obligations);
+
+            debug!(?obligations, "vtable_auto_impl");
+
+            obligations
+        })
+    }
+
+    fn confirm_impl_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        impl_def_id: DefId,
+    ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
+        debug!(?obligation, ?impl_def_id, "confirm_impl_candidate");
+
+        // First, create the substitutions by matching the impl again,
+        // this time not in a probe.
+        let args = self.rematch_impl(impl_def_id, obligation);
+        debug!(?args, "impl args");
+        ensure_sufficient_stack(|| {
+            self.vtable_impl(
+                impl_def_id,
+                args,
+                &obligation.cause,
+                obligation.recursion_depth + 1,
+                obligation.param_env,
+                obligation.predicate,
+            )
+        })
+    }
+
+    fn vtable_impl(
+        &mut self,
+        impl_def_id: DefId,
+        args: Normalized<'tcx, GenericArgsRef<'tcx>>,
+        cause: &ObligationCause<'tcx>,
+        recursion_depth: usize,
+        param_env: ty::ParamEnv<'tcx>,
+        parent_trait_pred: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
+    ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
+        debug!(?impl_def_id, ?args, ?recursion_depth, "vtable_impl");
+
+        let mut impl_obligations = self.impl_or_trait_obligations(
+            cause,
+            recursion_depth,
+            param_env,
+            impl_def_id,
+            args.value,
+            parent_trait_pred,
+        );
+
+        debug!(?impl_obligations, "vtable_impl");
+
+        // Because of RFC447, the impl-trait-ref and obligations
+        // are sufficient to determine the impl args, without
+        // relying on projections in the impl-trait-ref.
+        //
+        // e.g., `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V`
+        impl_obligations.extend(args.obligations);
+
+        ImplSourceUserDefinedData { impl_def_id, args: args.value, nested: impl_obligations }
+    }
+
+    fn confirm_object_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        index: usize,
+    ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        let tcx = self.tcx();
+        debug!(?obligation, ?index, "confirm_object_candidate");
+
+        let trait_predicate = self.infcx.instantiate_binder_with_placeholders(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 {
+            span_bug!(obligation.cause.span, "object candidate with non-object");
+        };
+
+        let object_trait_ref = data.principal().unwrap_or_else(|| {
+            span_bug!(obligation.cause.span, "object candidate with no principal")
+        });
+        let object_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
+            obligation.cause.span,
+            HigherRankedType,
+            object_trait_ref,
+        );
+        let object_trait_ref = object_trait_ref.with_self_ty(self.tcx(), self_ty);
+
+        let mut nested = vec![];
+
+        let mut supertraits = util::supertraits(tcx, ty::Binder::dummy(object_trait_ref));
+        let unnormalized_upcast_trait_ref =
+            supertraits.nth(index).expect("supertraits iterator no longer has as many elements");
+
+        let upcast_trait_ref = normalize_with_depth_to(
+            self,
+            obligation.param_env,
+            obligation.cause.clone(),
+            obligation.recursion_depth + 1,
+            unnormalized_upcast_trait_ref,
+            &mut nested,
+        );
+
+        nested.extend(self.infcx.commit_if_ok(|_| {
+            self.infcx
+                .at(&obligation.cause, obligation.param_env)
+                .sup(DefineOpaqueTypes::No, obligation_trait_ref, upcast_trait_ref)
+                .map(|InferOk { obligations, .. }| obligations)
+                .map_err(|_| Unimplemented)
+        })?);
+
+        // Check supertraits hold. This is so that their associated type bounds
+        // will be checked in the code below.
+        for super_trait in tcx
+            .super_predicates_of(trait_predicate.def_id())
+            .instantiate(tcx, trait_predicate.trait_ref.args)
+            .predicates
+            .into_iter()
+        {
+            let normalized_super_trait = normalize_with_depth_to(
+                self,
+                obligation.param_env,
+                obligation.cause.clone(),
+                obligation.recursion_depth + 1,
+                super_trait,
+                &mut nested,
+            );
+            nested.push(obligation.with(tcx, normalized_super_trait));
+        }
+
+        let assoc_types: Vec<_> = tcx
+            .associated_items(trait_predicate.def_id())
+            .in_definition_order()
+            // Associated types that require `Self: Sized` do not show up in the built-in
+            // implementation of `Trait for dyn Trait`, and can be dropped here.
+            .filter(|item| !tcx.generics_require_sized_self(item.def_id))
+            .filter_map(
+                |item| if item.kind == ty::AssocKind::Type { Some(item.def_id) } else { None },
+            )
+            .collect();
+
+        for assoc_type in assoc_types {
+            let defs: &ty::Generics = tcx.generics_of(assoc_type);
+
+            if !defs.params.is_empty() && !tcx.features().generic_associated_types_extended {
+                tcx.dcx().span_delayed_bug(
+                    obligation.cause.span,
+                    "GATs in trait object shouldn't have been considered",
+                );
+                return Err(SelectionError::TraitNotObjectSafe(trait_predicate.trait_ref.def_id));
+            }
+
+            // This maybe belongs in wf, but that can't (doesn't) handle
+            // higher-ranked things.
+            // Prevent, e.g., `dyn Iterator<Item = str>`.
+            for bound in self.tcx().item_bounds(assoc_type).transpose_iter() {
+                let subst_bound = if defs.count() == 0 {
+                    bound.instantiate(tcx, trait_predicate.trait_ref.args)
+                } else {
+                    let mut args = smallvec::SmallVec::with_capacity(defs.count());
+                    args.extend(trait_predicate.trait_ref.args.iter());
+                    let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
+                        smallvec::SmallVec::with_capacity(
+                            bound.skip_binder().kind().bound_vars().len() + defs.count(),
+                        );
+                    bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter());
+                    GenericArgs::fill_single(&mut args, defs, &mut |param, _| match param.kind {
+                        GenericParamDefKind::Type { .. } => {
+                            let kind = ty::BoundTyKind::Param(param.def_id, param.name);
+                            let bound_var = ty::BoundVariableKind::Ty(kind);
+                            bound_vars.push(bound_var);
+                            Ty::new_bound(
+                                tcx,
+                                ty::INNERMOST,
+                                ty::BoundTy {
+                                    var: ty::BoundVar::from_usize(bound_vars.len() - 1),
+                                    kind,
+                                },
+                            )
+                            .into()
+                        }
+                        GenericParamDefKind::Lifetime => {
+                            let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
+                            let bound_var = ty::BoundVariableKind::Region(kind);
+                            bound_vars.push(bound_var);
+                            ty::Region::new_bound(
+                                tcx,
+                                ty::INNERMOST,
+                                ty::BoundRegion {
+                                    var: ty::BoundVar::from_usize(bound_vars.len() - 1),
+                                    kind,
+                                },
+                            )
+                            .into()
+                        }
+                        GenericParamDefKind::Const { .. } => {
+                            let bound_var = ty::BoundVariableKind::Const;
+                            bound_vars.push(bound_var);
+                            ty::Const::new_bound(
+                                tcx,
+                                ty::INNERMOST,
+                                ty::BoundVar::from_usize(bound_vars.len() - 1),
+                                tcx.type_of(param.def_id)
+                                    .no_bound_vars()
+                                    .expect("const parameter types cannot be generic"),
+                            )
+                            .into()
+                        }
+                    });
+                    let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
+                    let assoc_ty_args = tcx.mk_args(&args);
+                    let bound =
+                        bound.map_bound(|b| b.kind().skip_binder()).instantiate(tcx, assoc_ty_args);
+                    ty::Binder::bind_with_vars(bound, bound_vars).to_predicate(tcx)
+                };
+                let normalized_bound = normalize_with_depth_to(
+                    self,
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    subst_bound,
+                    &mut nested,
+                );
+                nested.push(obligation.with(tcx, normalized_bound));
+            }
+        }
+
+        debug!(?nested, "object nested obligations");
+
+        let vtable_base = vtable_trait_first_method_offset(
+            tcx,
+            (unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)),
+        );
+
+        Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested))
+    }
+
+    fn confirm_fn_pointer_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        fn_host_effect: ty::Const<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        debug!(?obligation, "confirm_fn_pointer_candidate");
+
+        let tcx = self.tcx();
+
+        let Some(self_ty) = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars()) else {
+            // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`,
+            // but we do not currently. Luckily, such a bound is not
+            // particularly useful, so we don't expect users to write
+            // them often.
+            return Err(SelectionError::Unimplemented);
+        };
+
+        let sig = self_ty.fn_sig(tcx);
+        let trait_ref = closure_trait_ref_and_return_type(
+            tcx,
+            obligation.predicate.def_id(),
+            self_ty,
+            sig,
+            util::TupleArgumentsFlag::Yes,
+            fn_host_effect,
+        )
+        .map_bound(|(trait_ref, _)| trait_ref);
+
+        let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
+        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 = normalize_with_depth_to(
+            self,
+            obligation.param_env,
+            cause.clone(),
+            obligation.recursion_depth,
+            output_ty,
+            &mut nested,
+        );
+        let tr = ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [output_ty]);
+        nested.push(Obligation::new(self.infcx.tcx, cause, obligation.param_env, tr));
+
+        Ok(nested)
+    }
+
+    fn confirm_trait_alias_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Vec<PredicateObligation<'tcx>> {
+        debug!(?obligation, "confirm_trait_alias_candidate");
+
+        let predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
+        let trait_ref = predicate.trait_ref;
+        let trait_def_id = trait_ref.def_id;
+        let args = trait_ref.args;
+
+        let trait_obligations = self.impl_or_trait_obligations(
+            &obligation.cause,
+            obligation.recursion_depth,
+            obligation.param_env,
+            trait_def_id,
+            args,
+            obligation.predicate,
+        );
+
+        debug!(?trait_def_id, ?trait_obligations, "trait alias obligations");
+
+        trait_obligations
+    }
+
+    fn confirm_coroutine_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        // Okay to skip binder because the args on coroutine types never
+        // 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::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
+            bug!("closure candidate for non-closure {:?}", obligation);
+        };
+
+        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_coroutine_candidate");
+
+        let coroutine_sig = args.as_coroutine().sig();
+
+        // NOTE: The self-type is a coroutine type and hence is
+        // in fact unparameterized (or at least does not reference any
+        // regions bound in the obligation).
+        let self_ty = obligation
+            .predicate
+            .self_ty()
+            .no_bound_vars()
+            .expect("unboxed closure type should not capture bound vars from the predicate");
+
+        let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs(
+            self.tcx(),
+            obligation.predicate.def_id(),
+            self_ty,
+            coroutine_sig,
+        );
+
+        let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
+        debug!(?trait_ref, ?nested, "coroutine candidate obligations");
+
+        Ok(nested)
+    }
+
+    fn confirm_future_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        // Okay to skip binder because the args on coroutine types never
+        // 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::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
+            bug!("closure candidate for non-closure {:?}", obligation);
+        };
+
+        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_future_candidate");
+
+        let coroutine_sig = args.as_coroutine().sig();
+
+        let (trait_ref, _) = super::util::future_trait_ref_and_outputs(
+            self.tcx(),
+            obligation.predicate.def_id(),
+            obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(),
+            coroutine_sig,
+        );
+
+        let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
+        debug!(?trait_ref, ?nested, "future candidate obligations");
+
+        Ok(nested)
+    }
+
+    fn confirm_iterator_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        // Okay to skip binder because the args on coroutine types never
+        // 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::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
+            bug!("closure candidate for non-closure {:?}", obligation);
+        };
+
+        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_iterator_candidate");
+
+        let gen_sig = args.as_coroutine().sig();
+
+        let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs(
+            self.tcx(),
+            obligation.predicate.def_id(),
+            obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
+            gen_sig,
+        );
+
+        let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
+        debug!(?trait_ref, ?nested, "iterator candidate obligations");
+
+        Ok(nested)
+    }
+
+    fn confirm_async_iterator_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        // Okay to skip binder because the args on coroutine types never
+        // 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::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
+            bug!("closure candidate for non-closure {:?}", obligation);
+        };
+
+        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_async_iterator_candidate");
+
+        let gen_sig = args.as_coroutine().sig();
+
+        let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs(
+            self.tcx(),
+            obligation.predicate.def_id(),
+            obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
+            gen_sig,
+        );
+
+        let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
+        debug!(?trait_ref, ?nested, "iterator candidate obligations");
+
+        Ok(nested)
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn confirm_closure_candidate(
+        &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 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 =
+            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)
+    }
+
+    /// In the case of closure types and fn pointers,
+    /// we currently treat the input type parameters on the trait as
+    /// outputs. This means that when we have a match we have only
+    /// considered the self type, so we have to go back and make sure
+    /// to relate the argument types too. This is kind of wrong, but
+    /// since we control the full set of impls, also not that wrong,
+    /// and it DOES yield better error messages (since we don't report
+    /// errors as if there is no applicable impl, but rather report
+    /// errors are about mismatched argument types.
+    ///
+    /// Here is an example. Imagine we have a closure expression
+    /// and we desugared it so that the type of the expression is
+    /// `Closure`, and `Closure` expects `i32` as argument. Then it
+    /// is "as if" the compiler generated this impl:
+    /// ```ignore (illustrative)
+    /// impl Fn(i32) for Closure { ... }
+    /// ```
+    /// Now imagine our obligation is `Closure: Fn(usize)`. So far
+    /// we have matched the self type `Closure`. At this point we'll
+    /// compare the `i32` to `usize` and generate an error.
+    ///
+    /// Note that this checking occurs *after* the impl has selected,
+    /// because these output type parameters should not affect the
+    /// selection of the impl. Therefore, if there is a mismatch, we
+    /// report an error to the user.
+    #[instrument(skip(self), level = "trace")]
+    fn confirm_poly_trait_refs(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        self_ty_trait_ref: ty::PolyTraitRef<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        let obligation_trait_ref = obligation.predicate.to_poly_trait_ref();
+        // Normalize the obligation and expected trait refs together, because why not
+        let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } =
+            ensure_sufficient_stack(|| {
+                normalize_with_depth(
+                    self,
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    (obligation_trait_ref, self_ty_trait_ref),
+                )
+            });
+
+        // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs
+        self.infcx
+            .at(&obligation.cause, obligation.param_env)
+            .sup(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref)
+            .map(|InferOk { mut obligations, .. }| {
+                obligations.extend(nested);
+                obligations
+            })
+            .map_err(|terr| {
+                OutputTypeParameterMismatch(Box::new(SelectionOutputTypeParameterMismatch {
+                    expected_trait_ref: obligation_trait_ref,
+                    found_trait_ref: expected_trait_ref,
+                    terr,
+                }))
+            })
+    }
+
+    fn confirm_trait_upcasting_unsize_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        idx: usize,
+    ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        let tcx = self.tcx();
+
+        // `assemble_candidates_for_unsizing` should ensure there are no late-bound
+        // regions here. See the comment there for more details.
+        let predicate = obligation.predicate.no_bound_vars().unwrap();
+        let a_ty = self.infcx.shallow_resolve(predicate.self_ty());
+        let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1));
+
+        let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
+            bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`")
+        };
+        let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
+            bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`")
+        };
+
+        let source_principal = a_data.principal().unwrap().with_self_ty(tcx, a_ty);
+        let unnormalized_upcast_principal =
+            util::supertraits(tcx, source_principal).nth(idx).unwrap();
+
+        let nested = self
+            .match_upcast_principal(
+                obligation,
+                unnormalized_upcast_principal,
+                a_data,
+                b_data,
+                a_region,
+                b_region,
+            )?
+            .expect("did not expect ambiguity during confirmation");
+
+        let vtable_segment_callback = {
+            let mut vptr_offset = 0;
+            move |segment| {
+                match segment {
+                    VtblSegment::MetadataDSA => {
+                        vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
+                    }
+                    VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+                        vptr_offset += count_own_vtable_entries(tcx, trait_ref);
+                        if trait_ref == unnormalized_upcast_principal {
+                            if emit_vptr {
+                                return ControlFlow::Break(Some(vptr_offset));
+                            } else {
+                                return ControlFlow::Break(None);
+                            }
+                        }
+
+                        if emit_vptr {
+                            vptr_offset += 1;
+                        }
+                    }
+                }
+                ControlFlow::Continue(())
+            }
+        };
+
+        let vtable_vptr_slot =
+            prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap();
+
+        Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested))
+    }
+
+    fn confirm_builtin_unsize_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        let tcx = self.tcx();
+
+        // `assemble_candidates_for_unsizing` should ensure there are no late-bound
+        // regions here. See the comment there for more details.
+        let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap());
+        let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
+        let target = self.infcx.shallow_resolve(target);
+        debug!(?source, ?target, "confirm_builtin_unsize_candidate");
+
+        Ok(match (source.kind(), target.kind()) {
+            // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping).
+            (&ty::Dynamic(data_a, r_a, dyn_a), &ty::Dynamic(data_b, r_b, dyn_b))
+                if dyn_a == dyn_b =>
+            {
+                // See `assemble_candidates_for_unsizing` for more info.
+                // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`.
+                let iter = data_a
+                    .principal()
+                    .map(|b| b.map_bound(ty::ExistentialPredicate::Trait))
+                    .into_iter()
+                    .chain(
+                        data_a
+                            .projection_bounds()
+                            .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)),
+                    )
+                    .chain(
+                        data_b
+                            .auto_traits()
+                            .map(ty::ExistentialPredicate::AutoTrait)
+                            .map(ty::Binder::dummy),
+                    );
+                let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter);
+                let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, dyn_a);
+
+                // Require that the traits involved in this upcast are **equal**;
+                // only the **lifetime bound** is changed.
+                let InferOk { mut obligations, .. } = self
+                    .infcx
+                    .at(&obligation.cause, obligation.param_env)
+                    .sup(DefineOpaqueTypes::No, target, source_trait)
+                    .map_err(|_| Unimplemented)?;
+
+                // Register one obligation for 'a: 'b.
+                let outlives = ty::OutlivesPredicate(r_a, r_b);
+                obligations.push(Obligation::with_depth(
+                    tcx,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.param_env,
+                    obligation.predicate.rebind(outlives),
+                ));
+
+                ImplSource::Builtin(BuiltinImplSource::Misc, obligations)
+            }
+
+            // `T` -> `Trait`
+            (_, &ty::Dynamic(data, r, ty::Dyn)) => {
+                let mut object_dids = data.auto_traits().chain(data.principal_def_id());
+                if let Some(did) = object_dids.find(|did| !tcx.check_is_object_safe(*did)) {
+                    return Err(TraitNotObjectSafe(did));
+                }
+
+                let predicate_to_obligation = |predicate| {
+                    Obligation::with_depth(
+                        tcx,
+                        obligation.cause.clone(),
+                        obligation.recursion_depth + 1,
+                        obligation.param_env,
+                        predicate,
+                    )
+                };
+
+                // Create obligations:
+                //  - Casting `T` to `Trait`
+                //  - For all the various builtin bounds attached to the object cast. (In other
+                //  words, if the object type is `Foo + Send`, this would create an obligation for
+                //  the `Send` check.)
+                //  - Projection predicates
+                let mut nested: Vec<_> = data
+                    .iter()
+                    .map(|predicate| predicate_to_obligation(predicate.with_self_ty(tcx, source)))
+                    .collect();
+
+                // We can only make objects from sized types.
+                let tr = ty::TraitRef::from_lang_item(
+                    tcx,
+                    LangItem::Sized,
+                    obligation.cause.span,
+                    [source],
+                );
+                nested.push(predicate_to_obligation(tr.to_predicate(tcx)));
+
+                // If the type is `Foo + 'a`, ensure that the type
+                // being cast to `Foo + 'a` outlives `'a`:
+                let outlives = ty::OutlivesPredicate(source, r);
+                nested.push(predicate_to_obligation(
+                    ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx),
+                ));
+
+                ImplSource::Builtin(BuiltinImplSource::Misc, nested)
+            }
+
+            // `[T; n]` -> `[T]`
+            (&ty::Array(a, _), &ty::Slice(b)) => {
+                let InferOk { obligations, .. } = self
+                    .infcx
+                    .at(&obligation.cause, obligation.param_env)
+                    .eq(DefineOpaqueTypes::No, b, a)
+                    .map_err(|_| Unimplemented)?;
+
+                ImplSource::Builtin(BuiltinImplSource::Misc, obligations)
+            }
+
+            // `Struct<T>` -> `Struct<U>`
+            (&ty::Adt(def, args_a), &ty::Adt(_, args_b)) => {
+                let unsizing_params = tcx.unsizing_params_for_adt(def.did());
+                if unsizing_params.is_empty() {
+                    return Err(Unimplemented);
+                }
+
+                let tail_field = def.non_enum_variant().tail();
+                let tail_field_ty = tcx.type_of(tail_field.did);
+
+                let mut nested = vec![];
+
+                // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`,
+                // normalizing in the process, since `type_of` returns something directly from
+                // astconv (which means it's un-normalized).
+                let source_tail = normalize_with_depth_to(
+                    self,
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    tail_field_ty.instantiate(tcx, args_a),
+                    &mut nested,
+                );
+                let target_tail = normalize_with_depth_to(
+                    self,
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    tail_field_ty.instantiate(tcx, args_b),
+                    &mut nested,
+                );
+
+                // Check that the source struct with the target's
+                // unsizing parameters is equal to the target.
+                let args =
+                    tcx.mk_args_from_iter(args_a.iter().enumerate().map(|(i, k)| {
+                        if unsizing_params.contains(i as u32) { args_b[i] } else { k }
+                    }));
+                let new_struct = Ty::new_adt(tcx, def, args);
+                let InferOk { obligations, .. } = self
+                    .infcx
+                    .at(&obligation.cause, obligation.param_env)
+                    .eq(DefineOpaqueTypes::No, target, new_struct)
+                    .map_err(|_| Unimplemented)?;
+                nested.extend(obligations);
+
+                // Construct the nested `TailField<T>: Unsize<TailField<U>>` predicate.
+                let tail_unsize_obligation = obligation.with(
+                    tcx,
+                    ty::TraitRef::new(
+                        tcx,
+                        obligation.predicate.def_id(),
+                        [source_tail, target_tail],
+                    ),
+                );
+                nested.push(tail_unsize_obligation);
+
+                ImplSource::Builtin(BuiltinImplSource::Misc, nested)
+            }
+
+            // `(.., T)` -> `(.., U)`
+            (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => {
+                assert_eq!(tys_a.len(), tys_b.len());
+
+                // The last field of the tuple has to exist.
+                let (&a_last, a_mid) = tys_a.split_last().ok_or(Unimplemented)?;
+                let &b_last = tys_b.last().unwrap();
+
+                // Check that the source tuple with the target's
+                // last element is equal to the target.
+                let new_tuple =
+                    Ty::new_tup_from_iter(tcx, a_mid.iter().copied().chain(iter::once(b_last)));
+                let InferOk { mut obligations, .. } = self
+                    .infcx
+                    .at(&obligation.cause, obligation.param_env)
+                    .eq(DefineOpaqueTypes::No, target, new_tuple)
+                    .map_err(|_| Unimplemented)?;
+
+                // Add a nested `T: Unsize<U>` predicate.
+                let last_unsize_obligation = obligation.with(
+                    tcx,
+                    ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]),
+                );
+                obligations.push(last_unsize_obligation);
+
+                ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, obligations)
+            }
+
+            _ => bug!("source: {source}, target: {target}"),
+        })
+    }
+
+    fn confirm_const_destruct_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        impl_def_id: Option<DefId>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        let Some(host_effect_index) =
+            self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index
+        else {
+            bug!()
+        };
+        let host_effect_param: ty::GenericArg<'tcx> =
+            obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index).into();
+
+        let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None);
+
+        let tcx = self.tcx();
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
+
+        let mut nested = vec![];
+        let cause = obligation.derived_cause(BuiltinDerivedObligation);
+
+        // If we have a custom `impl const Drop`, then
+        // first check it like a regular impl candidate.
+        // This is copied from confirm_impl_candidate but remaps the predicate to `~const Drop` beforehand.
+        if let Some(impl_def_id) = impl_def_id {
+            let mut new_obligation = obligation.clone();
+            new_obligation.predicate = new_obligation.predicate.map_bound(|mut trait_pred| {
+                trait_pred.trait_ref.def_id = drop_trait;
+                trait_pred
+            });
+            let args = self.rematch_impl(impl_def_id, &new_obligation);
+            debug!(?args, "impl args");
+
+            let cause = obligation.derived_cause(|derived| {
+                ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
+                    derived,
+                    impl_or_alias_def_id: impl_def_id,
+                    impl_def_predicate_index: None,
+                    span: obligation.cause.span,
+                }))
+            });
+            let obligations = ensure_sufficient_stack(|| {
+                self.vtable_impl(
+                    impl_def_id,
+                    args,
+                    &cause,
+                    new_obligation.recursion_depth + 1,
+                    new_obligation.param_env,
+                    obligation.predicate,
+                )
+            });
+            nested.extend(obligations.nested);
+        }
+
+        // We want to confirm the ADT's fields if we have an ADT
+        let mut stack = match *self_ty.skip_binder().kind() {
+            ty::Adt(def, args) => def.all_fields().map(|f| f.ty(tcx, args)).collect(),
+            _ => vec![self_ty.skip_binder()],
+        };
+
+        while let Some(nested_ty) = stack.pop() {
+            match *nested_ty.kind() {
+                // We know these types are trivially drop
+                ty::Bool
+                | ty::Char
+                | ty::Int(_)
+                | ty::Uint(_)
+                | ty::Float(_)
+                | ty::Infer(ty::IntVar(_))
+                | ty::Infer(ty::FloatVar(_))
+                | ty::Str
+                | ty::RawPtr(_)
+                | ty::Ref(..)
+                | ty::FnDef(..)
+                | ty::FnPtr(_)
+                | ty::Never
+                | ty::Foreign(_) => {}
+
+                // `ManuallyDrop` is trivially drop
+                ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().manually_drop() => {}
+
+                // These types are built-in, so we can fast-track by registering
+                // nested predicates for their constituent type(s)
+                ty::Array(ty, _) | ty::Slice(ty) => {
+                    stack.push(ty);
+                }
+                ty::Tuple(tys) => {
+                    stack.extend(tys.iter());
+                }
+                ty::Closure(_, args) => {
+                    stack.push(args.as_closure().tupled_upvars_ty());
+                }
+                ty::Coroutine(_, args) => {
+                    let coroutine = args.as_coroutine();
+                    stack.extend([coroutine.tupled_upvars_ty(), coroutine.witness()]);
+                }
+                ty::CoroutineWitness(def_id, args) => {
+                    let tcx = self.tcx();
+                    stack.extend(tcx.coroutine_hidden_types(def_id).map(|bty| {
+                        let ty = bty.instantiate(tcx, args);
+                        debug_assert!(!ty.has_bound_regions());
+                        ty
+                    }))
+                }
+
+                // If we have a projection type, make sure to normalize it so we replace it
+                // with a fresh infer variable
+                ty::Alias(ty::Projection | ty::Inherent, ..) => {
+                    let predicate = normalize_with_depth_to(
+                        self,
+                        obligation.param_env,
+                        cause.clone(),
+                        obligation.recursion_depth + 1,
+                        self_ty.rebind(ty::TraitPredicate {
+                            trait_ref: ty::TraitRef::from_lang_item(
+                                self.tcx(),
+                                LangItem::Destruct,
+                                cause.span,
+                                [nested_ty.into(), host_effect_param],
+                            ),
+                            polarity: ty::ImplPolarity::Positive,
+                        }),
+                        &mut nested,
+                    );
+
+                    nested.push(Obligation::with_depth(
+                        tcx,
+                        cause.clone(),
+                        obligation.recursion_depth + 1,
+                        obligation.param_env,
+                        predicate,
+                    ));
+                }
+
+                // If we have any other type (e.g. an ADT), just register a nested obligation
+                // since it's either not `const Drop` (and we raise an error during selection),
+                // or it's an ADT (and we need to check for a custom impl during selection)
+                _ => {
+                    let predicate = self_ty.rebind(ty::TraitPredicate {
+                        trait_ref: ty::TraitRef::from_lang_item(
+                            self.tcx(),
+                            LangItem::Destruct,
+                            cause.span,
+                            [nested_ty.into(), host_effect_param],
+                        ),
+                        polarity: ty::ImplPolarity::Positive,
+                    });
+
+                    nested.push(Obligation::with_depth(
+                        tcx,
+                        cause.clone(),
+                        obligation.recursion_depth + 1,
+                        obligation.param_env,
+                        predicate,
+                    ));
+                }
+            }
+        }
+
+        Ok(nested)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
new file mode 100644
index 00000000000..c45925295ee
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -0,0 +1,3116 @@
+//! Candidate selection. See the [rustc dev guide] for more information on how this works.
+//!
+//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection
+
+use self::EvaluationResult::*;
+use self::SelectionCandidate::*;
+
+use super::coherence::{self, Conflict};
+use super::const_evaluatable;
+use super::project;
+use super::project::normalize_with_depth_to;
+use super::project::ProjectionTyObligation;
+use super::util;
+use super::util::closure_trait_ref_and_return_type;
+use super::wf;
+use super::{
+    ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, Normalized, Obligation,
+    ObligationCause, ObligationCauseCode, Overflow, PolyTraitObligation, PredicateObligation,
+    Selection, SelectionError, SelectionResult, TraitQueryMode,
+};
+
+use crate::infer::{InferCtxt, InferOk, TypeFreshener};
+use crate::solve::InferCtxtSelectExt;
+use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::project::try_normalize_with_depth_to;
+use crate::traits::project::ProjectAndUnifyResult;
+use crate::traits::project::ProjectionCacheKeyExt;
+use crate::traits::ProjectionCacheKey;
+use crate::traits::Unimplemented;
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_errors::Diagnostic;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::BoundRegionConversionTime;
+use rustc_infer::infer::DefineOpaqueTypes;
+use rustc_infer::traits::TraitObligation;
+use rustc_middle::dep_graph::dep_kinds;
+use rustc_middle::dep_graph::DepNodeIndex;
+use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::ty::_match::MatchAgainstFreshVars;
+use rustc_middle::ty::abstract_const::NotConstEvaluatable;
+use rustc_middle::ty::fold::BottomUpFolder;
+use rustc_middle::ty::relate::TypeRelation;
+use rustc_middle::ty::GenericArgsRef;
+use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
+use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
+use rustc_span::symbol::sym;
+use rustc_span::Symbol;
+
+use std::cell::{Cell, RefCell};
+use std::cmp;
+use std::fmt::{self, Display};
+use std::iter;
+
+pub use rustc_middle::traits::select::*;
+use rustc_middle::ty::print::with_no_trimmed_paths;
+
+mod candidate_assembly;
+mod confirmation;
+
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum IntercrateAmbiguityCause<'tcx> {
+    DownstreamCrate { trait_ref: ty::TraitRef<'tcx>, self_ty: Option<Ty<'tcx>> },
+    UpstreamCrateUpdate { trait_ref: ty::TraitRef<'tcx>, self_ty: Option<Ty<'tcx>> },
+    ReservationImpl { message: Symbol },
+}
+
+impl<'tcx> IntercrateAmbiguityCause<'tcx> {
+    /// Emits notes when the overlap is caused by complex intercrate ambiguities.
+    /// See #23980 for details.
+    pub fn add_intercrate_ambiguity_hint(&self, err: &mut Diagnostic) {
+        err.note(self.intercrate_ambiguity_hint());
+    }
+
+    pub fn intercrate_ambiguity_hint(&self) -> String {
+        with_no_trimmed_paths!(match self {
+            IntercrateAmbiguityCause::DownstreamCrate { trait_ref, self_ty } => {
+                format!(
+                    "downstream crates may implement trait `{trait_desc}`{self_desc}",
+                    trait_desc = trait_ref.print_trait_sugared(),
+                    self_desc = if let Some(self_ty) = self_ty {
+                        format!(" for type `{self_ty}`")
+                    } else {
+                        String::new()
+                    }
+                )
+            }
+            IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_ref, self_ty } => {
+                format!(
+                    "upstream crates may add a new impl of trait `{trait_desc}`{self_desc} \
+                in future versions",
+                    trait_desc = trait_ref.print_trait_sugared(),
+                    self_desc = if let Some(self_ty) = self_ty {
+                        format!(" for type `{self_ty}`")
+                    } else {
+                        String::new()
+                    }
+                )
+            }
+            IntercrateAmbiguityCause::ReservationImpl { message } => message.to_string(),
+        })
+    }
+}
+
+pub struct SelectionContext<'cx, 'tcx> {
+    pub infcx: &'cx InferCtxt<'tcx>,
+
+    /// Freshener used specifically for entries on the obligation
+    /// stack. This ensures that all entries on the stack at one time
+    /// will have the same set of placeholder entries, which is
+    /// important for checking for trait bounds that recursively
+    /// require themselves.
+    freshener: TypeFreshener<'cx, 'tcx>,
+
+    /// If `intercrate` is set, we remember predicates which were
+    /// considered ambiguous because of impls potentially added in other crates.
+    /// This is used in coherence to give improved diagnostics.
+    /// We don't do his until we detect a coherence error because it can
+    /// lead to false overflow results (#47139) and because always
+    /// computing it may negatively impact performance.
+    intercrate_ambiguity_causes: Option<FxIndexSet<IntercrateAmbiguityCause<'tcx>>>,
+
+    /// The mode that trait queries run in, which informs our error handling
+    /// policy. In essence, canonicalized queries need their errors propagated
+    /// rather than immediately reported because we do not have accurate spans.
+    query_mode: TraitQueryMode,
+
+    treat_inductive_cycle: TreatInductiveCycleAs,
+}
+
+// A stack that walks back up the stack frame.
+struct TraitObligationStack<'prev, 'tcx> {
+    obligation: &'prev PolyTraitObligation<'tcx>,
+
+    /// The trait predicate from `obligation` but "freshened" with the
+    /// selection-context's freshener. Used to check for recursion.
+    fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
+
+    /// Starts out equal to `depth` -- if, during evaluation, we
+    /// encounter a cycle, then we will set this flag to the minimum
+    /// depth of that cycle for all participants in the cycle. These
+    /// participants will then forego caching their results. This is
+    /// not the most efficient solution, but it addresses #60010. The
+    /// problem we are trying to prevent:
+    ///
+    /// - If you have `A: AutoTrait` requires `B: AutoTrait` and `C: NonAutoTrait`
+    /// - `B: AutoTrait` requires `A: AutoTrait` (coinductive cycle, ok)
+    /// - `C: NonAutoTrait` requires `A: AutoTrait` (non-coinductive cycle, not ok)
+    ///
+    /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait`
+    /// is `EvaluatedToOk`; this is because they were only considered
+    /// ok on the premise that if `A: AutoTrait` held, but we indeed
+    /// encountered a problem (later on) with `A: AutoTrait`. So we
+    /// currently set a flag on the stack node for `B: AutoTrait` (as
+    /// well as the second instance of `A: AutoTrait`) to suppress
+    /// caching.
+    ///
+    /// This is a simple, targeted fix. A more-performant fix requires
+    /// deeper changes, but would permit more caching: we could
+    /// basically defer caching until we have fully evaluated the
+    /// tree, and then cache the entire tree at once. In any case, the
+    /// performance impact here shouldn't be so horrible: every time
+    /// this is hit, we do cache at least one trait, so we only
+    /// evaluate each member of a cycle up to N times, where N is the
+    /// length of the cycle. This means the performance impact is
+    /// bounded and we shouldn't have any terrible worst-cases.
+    reached_depth: Cell<usize>,
+
+    previous: TraitObligationStackList<'prev, 'tcx>,
+
+    /// The number of parent frames plus one (thus, the topmost frame has depth 1).
+    depth: usize,
+
+    /// The depth-first number of this node in the search graph -- a
+    /// pre-order index. Basically, a freshly incremented counter.
+    dfn: usize,
+}
+
+struct SelectionCandidateSet<'tcx> {
+    /// A list of candidates that definitely apply to the current
+    /// obligation (meaning: types unify).
+    vec: Vec<SelectionCandidate<'tcx>>,
+
+    /// If `true`, then there were candidates that might or might
+    /// not have applied, but we couldn't tell. This occurs when some
+    /// of the input types are type variables, in which case there are
+    /// various "builtin" rules that might or might not trigger.
+    ambiguous: bool,
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+struct EvaluatedCandidate<'tcx> {
+    candidate: SelectionCandidate<'tcx>,
+    evaluation: EvaluationResult,
+}
+
+/// When does the builtin impl for `T: Trait` apply?
+#[derive(Debug)]
+enum BuiltinImplConditions<'tcx> {
+    /// The impl is conditional on `T1, T2, ...: Trait`.
+    Where(ty::Binder<'tcx, Vec<Ty<'tcx>>>),
+    /// There is no built-in impl. There may be some other
+    /// candidate (a where-clause or user-defined impl).
+    None,
+    /// It is unknown whether there is an impl.
+    Ambiguous,
+}
+
+#[derive(Copy, Clone)]
+pub enum TreatInductiveCycleAs {
+    /// This is the previous behavior, where `Recur` represents an inductive
+    /// cycle that is known not to hold. This is not forwards-compatible with
+    /// coinduction, and will be deprecated. This is the default behavior
+    /// of the old trait solver due to back-compat reasons.
+    Recur,
+    /// This is the behavior of the new trait solver, where inductive cycles
+    /// are treated as ambiguous and possibly holding.
+    Ambig,
+}
+
+impl From<TreatInductiveCycleAs> for EvaluationResult {
+    fn from(treat: TreatInductiveCycleAs) -> EvaluationResult {
+        match treat {
+            TreatInductiveCycleAs::Ambig => EvaluatedToAmbigStackDependent,
+            TreatInductiveCycleAs::Recur => EvaluatedToErrStackDependent,
+        }
+    }
+}
+
+impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
+    pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> {
+        SelectionContext {
+            infcx,
+            freshener: infcx.freshener(),
+            intercrate_ambiguity_causes: None,
+            query_mode: TraitQueryMode::Standard,
+            treat_inductive_cycle: TreatInductiveCycleAs::Recur,
+        }
+    }
+
+    // Sets the `TreatInductiveCycleAs` mode temporarily in the selection context
+    pub fn with_treat_inductive_cycle_as<T>(
+        &mut self,
+        treat_inductive_cycle: TreatInductiveCycleAs,
+        f: impl FnOnce(&mut Self) -> T,
+    ) -> T {
+        // Should be executed in a context where caching is disabled,
+        // otherwise the cache is poisoned with the temporary result.
+        assert!(self.is_intercrate());
+        let treat_inductive_cycle =
+            std::mem::replace(&mut self.treat_inductive_cycle, treat_inductive_cycle);
+        let value = f(self);
+        self.treat_inductive_cycle = treat_inductive_cycle;
+        value
+    }
+
+    pub fn with_query_mode(
+        infcx: &'cx InferCtxt<'tcx>,
+        query_mode: TraitQueryMode,
+    ) -> SelectionContext<'cx, 'tcx> {
+        debug!(?query_mode, "with_query_mode");
+        SelectionContext { query_mode, ..SelectionContext::new(infcx) }
+    }
+
+    /// Enables tracking of intercrate ambiguity causes. See
+    /// the documentation of [`Self::intercrate_ambiguity_causes`] for more.
+    pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) {
+        assert!(self.is_intercrate());
+        assert!(self.intercrate_ambiguity_causes.is_none());
+        self.intercrate_ambiguity_causes = Some(FxIndexSet::default());
+        debug!("selcx: enable_tracking_intercrate_ambiguity_causes");
+    }
+
+    /// Gets the intercrate ambiguity causes collected since tracking
+    /// was enabled and disables tracking at the same time. If
+    /// tracking is not enabled, just returns an empty vector.
+    pub fn take_intercrate_ambiguity_causes(
+        &mut self,
+    ) -> FxIndexSet<IntercrateAmbiguityCause<'tcx>> {
+        assert!(self.is_intercrate());
+        self.intercrate_ambiguity_causes.take().unwrap_or_default()
+    }
+
+    pub fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    pub fn is_intercrate(&self) -> bool {
+        self.infcx.intercrate
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Selection
+    //
+    // The selection phase tries to identify *how* an obligation will
+    // be resolved. For example, it will identify which impl or
+    // parameter bound is to be used. The process can be inconclusive
+    // if the self type in the obligation is not fully inferred. Selection
+    // can result in an error in one of two ways:
+    //
+    // 1. If no applicable impl or parameter bound can be found.
+    // 2. If the output type parameters in the obligation do not match
+    //    those specified by the impl/bound. For example, if the obligation
+    //    is `Vec<Foo>: Iterable<Bar>`, but the impl specifies
+    //    `impl<T> Iterable<T> for Vec<T>`, than an error would result.
+
+    /// Attempts to satisfy the obligation. If successful, this will affect the surrounding
+    /// type environment by performing unification.
+    #[instrument(level = "debug", skip(self), ret)]
+    pub fn poly_select(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> SelectionResult<'tcx, Selection<'tcx>> {
+        if self.infcx.next_trait_solver() {
+            return self.infcx.select_in_new_trait_solver(obligation);
+        }
+
+        let candidate = match self.select_from_obligation(obligation) {
+            Err(SelectionError::Overflow(OverflowError::Canonical)) => {
+                // In standard mode, overflow must have been caught and reported
+                // earlier.
+                assert!(self.query_mode == TraitQueryMode::Canonical);
+                return Err(SelectionError::Overflow(OverflowError::Canonical));
+            }
+            Err(e) => {
+                return Err(e);
+            }
+            Ok(None) => {
+                return Ok(None);
+            }
+            Ok(Some(candidate)) => candidate,
+        };
+
+        match self.confirm_candidate(obligation, candidate) {
+            Err(SelectionError::Overflow(OverflowError::Canonical)) => {
+                assert!(self.query_mode == TraitQueryMode::Canonical);
+                Err(SelectionError::Overflow(OverflowError::Canonical))
+            }
+            Err(e) => Err(e),
+            Ok(candidate) => Ok(Some(candidate)),
+        }
+    }
+
+    pub fn select(
+        &mut self,
+        obligation: &TraitObligation<'tcx>,
+    ) -> SelectionResult<'tcx, Selection<'tcx>> {
+        self.poly_select(&Obligation {
+            cause: obligation.cause.clone(),
+            param_env: obligation.param_env,
+            predicate: ty::Binder::dummy(obligation.predicate),
+            recursion_depth: obligation.recursion_depth,
+        })
+    }
+
+    fn select_from_obligation(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+        debug_assert!(!obligation.predicate.has_escaping_bound_vars());
+
+        let pec = &ProvisionalEvaluationCache::default();
+        let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation);
+
+        self.candidate_from_obligation(&stack)
+    }
+
+    #[instrument(level = "debug", skip(self), ret)]
+    fn candidate_from_obligation<'o>(
+        &mut self,
+        stack: &TraitObligationStack<'o, 'tcx>,
+    ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+        debug_assert!(!self.infcx.next_trait_solver());
+        // Watch out for overflow. This intentionally bypasses (and does
+        // not update) the cache.
+        self.check_recursion_limit(stack.obligation, stack.obligation)?;
+
+        // Check the cache. Note that we freshen the trait-ref
+        // separately rather than using `stack.fresh_trait_ref` --
+        // this is because we want the unbound variables to be
+        // replaced with fresh types starting from index 0.
+        let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate);
+        debug!(?cache_fresh_trait_pred);
+        debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars());
+
+        if let Some(c) =
+            self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred)
+        {
+            debug!("CACHE HIT");
+            return c;
+        }
+
+        // If no match, compute result and insert into cache.
+        //
+        // FIXME(nikomatsakis) -- this cache is not taking into
+        // account cycles that may have occurred in forming the
+        // candidate. I don't know of any specific problems that
+        // result but it seems awfully suspicious.
+        let (candidate, dep_node) =
+            self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
+
+        debug!("CACHE MISS");
+        self.insert_candidate_cache(
+            stack.obligation.param_env,
+            cache_fresh_trait_pred,
+            dep_node,
+            candidate.clone(),
+        );
+        candidate
+    }
+
+    fn candidate_from_obligation_no_cache<'o>(
+        &mut self,
+        stack: &TraitObligationStack<'o, 'tcx>,
+    ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+        if let Err(conflict) = self.is_knowable(stack) {
+            debug!("coherence stage: not knowable");
+            if self.intercrate_ambiguity_causes.is_some() {
+                debug!("evaluate_stack: intercrate_ambiguity_causes is some");
+                // Heuristics: show the diagnostics when there are no candidates in crate.
+                if let Ok(candidate_set) = self.assemble_candidates(stack) {
+                    let mut no_candidates_apply = true;
+
+                    for c in candidate_set.vec.iter() {
+                        if self.evaluate_candidate(stack, c)?.may_apply() {
+                            no_candidates_apply = false;
+                            break;
+                        }
+                    }
+
+                    if !candidate_set.ambiguous && no_candidates_apply {
+                        let trait_ref = self.infcx.resolve_vars_if_possible(
+                            stack.obligation.predicate.skip_binder().trait_ref,
+                        );
+                        if !trait_ref.references_error() {
+                            let self_ty = trait_ref.self_ty();
+                            let self_ty = self_ty.has_concrete_skeleton().then(|| self_ty);
+                            let cause = if let Conflict::Upstream = conflict {
+                                IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_ref, self_ty }
+                            } else {
+                                IntercrateAmbiguityCause::DownstreamCrate { trait_ref, self_ty }
+                            };
+                            debug!(?cause, "evaluate_stack: pushing cause");
+                            self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause);
+                        }
+                    }
+                }
+            }
+            return Ok(None);
+        }
+
+        let candidate_set = self.assemble_candidates(stack)?;
+
+        if candidate_set.ambiguous {
+            debug!("candidate set contains ambig");
+            return Ok(None);
+        }
+
+        let candidates = candidate_set.vec;
+
+        debug!(?stack, ?candidates, "assembled {} candidates", candidates.len());
+
+        // At this point, we know that each of the entries in the
+        // candidate set is *individually* applicable. Now we have to
+        // figure out if they contain mutual incompatibilities. This
+        // frequently arises if we have an unconstrained input type --
+        // for example, we are looking for `$0: Eq` where `$0` is some
+        // unconstrained type variable. In that case, we'll get a
+        // candidate which assumes $0 == int, one that assumes `$0 ==
+        // usize`, etc. This spells an ambiguity.
+
+        let mut candidates = self.filter_impls(candidates, stack.obligation);
+
+        // If there is more than one candidate, first winnow them down
+        // by considering extra conditions (nested obligations and so
+        // forth). We don't winnow if there is exactly one
+        // candidate. This is a relatively minor distinction but it
+        // can lead to better inference and error-reporting. An
+        // example would be if there was an impl:
+        //
+        //     impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
+        //
+        // and we were to see some code `foo.push_clone()` where `boo`
+        // is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
+        // we were to winnow, we'd wind up with zero candidates.
+        // 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);
+        }
+
+        // Winnow, but record the exact outcome of evaluation, which
+        // is needed for specialization. Propagate overflow if it occurs.
+        let mut candidates = candidates
+            .into_iter()
+            .map(|c| match self.evaluate_candidate(stack, &c) {
+                Ok(eval) if eval.may_apply() => {
+                    Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
+                }
+                Ok(_) => Ok(None),
+                Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)),
+                Err(OverflowError::ErrorReporting) => Err(ErrorReporting),
+                Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))),
+            })
+            .flat_map(Result::transpose)
+            .collect::<Result<Vec<_>, _>>()?;
+
+        debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len());
+
+        let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
+
+        // If there are STILL multiple candidates, we can further
+        // reduce the list by dropping duplicates -- including
+        // resolving specializations.
+        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| {
+                    self.candidate_should_be_dropped_in_favor_of(
+                        &candidates[i],
+                        &candidates[j],
+                        has_non_region_infer,
+                    ) == DropVictim::Yes
+                });
+                if should_drop_i {
+                    debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
+                    candidates.swap_remove(i);
+                } else {
+                    debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
+                    i += 1;
+
+                    // If there are *STILL* multiple candidates, give up
+                    // and report ambiguity.
+                    if i > 1 {
+                        debug!("multiple matches, ambig");
+                        return Ok(None);
+                    }
+                }
+            }
+        }
+
+        // If there are *NO* candidates, then there are no impls --
+        // that we know of, anyway. Note that in the case where there
+        // are unbound type variables within the obligation, it might
+        // be the case that you could still satisfy the obligation
+        // from another crate by instantiating the type variables with
+        // a type from another crate that does have an impl. This case
+        // is checked for in `evaluate_stack` (and hence users
+        // who might care about this case, like coherence, should use
+        // that function).
+        if candidates.is_empty() {
+            // If there's an error type, 'downgrade' our result from
+            // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
+            // emitting additional spurious errors, since we're guaranteed
+            // to have emitted at least one.
+            if stack.obligation.predicate.references_error() {
+                debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous");
+                return Ok(None);
+            }
+            return Err(Unimplemented);
+        }
+
+        // Just one candidate left.
+        self.filter_reservation_impls(candidates.pop().unwrap().candidate, stack.obligation)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // EVALUATION
+    //
+    // Tests whether an obligation can be selected or whether an impl
+    // can be applied to particular types. It skips the "confirmation"
+    // step and hence completely ignores output type parameters.
+    //
+    // The result is "true" if the obligation *may* hold and "false" if
+    // we can be sure it does not.
+
+    /// Evaluates whether the obligation `obligation` can be satisfied
+    /// and returns an `EvaluationResult`. This is meant for the
+    /// *initial* call.
+    ///
+    /// Do not use this directly, use `infcx.evaluate_obligation` instead.
+    pub fn evaluate_root_obligation(
+        &mut self,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        debug_assert!(!self.infcx.next_trait_solver());
+        self.evaluation_probe(|this| {
+            let goal =
+                this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
+            let mut result = this.evaluate_predicate_recursively(
+                TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
+                obligation.clone(),
+            )?;
+            // If the predicate has done any inference, then downgrade the
+            // result to ambiguous.
+            if this.infcx.shallow_resolve(goal) != goal {
+                result = result.max(EvaluatedToAmbig);
+            }
+            Ok(result)
+        })
+    }
+
+    fn evaluation_probe(
+        &mut self,
+        op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
+            let outer_universe = self.infcx.universe();
+            let result = op(self)?;
+
+            match self.infcx.leak_check(outer_universe, Some(snapshot)) {
+                Ok(()) => {}
+                Err(_) => return Ok(EvaluatedToErr),
+            }
+
+            if self.infcx.opaque_types_added_in_snapshot(snapshot) {
+                return Ok(result.max(EvaluatedToOkModuloOpaqueTypes));
+            }
+
+            if self.infcx.region_constraints_added_in_snapshot(snapshot) {
+                Ok(result.max(EvaluatedToOkModuloRegions))
+            } else {
+                Ok(result)
+            }
+        })
+    }
+
+    /// Evaluates the predicates in `predicates` recursively. Note that
+    /// this applies projections in the predicates, and therefore
+    /// is run within an inference probe.
+    #[instrument(skip(self, stack), level = "debug")]
+    fn evaluate_predicates_recursively<'o, I>(
+        &mut self,
+        stack: TraitObligationStackList<'o, 'tcx>,
+        predicates: I,
+    ) -> Result<EvaluationResult, OverflowError>
+    where
+        I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
+    {
+        let mut result = EvaluatedToOk;
+        for mut obligation in predicates {
+            obligation.set_depth_from_parent(stack.depth());
+            let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
+            if let EvaluatedToErr = eval {
+                // fast-path - EvaluatedToErr is the top of the lattice,
+                // so we don't need to look on the other predicates.
+                return Ok(EvaluatedToErr);
+            } else {
+                result = cmp::max(result, eval);
+            }
+        }
+        Ok(result)
+    }
+
+    #[instrument(
+        level = "debug",
+        skip(self, previous_stack),
+        fields(previous_stack = ?previous_stack.head())
+        ret,
+    )]
+    fn evaluate_predicate_recursively<'o>(
+        &mut self,
+        previous_stack: TraitObligationStackList<'o, 'tcx>,
+        obligation: PredicateObligation<'tcx>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        debug_assert!(!self.infcx.next_trait_solver());
+        // `previous_stack` stores a `PolyTraitObligation`, while `obligation` is
+        // a `PredicateObligation`. These are distinct types, so we can't
+        // use any `Option` combinator method that would force them to be
+        // the same.
+        match previous_stack.head() {
+            Some(h) => self.check_recursion_limit(&obligation, h.obligation)?,
+            None => self.check_recursion_limit(&obligation, &obligation)?,
+        }
+
+        ensure_sufficient_stack(|| {
+            let bound_predicate = obligation.predicate.kind();
+            match bound_predicate.skip_binder() {
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
+                    let t = bound_predicate.rebind(t);
+                    debug_assert!(!t.has_escaping_bound_vars());
+                    let obligation = obligation.with(self.tcx(), t);
+                    self.evaluate_trait_predicate_recursively(previous_stack, obligation)
+                }
+
+                ty::PredicateKind::Subtype(p) => {
+                    let p = bound_predicate.rebind(p);
+                    // Does this code ever run?
+                    match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) {
+                        Ok(Ok(InferOk { obligations, .. })) => {
+                            self.evaluate_predicates_recursively(previous_stack, obligations)
+                        }
+                        Ok(Err(_)) => Ok(EvaluatedToErr),
+                        Err(..) => Ok(EvaluatedToAmbig),
+                    }
+                }
+
+                ty::PredicateKind::Coerce(p) => {
+                    let p = bound_predicate.rebind(p);
+                    // Does this code ever run?
+                    match self.infcx.coerce_predicate(&obligation.cause, obligation.param_env, p) {
+                        Ok(Ok(InferOk { obligations, .. })) => {
+                            self.evaluate_predicates_recursively(previous_stack, obligations)
+                        }
+                        Ok(Err(_)) => Ok(EvaluatedToErr),
+                        Err(..) => Ok(EvaluatedToAmbig),
+                    }
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+                    // So, there is a bit going on here. First, `WellFormed` predicates
+                    // are coinductive, like trait predicates with auto traits.
+                    // This means that we need to detect if we have recursively
+                    // evaluated `WellFormed(X)`. Otherwise, we would run into
+                    // a "natural" overflow error.
+                    //
+                    // Now, the next question is whether we need to do anything
+                    // special with caching. Considering the following tree:
+                    // - `WF(Foo<T>)`
+                    //   - `Bar<T>: Send`
+                    //     - `WF(Foo<T>)`
+                    //   - `Foo<T>: Trait`
+                    // In this case, the innermost `WF(Foo<T>)` should return
+                    // `EvaluatedToOk`, since it's coinductive. Then if
+                    // `Bar<T>: Send` is resolved to `EvaluatedToOk`, it can be
+                    // inserted into a cache (because without thinking about `WF`
+                    // goals, it isn't in a cycle). If `Foo<T>: Trait` later doesn't
+                    // hold, then `Bar<T>: Send` shouldn't hold. Therefore, we
+                    // *do* need to keep track of coinductive cycles.
+
+                    let cache = previous_stack.cache;
+                    let dfn = cache.next_dfn();
+
+                    for stack_arg in previous_stack.cache.wf_args.borrow().iter().rev() {
+                        if stack_arg.0 != arg {
+                            continue;
+                        }
+                        debug!("WellFormed({:?}) on stack", arg);
+                        if let Some(stack) = previous_stack.head {
+                            // Okay, let's imagine we have two different stacks:
+                            //   `T: NonAutoTrait -> WF(T) -> T: NonAutoTrait`
+                            //   `WF(T) -> T: NonAutoTrait -> WF(T)`
+                            // Because of this, we need to check that all
+                            // predicates between the WF goals are coinductive.
+                            // Otherwise, we can say that `T: NonAutoTrait` is
+                            // true.
+                            // Let's imagine we have a predicate stack like
+                            //         `Foo: Bar -> WF(T) -> T: NonAutoTrait -> T: Auto`
+                            // depth   ^1                    ^2                 ^3
+                            // and the current predicate is `WF(T)`. `wf_args`
+                            // would contain `(T, 1)`. We want to check all
+                            // trait predicates greater than `1`. The previous
+                            // stack would be `T: Auto`.
+                            let cycle = stack.iter().take_while(|s| s.depth > stack_arg.1);
+                            let tcx = self.tcx();
+                            let cycle =
+                                cycle.map(|stack| stack.obligation.predicate.to_predicate(tcx));
+                            if self.coinductive_match(cycle) {
+                                stack.update_reached_depth(stack_arg.1);
+                                return Ok(EvaluatedToOk);
+                            } else {
+                                return Ok(self.treat_inductive_cycle.into());
+                            }
+                        }
+                        return Ok(EvaluatedToOk);
+                    }
+
+                    match wf::obligations(
+                        self.infcx,
+                        obligation.param_env,
+                        obligation.cause.body_id,
+                        obligation.recursion_depth + 1,
+                        arg,
+                        obligation.cause.span,
+                    ) {
+                        Some(obligations) => {
+                            cache.wf_args.borrow_mut().push((arg, previous_stack.depth()));
+                            let result =
+                                self.evaluate_predicates_recursively(previous_stack, obligations);
+                            cache.wf_args.borrow_mut().pop();
+
+                            let result = result?;
+
+                            if !result.must_apply_modulo_regions() {
+                                cache.on_failure(dfn);
+                            }
+
+                            cache.on_completion(dfn);
+
+                            Ok(result)
+                        }
+                        None => Ok(EvaluatedToAmbig),
+                    }
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(pred)) => {
+                    // A global type with no free lifetimes or generic parameters
+                    // outlives anything.
+                    if pred.0.has_free_regions()
+                        || pred.0.has_bound_regions()
+                        || pred.0.has_non_region_infer()
+                        || pred.0.has_non_region_infer()
+                    {
+                        Ok(EvaluatedToOkModuloRegions)
+                    } else {
+                        Ok(EvaluatedToOk)
+                    }
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => {
+                    // We do not consider region relationships when evaluating trait matches.
+                    Ok(EvaluatedToOkModuloRegions)
+                }
+
+                ty::PredicateKind::ObjectSafe(trait_def_id) => {
+                    if self.tcx().check_is_object_safe(trait_def_id) {
+                        Ok(EvaluatedToOk)
+                    } else {
+                        Ok(EvaluatedToErr)
+                    }
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
+                    let data = bound_predicate.rebind(data);
+                    let project_obligation = obligation.with(self.tcx(), data);
+                    match project::poly_project_and_unify_type(self, &project_obligation) {
+                        ProjectAndUnifyResult::Holds(mut subobligations) => {
+                            'compute_res: {
+                                // If we've previously marked this projection as 'complete', then
+                                // use the final cached result (either `EvaluatedToOk` or
+                                // `EvaluatedToOkModuloRegions`), and skip re-evaluating the
+                                // sub-obligations.
+                                if let Some(key) =
+                                    ProjectionCacheKey::from_poly_projection_predicate(self, data)
+                                {
+                                    if let Some(cached_res) = self
+                                        .infcx
+                                        .inner
+                                        .borrow_mut()
+                                        .projection_cache()
+                                        .is_complete(key)
+                                    {
+                                        break 'compute_res Ok(cached_res);
+                                    }
+                                }
+
+                                // Need to explicitly set the depth of nested goals here as
+                                // projection obligations can cycle by themselves and in
+                                // `evaluate_predicates_recursively` we only add the depth
+                                // for parent trait goals because only these get added to the
+                                // `TraitObligationStackList`.
+                                for subobligation in subobligations.iter_mut() {
+                                    subobligation.set_depth_from_parent(obligation.recursion_depth);
+                                }
+                                let res = self.evaluate_predicates_recursively(
+                                    previous_stack,
+                                    subobligations,
+                                );
+                                if let Ok(eval_rslt) = res
+                                    && (eval_rslt == EvaluatedToOk
+                                        || eval_rslt == EvaluatedToOkModuloRegions)
+                                    && let Some(key) =
+                                        ProjectionCacheKey::from_poly_projection_predicate(
+                                            self, data,
+                                        )
+                                {
+                                    // If the result is something that we can cache, then mark this
+                                    // entry as 'complete'. This will allow us to skip evaluating the
+                                    // subobligations at all the next time we evaluate the projection
+                                    // predicate.
+                                    self.infcx
+                                        .inner
+                                        .borrow_mut()
+                                        .projection_cache()
+                                        .complete(key, eval_rslt);
+                                }
+                                res
+                            }
+                        }
+                        ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig),
+                        ProjectAndUnifyResult::Recursive => Ok(self.treat_inductive_cycle.into()),
+                        ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr),
+                    }
+                }
+
+                ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => {
+                    match const_evaluatable::is_const_evaluatable(
+                        self.infcx,
+                        uv,
+                        obligation.param_env,
+                        obligation.cause.span,
+                    ) {
+                        Ok(()) => Ok(EvaluatedToOk),
+                        Err(NotConstEvaluatable::MentionsInfer) => Ok(EvaluatedToAmbig),
+                        Err(NotConstEvaluatable::MentionsParam) => Ok(EvaluatedToErr),
+                        Err(_) => Ok(EvaluatedToErr),
+                    }
+                }
+
+                ty::PredicateKind::ConstEquate(c1, c2) => {
+                    let tcx = self.tcx();
+                    assert!(
+                        tcx.features().generic_const_exprs,
+                        "`ConstEquate` without a feature gate: {c1:?} {c2:?}",
+                    );
+
+                    {
+                        let c1 = tcx.expand_abstract_consts(c1);
+                        let c2 = tcx.expand_abstract_consts(c2);
+                        debug!(
+                            "evaluate_predicate_recursively: equating consts:\nc1= {:?}\nc2= {:?}",
+                            c1, c2
+                        );
+
+                        use rustc_hir::def::DefKind;
+                        use ty::Unevaluated;
+                        match (c1.kind(), c2.kind()) {
+                            (Unevaluated(a), Unevaluated(b))
+                                if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
+                            {
+                                if let Ok(InferOk { obligations, value: () }) = self
+                                    .infcx
+                                    .at(&obligation.cause, obligation.param_env)
+                                    .trace(c1, c2)
+                                    .eq(DefineOpaqueTypes::No, a.args, b.args)
+                                {
+                                    return self.evaluate_predicates_recursively(
+                                        previous_stack,
+                                        obligations,
+                                    );
+                                }
+                            }
+                            (_, Unevaluated(_)) | (Unevaluated(_), _) => (),
+                            (_, _) => {
+                                if let Ok(InferOk { obligations, value: () }) = self
+                                    .infcx
+                                    .at(&obligation.cause, obligation.param_env)
+                                    .eq(DefineOpaqueTypes::No, c1, c2)
+                                {
+                                    return self.evaluate_predicates_recursively(
+                                        previous_stack,
+                                        obligations,
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    let evaluate = |c: ty::Const<'tcx>| {
+                        if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() {
+                            match self.infcx.try_const_eval_resolve(
+                                obligation.param_env,
+                                unevaluated,
+                                c.ty(),
+                                Some(obligation.cause.span),
+                            ) {
+                                Ok(val) => Ok(val),
+                                Err(e) => Err(e),
+                            }
+                        } else {
+                            Ok(c)
+                        }
+                    };
+
+                    match (evaluate(c1), evaluate(c2)) {
+                        (Ok(c1), Ok(c2)) => {
+                            match self.infcx.at(&obligation.cause, obligation.param_env).eq(
+                                DefineOpaqueTypes::No,
+                                c1,
+                                c2,
+                            ) {
+                                Ok(inf_ok) => self.evaluate_predicates_recursively(
+                                    previous_stack,
+                                    inf_ok.into_obligations(),
+                                ),
+                                Err(_) => Ok(EvaluatedToErr),
+                            }
+                        }
+                        (Err(ErrorHandled::Reported(..)), _)
+                        | (_, Err(ErrorHandled::Reported(..))) => Ok(EvaluatedToErr),
+                        (Err(ErrorHandled::TooGeneric(..)), _)
+                        | (_, Err(ErrorHandled::TooGeneric(..))) => {
+                            if c1.has_non_region_infer() || c2.has_non_region_infer() {
+                                Ok(EvaluatedToAmbig)
+                            } else {
+                                // Two different constants using generic parameters ~> error.
+                                Ok(EvaluatedToErr)
+                            }
+                        }
+                    }
+                }
+                ty::PredicateKind::NormalizesTo(..) => {
+                    bug!("NormalizesTo is only used by the new solver")
+                }
+                ty::PredicateKind::AliasRelate(..) => {
+                    bug!("AliasRelate is only used by the new solver")
+                }
+                ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig),
+                ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
+                    match self.infcx.at(&obligation.cause, obligation.param_env).eq(
+                        DefineOpaqueTypes::No,
+                        ct.ty(),
+                        ty,
+                    ) {
+                        Ok(inf_ok) => self.evaluate_predicates_recursively(
+                            previous_stack,
+                            inf_ok.into_obligations(),
+                        ),
+                        Err(_) => Ok(EvaluatedToErr),
+                    }
+                }
+            }
+        })
+    }
+
+    #[instrument(skip(self, previous_stack), level = "debug", ret)]
+    fn evaluate_trait_predicate_recursively<'o>(
+        &mut self,
+        previous_stack: TraitObligationStackList<'o, 'tcx>,
+        mut obligation: PolyTraitObligation<'tcx>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        if !self.is_intercrate()
+            && obligation.is_global()
+            && obligation.param_env.caller_bounds().iter().all(|bound| bound.has_param())
+        {
+            // If a param env has no global bounds, global obligations do not
+            // depend on its particular value in order to work, so we can clear
+            // out the param env and get better caching.
+            debug!("in global");
+            obligation.param_env = obligation.param_env.without_caller_bounds();
+        }
+
+        let stack = self.push_stack(previous_stack, &obligation);
+        let fresh_trait_pred = stack.fresh_trait_pred;
+        let param_env = obligation.param_env;
+
+        debug!(?fresh_trait_pred);
+
+        // If a trait predicate is in the (local or global) evaluation cache,
+        // then we know it holds without cycles.
+        if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) {
+            debug!("CACHE HIT");
+            return Ok(result);
+        }
+
+        if let Some(result) = stack.cache().get_provisional(fresh_trait_pred) {
+            debug!("PROVISIONAL CACHE HIT");
+            stack.update_reached_depth(result.reached_depth);
+            return Ok(result.result);
+        }
+
+        // Check if this is a match for something already on the
+        // stack. If so, we don't want to insert the result into the
+        // main cache (it is cycle dependent) nor the provisional
+        // cache (which is meant for things that have completed but
+        // for a "backedge" -- this result *is* the backedge).
+        if let Some(cycle_result) = self.check_evaluation_cycle(&stack) {
+            return Ok(cycle_result);
+        }
+
+        let (result, dep_node) = self.in_task(|this| {
+            let mut result = this.evaluate_stack(&stack)?;
+
+            // fix issue #103563, we don't normalize
+            // nested obligations which produced by `TraitDef` candidate
+            // (i.e. using bounds on assoc items as assumptions).
+            // because we don't have enough information to
+            // normalize these obligations before evaluating.
+            // so we will try to normalize the obligation and evaluate again.
+            // we will replace it with new solver in the future.
+            if EvaluationResult::EvaluatedToErr == result
+                && fresh_trait_pred.has_projections()
+                && fresh_trait_pred.is_global()
+            {
+                let mut nested_obligations = Vec::new();
+                let predicate = try_normalize_with_depth_to(
+                    this,
+                    param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.predicate,
+                    &mut nested_obligations,
+                );
+                if predicate != obligation.predicate {
+                    let mut nested_result = EvaluationResult::EvaluatedToOk;
+                    for obligation in nested_obligations {
+                        nested_result = cmp::max(
+                            this.evaluate_predicate_recursively(previous_stack, obligation)?,
+                            nested_result,
+                        );
+                    }
+
+                    if nested_result.must_apply_modulo_regions() {
+                        let obligation = obligation.with(this.tcx(), predicate);
+                        result = cmp::max(
+                            nested_result,
+                            this.evaluate_trait_predicate_recursively(previous_stack, obligation)?,
+                        );
+                    }
+                }
+            }
+
+            Ok::<_, OverflowError>(result)
+        });
+
+        let result = result?;
+
+        if !result.must_apply_modulo_regions() {
+            stack.cache().on_failure(stack.dfn);
+        }
+
+        let reached_depth = stack.reached_depth.get();
+        if reached_depth >= stack.depth {
+            debug!("CACHE MISS");
+            self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result);
+            stack.cache().on_completion(stack.dfn);
+        } else {
+            debug!("PROVISIONAL");
+            debug!(
+                "caching provisionally because {:?} \
+                 is a cycle participant (at depth {}, reached depth {})",
+                fresh_trait_pred, stack.depth, reached_depth,
+            );
+
+            stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_pred, result);
+        }
+
+        Ok(result)
+    }
+
+    /// If there is any previous entry on the stack that precisely
+    /// matches this obligation, then we can assume that the
+    /// obligation is satisfied for now (still all other conditions
+    /// must be met of course). One obvious case this comes up is
+    /// marker traits like `Send`. Think of a linked list:
+    ///
+    ///     struct List<T> { data: T, next: Option<Box<List<T>>> }
+    ///
+    /// `Box<List<T>>` will be `Send` if `T` is `Send` and
+    /// `Option<Box<List<T>>>` is `Send`, and in turn
+    /// `Option<Box<List<T>>>` is `Send` if `Box<List<T>>` is
+    /// `Send`.
+    ///
+    /// Note that we do this comparison using the `fresh_trait_ref`
+    /// fields. Because these have all been freshened using
+    /// `self.freshener`, we can be sure that (a) this will not
+    /// affect the inferencer state and (b) that if we see two
+    /// fresh regions with the same index, they refer to the same
+    /// unbound type variable.
+    fn check_evaluation_cycle(
+        &mut self,
+        stack: &TraitObligationStack<'_, 'tcx>,
+    ) -> Option<EvaluationResult> {
+        if let Some(cycle_depth) = stack
+            .iter()
+            .skip(1) // Skip top-most frame.
+            .find(|prev| {
+                stack.obligation.param_env == prev.obligation.param_env
+                    && stack.fresh_trait_pred == prev.fresh_trait_pred
+            })
+            .map(|stack| stack.depth)
+        {
+            debug!("evaluate_stack --> recursive at depth {}", cycle_depth);
+
+            // If we have a stack like `A B C D E A`, where the top of
+            // the stack is the final `A`, then this will iterate over
+            // `A, E, D, C, B` -- i.e., all the participants apart
+            // from the cycle head. We mark them as participating in a
+            // cycle. This suppresses caching for those nodes. See
+            // `in_cycle` field for more details.
+            stack.update_reached_depth(cycle_depth);
+
+            // Subtle: when checking for a coinductive cycle, we do
+            // not compare using the "freshened trait refs" (which
+            // have erased regions) but rather the fully explicit
+            // trait refs. This is important because it's only a cycle
+            // if the regions match exactly.
+            let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth);
+            let tcx = self.tcx();
+            let cycle = cycle.map(|stack| stack.obligation.predicate.to_predicate(tcx));
+            if self.coinductive_match(cycle) {
+                debug!("evaluate_stack --> recursive, coinductive");
+                Some(EvaluatedToOk)
+            } else {
+                debug!("evaluate_stack --> recursive, inductive");
+                Some(self.treat_inductive_cycle.into())
+            }
+        } else {
+            None
+        }
+    }
+
+    fn evaluate_stack<'o>(
+        &mut self,
+        stack: &TraitObligationStack<'o, 'tcx>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        debug_assert!(!self.infcx.next_trait_solver());
+        // In intercrate mode, whenever any of the generics are unbound,
+        // there can always be an impl. Even if there are no impls in
+        // this crate, perhaps the type would be unified with
+        // something from another crate that does provide an impl.
+        //
+        // In intra mode, we must still be conservative. The reason is
+        // that we want to avoid cycles. Imagine an impl like:
+        //
+        //     impl<T:Eq> Eq for Vec<T>
+        //
+        // and a trait reference like `$0 : Eq` where `$0` is an
+        // unbound variable. When we evaluate this trait-reference, we
+        // will unify `$0` with `Vec<$1>` (for some fresh variable
+        // `$1`), on the condition that `$1 : Eq`. We will then wind
+        // up with many candidates (since that are other `Eq` impls
+        // that apply) and try to winnow things down. This results in
+        // a recursive evaluation that `$1 : Eq` -- as you can
+        // imagine, this is just where we started. To avoid that, we
+        // check for unbound variables and return an ambiguous (hence possible)
+        // match if we've seen this trait before.
+        //
+        // This suffices to allow chains like `FnMut` implemented in
+        // terms of `Fn` etc, but we could probably make this more
+        // precise still.
+        let unbound_input_types =
+            stack.fresh_trait_pred.skip_binder().trait_ref.args.types().any(|ty| ty.is_fresh());
+
+        if unbound_input_types
+            && stack.iter().skip(1).any(|prev| {
+                stack.obligation.param_env == prev.obligation.param_env
+                    && self.match_fresh_trait_refs(stack.fresh_trait_pred, prev.fresh_trait_pred)
+            })
+        {
+            debug!("evaluate_stack --> unbound argument, recursive --> giving up",);
+            return Ok(EvaluatedToAmbigStackDependent);
+        }
+
+        match self.candidate_from_obligation(stack) {
+            Ok(Some(c)) => self.evaluate_candidate(stack, &c),
+            Ok(None) => Ok(EvaluatedToAmbig),
+            Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical),
+            Err(ErrorReporting) => Err(OverflowError::ErrorReporting),
+            Err(..) => Ok(EvaluatedToErr),
+        }
+    }
+
+    /// For defaulted traits, we use a co-inductive strategy to solve, so
+    /// that recursion is ok. This routine returns `true` if the top of the
+    /// stack (`cycle[0]`):
+    ///
+    /// - is a defaulted trait,
+    /// - it also appears in the backtrace at some position `X`,
+    /// - all the predicates at positions `X..` between `X` and the top are
+    ///   also defaulted traits.
+    pub(crate) fn coinductive_match<I>(&mut self, mut cycle: I) -> bool
+    where
+        I: Iterator<Item = ty::Predicate<'tcx>>,
+    {
+        cycle.all(|predicate| predicate.is_coinductive(self.tcx()))
+    }
+
+    /// Further evaluates `candidate` to decide whether all type parameters match and whether nested
+    /// obligations are met. Returns whether `candidate` remains viable after this further
+    /// scrutiny.
+    #[instrument(
+        level = "debug",
+        skip(self, stack),
+        fields(depth = stack.obligation.recursion_depth),
+        ret
+    )]
+    fn evaluate_candidate<'o>(
+        &mut self,
+        stack: &TraitObligationStack<'o, 'tcx>,
+        candidate: &SelectionCandidate<'tcx>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        let mut result = self.evaluation_probe(|this| {
+            let candidate = (*candidate).clone();
+            match this.confirm_candidate(stack.obligation, candidate) {
+                Ok(selection) => {
+                    debug!(?selection);
+                    this.evaluate_predicates_recursively(
+                        stack.list(),
+                        selection.nested_obligations().into_iter(),
+                    )
+                }
+                Err(..) => Ok(EvaluatedToErr),
+            }
+        })?;
+
+        // If we erased any lifetimes, then we want to use
+        // `EvaluatedToOkModuloRegions` instead of `EvaluatedToOk`
+        // as your final result. The result will be cached using
+        // the freshened trait predicate as a key, so we need
+        // our result to be correct by *any* choice of original lifetimes,
+        // not just the lifetime choice for this particular (non-erased)
+        // predicate.
+        // See issue #80691
+        if stack.fresh_trait_pred.has_erased_regions() {
+            result = result.max(EvaluatedToOkModuloRegions);
+        }
+
+        Ok(result)
+    }
+
+    fn check_evaluation_cache(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> Option<EvaluationResult> {
+        // Neither the global nor local cache is aware of intercrate
+        // mode, so don't do any caching. In particular, we might
+        // re-use the same `InferCtxt` with both an intercrate
+        // and non-intercrate `SelectionContext`
+        if self.is_intercrate() {
+            return None;
+        }
+
+        let tcx = self.tcx();
+        if self.can_use_global_caches(param_env) {
+            if let Some(res) = tcx.evaluation_cache.get(&(param_env, trait_pred), tcx) {
+                return Some(res);
+            }
+        }
+        self.infcx.evaluation_cache.get(&(param_env, trait_pred), tcx)
+    }
+
+    fn insert_evaluation_cache(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+        dep_node: DepNodeIndex,
+        result: EvaluationResult,
+    ) {
+        // Avoid caching results that depend on more than just the trait-ref
+        // - the stack can create recursion.
+        if result.is_stack_dependent() {
+            return;
+        }
+
+        // Neither the global nor local cache is aware of intercrate
+        // mode, so don't do any caching. In particular, we might
+        // re-use the same `InferCtxt` with both an intercrate
+        // and non-intercrate `SelectionContext`
+        if self.is_intercrate() {
+            return;
+        }
+
+        if self.can_use_global_caches(param_env) {
+            if !trait_pred.has_infer() {
+                debug!(?trait_pred, ?result, "insert_evaluation_cache global");
+                // This may overwrite the cache with the same value
+                // FIXME: Due to #50507 this overwrites the different values
+                // This should be changed to use HashMapExt::insert_same
+                // when that is fixed
+                self.tcx().evaluation_cache.insert((param_env, trait_pred), dep_node, result);
+                return;
+            }
+        }
+
+        debug!(?trait_pred, ?result, "insert_evaluation_cache");
+        self.infcx.evaluation_cache.insert((param_env, trait_pred), dep_node, result);
+    }
+
+    fn check_recursion_depth<T>(
+        &self,
+        depth: usize,
+        error_obligation: &Obligation<'tcx, T>,
+    ) -> Result<(), OverflowError>
+    where
+        T: ToPredicate<'tcx> + Clone,
+    {
+        if !self.infcx.tcx.recursion_limit().value_within_limit(depth) {
+            match self.query_mode {
+                TraitQueryMode::Standard => {
+                    if let Some(e) = self.infcx.tainted_by_errors() {
+                        return Err(OverflowError::Error(e));
+                    }
+                    self.infcx.err_ctxt().report_overflow_obligation(error_obligation, true);
+                }
+                TraitQueryMode::Canonical => {
+                    return Err(OverflowError::Canonical);
+                }
+            }
+        }
+        Ok(())
+    }
+
+    /// Checks that the recursion limit has not been exceeded.
+    ///
+    /// The weird return type of this function allows it to be used with the `try` (`?`)
+    /// operator within certain functions.
+    #[inline(always)]
+    fn check_recursion_limit<T: Display + TypeFoldable<TyCtxt<'tcx>>, V>(
+        &self,
+        obligation: &Obligation<'tcx, T>,
+        error_obligation: &Obligation<'tcx, V>,
+    ) -> Result<(), OverflowError>
+    where
+        V: ToPredicate<'tcx> + Clone,
+    {
+        self.check_recursion_depth(obligation.recursion_depth, error_obligation)
+    }
+
+    fn in_task<OP, R>(&mut self, op: OP) -> (R, DepNodeIndex)
+    where
+        OP: FnOnce(&mut Self) -> R,
+    {
+        let (result, dep_node) =
+            self.tcx().dep_graph.with_anon_task(self.tcx(), dep_kinds::TraitSelect, || op(self));
+        self.tcx().dep_graph.read_index(dep_node);
+        (result, dep_node)
+    }
+
+    /// filter_impls filters candidates that have a positive impl for a negative
+    /// goal and a negative impl for a positive goal
+    #[instrument(level = "debug", skip(self, candidates))]
+    fn filter_impls(
+        &mut self,
+        candidates: Vec<SelectionCandidate<'tcx>>,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Vec<SelectionCandidate<'tcx>> {
+        trace!("{candidates:#?}");
+        let tcx = self.tcx();
+        let mut result = Vec::with_capacity(candidates.len());
+
+        for candidate in candidates {
+            if let ImplCandidate(def_id) = candidate {
+                if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id)
+                    || obligation.polarity() == tcx.impl_polarity(def_id)
+                {
+                    result.push(candidate);
+                }
+            } else {
+                result.push(candidate);
+            }
+        }
+
+        trace!("{result:#?}");
+        result
+    }
+
+    /// filter_reservation_impls filter reservation impl for any goal as ambiguous
+    #[instrument(level = "debug", skip(self))]
+    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.
+        if let ImplCandidate(def_id) = candidate {
+            if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) {
+                if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes {
+                    let message = tcx
+                        .get_attr(def_id, sym::rustc_reservation_impl)
+                        .and_then(|a| a.value_str());
+                    if let Some(message) = message {
+                        debug!(
+                            "filter_reservation_impls: \
+                                 reservation impl ambiguity on {:?}",
+                            def_id
+                        );
+                        intercrate_ambiguity_clauses
+                            .insert(IntercrateAmbiguityCause::ReservationImpl { message });
+                    }
+                }
+                return Ok(None);
+            }
+        }
+        Ok(Some(candidate))
+    }
+
+    fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Result<(), Conflict> {
+        debug!("is_knowable(intercrate={:?})", self.is_intercrate());
+
+        if !self.is_intercrate() {
+            return Ok(());
+        }
+
+        let obligation = &stack.obligation;
+        let predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
+
+        // Okay to skip binder because of the nature of the
+        // trait-ref-is-knowable check, which does not care about
+        // bound regions.
+        let trait_ref = predicate.skip_binder().trait_ref;
+
+        coherence::trait_ref_is_knowable::<!>(self.tcx(), trait_ref, |ty| Ok(ty)).unwrap()
+    }
+
+    /// Returns `true` if the global caches can be used.
+    fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool {
+        // If there are any inference variables in the `ParamEnv`, then we
+        // always use a cache local to this particular scope. Otherwise, we
+        // switch to a global cache.
+        if param_env.has_infer() {
+            return false;
+        }
+
+        // Avoid using the master cache during coherence and just rely
+        // on the local cache. This effectively disables caching
+        // during coherence. It is really just a simplification to
+        // avoid us having to fear that coherence results "pollute"
+        // the master cache. Since coherence executes pretty quickly,
+        // it's not worth going to more trouble to increase the
+        // hit-rate, I don't think.
+        if self.is_intercrate() {
+            return false;
+        }
+
+        // Otherwise, we can use the global cache.
+        true
+    }
+
+    fn check_candidate_cache(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>> {
+        // Neither the global nor local cache is aware of intercrate
+        // mode, so don't do any caching. In particular, we might
+        // re-use the same `InferCtxt` with both an intercrate
+        // and non-intercrate `SelectionContext`
+        if self.is_intercrate() {
+            return None;
+        }
+        let tcx = self.tcx();
+        let pred = cache_fresh_trait_pred.skip_binder();
+
+        if self.can_use_global_caches(param_env) {
+            if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) {
+                return Some(res);
+            }
+        }
+        self.infcx.selection_cache.get(&(param_env, pred), tcx)
+    }
+
+    /// Determines whether can we safely cache the result
+    /// of selecting an obligation. This is almost always `true`,
+    /// except when dealing with certain `ParamCandidate`s.
+    ///
+    /// Ordinarily, a `ParamCandidate` will contain no inference variables,
+    /// since it was usually produced directly from a `DefId`. However,
+    /// certain cases (currently only librustdoc's blanket impl finder),
+    /// a `ParamEnv` may be explicitly constructed with inference types.
+    /// When this is the case, we do *not* want to cache the resulting selection
+    /// candidate. This is due to the fact that it might not always be possible
+    /// to equate the obligation's trait ref and the candidate's trait ref,
+    /// if more constraints end up getting added to an inference variable.
+    ///
+    /// Because of this, we always want to re-run the full selection
+    /// process for our obligation the next time we see it, since
+    /// we might end up picking a different `SelectionCandidate` (or none at all).
+    fn can_cache_candidate(
+        &self,
+        result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>,
+    ) -> bool {
+        // Neither the global nor local cache is aware of intercrate
+        // mode, so don't do any caching. In particular, we might
+        // re-use the same `InferCtxt` with both an intercrate
+        // and non-intercrate `SelectionContext`
+        if self.is_intercrate() {
+            return false;
+        }
+        match result {
+            Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.has_infer(),
+            _ => true,
+        }
+    }
+
+    #[instrument(skip(self, param_env, cache_fresh_trait_pred, dep_node), level = "debug")]
+    fn insert_candidate_cache(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        dep_node: DepNodeIndex,
+        candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        let pred = cache_fresh_trait_pred.skip_binder();
+
+        if !self.can_cache_candidate(&candidate) {
+            debug!(?pred, ?candidate, "insert_candidate_cache - candidate is not cacheable");
+            return;
+        }
+
+        if self.can_use_global_caches(param_env) {
+            if let Err(Overflow(OverflowError::Canonical)) = candidate {
+                // Don't cache overflow globally; we only produce this in certain modes.
+            } else if !pred.has_infer() {
+                if !candidate.has_infer() {
+                    debug!(?pred, ?candidate, "insert_candidate_cache global");
+                    // This may overwrite the cache with the same value.
+                    tcx.selection_cache.insert((param_env, pred), dep_node, candidate);
+                    return;
+                }
+            }
+        }
+
+        debug!(?pred, ?candidate, "insert_candidate_cache local");
+        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(
+        &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);
+
+        // 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();
+
+        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()
+    }
+
+    /// Equates the trait in `obligation` with trait bound. If the two traits
+    /// can be equated and the normalized trait bound doesn't contain inference
+    /// variables or placeholders, the normalized bound is returned.
+    fn match_normalize_trait_ref(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        trait_bound: ty::PolyTraitRef<'tcx>,
+        placeholder_trait_ref: ty::TraitRef<'tcx>,
+    ) -> Result<Option<ty::PolyTraitRef<'tcx>>, ()> {
+        debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars());
+        if placeholder_trait_ref.def_id != trait_bound.def_id() {
+            // Avoid unnecessary normalization
+            return Err(());
+        }
+
+        let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| {
+            project::normalize_with_depth(
+                self,
+                obligation.param_env,
+                obligation.cause.clone(),
+                obligation.recursion_depth + 1,
+                trait_bound,
+            )
+        });
+        self.infcx
+            .at(&obligation.cause, obligation.param_env)
+            .sup(DefineOpaqueTypes::No, ty::Binder::dummy(placeholder_trait_ref), trait_bound)
+            .map(|InferOk { obligations: _, value: () }| {
+                // This method is called within a probe, so we can't have
+                // inference variables and placeholders escape.
+                if !trait_bound.has_infer() && !trait_bound.has_placeholders() {
+                    Some(trait_bound)
+                } else {
+                    None
+                }
+            })
+            .map_err(|_| ())
+    }
+
+    fn where_clause_may_apply<'o>(
+        &mut self,
+        stack: &TraitObligationStack<'o, 'tcx>,
+        where_clause_trait_ref: ty::PolyTraitRef<'tcx>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        self.evaluation_probe(|this| {
+            match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
+                Ok(obligations) => this.evaluate_predicates_recursively(stack.list(), obligations),
+                Err(()) => Ok(EvaluatedToErr),
+            }
+        })
+    }
+
+    /// Return `Yes` if the obligation's predicate type applies to the env_predicate, and
+    /// `No` if it does not. Return `Ambiguous` in the case that the projection type is a GAT,
+    /// and applying this env_predicate constrains any of the obligation's GAT substitutions.
+    ///
+    /// This behavior is a somewhat of a hack to prevent over-constraining inference variables
+    /// in cases like #91762.
+    pub(super) fn match_projection_projections(
+        &mut self,
+        obligation: &ProjectionTyObligation<'tcx>,
+        env_predicate: PolyProjectionPredicate<'tcx>,
+        potentially_unnormalized_candidates: bool,
+    ) -> ProjectionMatchesProjection {
+        let mut nested_obligations = Vec::new();
+        let infer_predicate = self.infcx.instantiate_binder_with_fresh_vars(
+            obligation.cause.span,
+            BoundRegionConversionTime::HigherRankedType,
+            env_predicate,
+        );
+        let infer_projection = if potentially_unnormalized_candidates {
+            ensure_sufficient_stack(|| {
+                project::normalize_with_depth_to(
+                    self,
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    infer_predicate.projection_ty,
+                    &mut nested_obligations,
+                )
+            })
+        } else {
+            infer_predicate.projection_ty
+        };
+
+        let is_match = self
+            .infcx
+            .at(&obligation.cause, obligation.param_env)
+            .sup(DefineOpaqueTypes::No, obligation.predicate, infer_projection)
+            .is_ok_and(|InferOk { obligations, value: () }| {
+                self.evaluate_predicates_recursively(
+                    TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
+                    nested_obligations.into_iter().chain(obligations),
+                )
+                .is_ok_and(|res| res.may_apply())
+            });
+
+        if is_match {
+            let generics = self.tcx().generics_of(obligation.predicate.def_id);
+            // FIXME(generic-associated-types): Addresses aggressive inference in #92917.
+            // If this type is a GAT, and of the GAT args resolve to something new,
+            // that means that we must have newly inferred something about the GAT.
+            // We should give up in that case.
+            if !generics.params.is_empty()
+                && obligation.predicate.args[generics.parent_count..]
+                    .iter()
+                    .any(|&p| p.has_non_region_infer() && self.infcx.shallow_resolve(p) != p)
+            {
+                ProjectionMatchesProjection::Ambiguous
+            } else {
+                ProjectionMatchesProjection::Yes
+            }
+        } else {
+            ProjectionMatchesProjection::No
+        }
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum DropVictim {
+    Yes,
+    No,
+}
+
+impl DropVictim {
+    fn drop_if(should_drop: bool) -> DropVictim {
+        if should_drop { DropVictim::Yes } else { DropVictim::No }
+    }
+}
+
+/// ## Winnowing
+///
+/// Winnowing is the process of attempting to resolve ambiguity by
+/// probing further. During the winnowing process, we unify all
+/// type variables and then we also attempt to evaluate recursive
+/// bounds to see if they are satisfied.
+impl<'tcx> SelectionContext<'_, 'tcx> {
+    /// Returns `DropVictim::Yes` if `victim` should be dropped in favor of
+    /// `other`. Generally speaking we will drop duplicate
+    /// candidates and prefer where-clause candidates.
+    ///
+    /// See the comment for "SelectionCandidate" for more details.
+    #[instrument(level = "debug", skip(self))]
+    fn candidate_should_be_dropped_in_favor_of(
+        &mut self,
+        victim: &EvaluatedCandidate<'tcx>,
+        other: &EvaluatedCandidate<'tcx>,
+        has_non_region_infer: bool,
+    ) -> DropVictim {
+        if victim.candidate == other.candidate {
+            return DropVictim::Yes;
+        }
+
+        // Check if a bound would previously have been removed when normalizing
+        // the param_env so that it can be given the lowest priority. See
+        // #50825 for the motivation for this.
+        let is_global =
+            |cand: &ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars();
+
+        // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`,
+        // `DiscriminantKindCandidate`, `ConstDestructCandidate`
+        // to anything else.
+        //
+        // This is a fix for #53123 and prevents winnowing from accidentally extending the
+        // lifetime of a variable.
+        match (&other.candidate, &victim.candidate) {
+            // FIXME(@jswrenn): this should probably be more sophisticated
+            (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No,
+
+            // (*)
+            (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => {
+                DropVictim::Yes
+            }
+            (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => {
+                DropVictim::No
+            }
+
+            (ParamCandidate(other), ParamCandidate(victim)) => {
+                let same_except_bound_vars = other.skip_binder().trait_ref
+                    == victim.skip_binder().trait_ref
+                    && other.skip_binder().polarity == victim.skip_binder().polarity
+                    && !other.skip_binder().trait_ref.has_escaping_bound_vars();
+                if same_except_bound_vars {
+                    // See issue #84398. In short, we can generate multiple ParamCandidates which are
+                    // the same except for unused bound vars. Just pick the one with the fewest bound vars
+                    // or the current one if tied (they should both evaluate to the same answer). This is
+                    // probably best characterized as a "hack", since we might prefer to just do our
+                    // best to *not* create essentially duplicate candidates in the first place.
+                    DropVictim::drop_if(other.bound_vars().len() <= victim.bound_vars().len())
+                } else {
+                    DropVictim::No
+                }
+            }
+
+            // Drop otherwise equivalent non-const fn pointer candidates
+            (FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => {
+                DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_)
+            }
+
+            (
+                ParamCandidate(ref other_cand),
+                ImplCandidate(..)
+                | AutoImplCandidate
+                | ClosureCandidate { .. }
+                | CoroutineCandidate
+                | FutureCandidate
+                | IteratorCandidate
+                | AsyncIteratorCandidate
+                | FnPointerCandidate { .. }
+                | BuiltinObjectCandidate
+                | BuiltinUnsizeCandidate
+                | TraitUpcastingUnsizeCandidate(_)
+                | BuiltinCandidate { .. }
+                | TraitAliasCandidate
+                | ObjectCandidate(_)
+                | ProjectionCandidate(_),
+            ) => {
+                // We have a where clause so don't go around looking
+                // for impls. Arbitrarily give param candidates priority
+                // over projection and object candidates.
+                //
+                // Global bounds from the where clause should be ignored
+                // here (see issue #50825).
+                DropVictim::drop_if(!is_global(other_cand))
+            }
+            (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref victim_cand)) => {
+                // Prefer these to a global where-clause bound
+                // (see issue #50825).
+                if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No }
+            }
+            (
+                ImplCandidate(_)
+                | AutoImplCandidate
+                | ClosureCandidate { .. }
+                | CoroutineCandidate
+                | FutureCandidate
+                | IteratorCandidate
+                | AsyncIteratorCandidate
+                | FnPointerCandidate { .. }
+                | BuiltinObjectCandidate
+                | BuiltinUnsizeCandidate
+                | TraitUpcastingUnsizeCandidate(_)
+                | BuiltinCandidate { has_nested: true }
+                | TraitAliasCandidate,
+                ParamCandidate(ref victim_cand),
+            ) => {
+                // Prefer these to a global where-clause bound
+                // (see issue #50825).
+                DropVictim::drop_if(
+                    is_global(victim_cand) && other.evaluation.must_apply_modulo_regions(),
+                )
+            }
+
+            (ProjectionCandidate(i), ProjectionCandidate(j))
+            | (ObjectCandidate(i), ObjectCandidate(j)) => {
+                // Arbitrarily pick the lower numbered candidate for backwards
+                // compatibility reasons. Don't let this affect inference.
+                DropVictim::drop_if(i < j && !has_non_region_infer)
+            }
+            (ObjectCandidate(_), ProjectionCandidate(_))
+            | (ProjectionCandidate(_), ObjectCandidate(_)) => {
+                bug!("Have both object and projection candidate")
+            }
+
+            // Arbitrarily give projection and object candidates priority.
+            (
+                ObjectCandidate(_) | ProjectionCandidate(_),
+                ImplCandidate(..)
+                | AutoImplCandidate
+                | ClosureCandidate { .. }
+                | CoroutineCandidate
+                | FutureCandidate
+                | IteratorCandidate
+                | AsyncIteratorCandidate
+                | FnPointerCandidate { .. }
+                | BuiltinObjectCandidate
+                | BuiltinUnsizeCandidate
+                | TraitUpcastingUnsizeCandidate(_)
+                | BuiltinCandidate { .. }
+                | TraitAliasCandidate,
+            ) => DropVictim::Yes,
+
+            (
+                ImplCandidate(..)
+                | AutoImplCandidate
+                | ClosureCandidate { .. }
+                | CoroutineCandidate
+                | FutureCandidate
+                | IteratorCandidate
+                | AsyncIteratorCandidate
+                | FnPointerCandidate { .. }
+                | BuiltinObjectCandidate
+                | BuiltinUnsizeCandidate
+                | TraitUpcastingUnsizeCandidate(_)
+                | BuiltinCandidate { .. }
+                | TraitAliasCandidate,
+                ObjectCandidate(_) | ProjectionCandidate(_),
+            ) => DropVictim::No,
+
+            (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => {
+                // See if we can toss out `victim` based on specialization.
+                // While this requires us to know *for sure* that the `other` impl applies
+                // we still use modulo regions here.
+                //
+                // This is fine as specialization currently assumes that specializing
+                // impls have to be always applicable, meaning that the only allowed
+                // region constraints may be constraints also present on the default impl.
+                let tcx = self.tcx();
+                if other.evaluation.must_apply_modulo_regions() {
+                    if tcx.specializes((other_def, victim_def)) {
+                        return DropVictim::Yes;
+                    }
+                }
+
+                match tcx.impls_are_allowed_to_overlap(other_def, victim_def) {
+                    // For #33140 the impl headers must be exactly equal, the trait must not have
+                    // any associated items and there are no where-clauses.
+                    //
+                    // We can just arbitrarily drop one of the impls.
+                    Some(ty::ImplOverlapKind::Issue33140) => {
+                        assert_eq!(other.evaluation, victim.evaluation);
+                        DropVictim::Yes
+                    }
+                    // For candidates which already reference errors it doesn't really
+                    // matter what we do 🤷
+                    Some(ty::ImplOverlapKind::Permitted { marker: false }) => {
+                        DropVictim::drop_if(other.evaluation.must_apply_considering_regions())
+                    }
+                    Some(ty::ImplOverlapKind::Permitted { marker: true }) => {
+                        // Subtle: If the predicate we are evaluating has inference
+                        // variables, do *not* allow discarding candidates due to
+                        // marker trait impls.
+                        //
+                        // Without this restriction, we could end up accidentally
+                        // constraining inference variables based on an arbitrarily
+                        // chosen trait impl.
+                        //
+                        // Imagine we have the following code:
+                        //
+                        // ```rust
+                        // #[marker] trait MyTrait {}
+                        // impl MyTrait for u8 {}
+                        // impl MyTrait for bool {}
+                        // ```
+                        //
+                        // And we are evaluating the predicate `<_#0t as MyTrait>`.
+                        //
+                        // During selection, we will end up with one candidate for each
+                        // impl of `MyTrait`. If we were to discard one impl in favor
+                        // of the other, we would be left with one candidate, causing
+                        // us to "successfully" select the predicate, unifying
+                        // _#0t with (for example) `u8`.
+                        //
+                        // However, we have no reason to believe that this unification
+                        // is correct - we've essentially just picked an arbitrary
+                        // *possibility* for _#0t, and required that this be the *only*
+                        // possibility.
+                        //
+                        // Eventually, we will either:
+                        // 1) Unify all inference variables in the predicate through
+                        // some other means (e.g. type-checking of a function). We will
+                        // then be in a position to drop marker trait candidates
+                        // without constraining inference variables (since there are
+                        // none left to constrain)
+                        // 2) Be left with some unconstrained inference variables. We
+                        // will then correctly report an inference error, since the
+                        // existence of multiple marker trait impls tells us nothing
+                        // about which one should actually apply.
+                        DropVictim::drop_if(
+                            !has_non_region_infer
+                                && other.evaluation.must_apply_considering_regions(),
+                        )
+                    }
+                    None => DropVictim::No,
+                }
+            }
+
+            (AutoImplCandidate, ImplCandidate(_)) | (ImplCandidate(_), AutoImplCandidate) => {
+                DropVictim::No
+            }
+
+            (AutoImplCandidate, _) | (_, AutoImplCandidate) => {
+                bug!(
+                    "default implementations shouldn't be recorded \
+                    when there are other global candidates: {:?} {:?}",
+                    other,
+                    victim
+                );
+            }
+
+            // Everything else is ambiguous
+            (
+                ImplCandidate(_)
+                | ClosureCandidate { .. }
+                | CoroutineCandidate
+                | FutureCandidate
+                | IteratorCandidate
+                | AsyncIteratorCandidate
+                | FnPointerCandidate { .. }
+                | BuiltinObjectCandidate
+                | BuiltinUnsizeCandidate
+                | TraitUpcastingUnsizeCandidate(_)
+                | BuiltinCandidate { has_nested: true }
+                | TraitAliasCandidate,
+                ImplCandidate(_)
+                | ClosureCandidate { .. }
+                | CoroutineCandidate
+                | FutureCandidate
+                | IteratorCandidate
+                | AsyncIteratorCandidate
+                | FnPointerCandidate { .. }
+                | BuiltinObjectCandidate
+                | BuiltinUnsizeCandidate
+                | TraitUpcastingUnsizeCandidate(_)
+                | BuiltinCandidate { has_nested: true }
+                | TraitAliasCandidate,
+            ) => DropVictim::No,
+        }
+    }
+}
+
+impl<'tcx> SelectionContext<'_, 'tcx> {
+    fn sized_conditions(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> BuiltinImplConditions<'tcx> {
+        use self::BuiltinImplConditions::{Ambiguous, None, Where};
+
+        // NOTE: binder moved to (*)
+        let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty());
+
+        match self_ty.kind() {
+            ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+            | ty::Uint(_)
+            | ty::Int(_)
+            | ty::Bool
+            | ty::Float(_)
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::RawPtr(..)
+            | ty::Char
+            | ty::Ref(..)
+            | ty::Coroutine(..)
+            | ty::CoroutineWitness(..)
+            | ty::Array(..)
+            | ty::Closure(..)
+            | ty::Never
+            | ty::Dynamic(_, _, ty::DynStar)
+            | ty::Error(_) => {
+                // safe for everything
+                Where(ty::Binder::dummy(Vec::new()))
+            }
+
+            ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None,
+
+            ty::Tuple(tys) => Where(
+                obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])),
+            ),
+
+            ty::Adt(def, args) => {
+                let sized_crit = def.sized_constraint(self.tcx());
+                // (*) binder moved here
+                Where(
+                    obligation
+                        .predicate
+                        .rebind(sized_crit.iter_instantiated(self.tcx(), args).collect()),
+                )
+            }
+
+            ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None,
+            ty::Infer(ty::TyVar(_)) => Ambiguous,
+
+            // We can make this an ICE if/once we actually instantiate the trait obligation eagerly.
+            ty::Bound(..) => None,
+
+            ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+                bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty);
+            }
+        }
+    }
+
+    fn copy_clone_conditions(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> BuiltinImplConditions<'tcx> {
+        // NOTE: binder moved to (*)
+        let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty());
+
+        use self::BuiltinImplConditions::{Ambiguous, None, Where};
+
+        match *self_ty.kind() {
+            ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())),
+
+            ty::Uint(_)
+            | ty::Int(_)
+            | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+            | ty::Bool
+            | ty::Float(_)
+            | ty::Char
+            | ty::RawPtr(..)
+            | ty::Never
+            | ty::Ref(_, _, hir::Mutability::Not)
+            | ty::Array(..) => {
+                // Implementations provided in libcore
+                None
+            }
+
+            ty::Dynamic(..)
+            | ty::Str
+            | ty::Slice(..)
+            | ty::Foreign(..)
+            | ty::Ref(_, _, hir::Mutability::Mut) => None,
+
+            ty::Tuple(tys) => {
+                // (*) binder moved here
+                Where(obligation.predicate.rebind(tys.iter().collect()))
+            }
+
+            ty::Coroutine(coroutine_def_id, args) => {
+                match self.tcx().coroutine_movability(coroutine_def_id) {
+                    hir::Movability::Static => None,
+                    hir::Movability::Movable => {
+                        if self.tcx().features().coroutine_clone {
+                            let resolved_upvars =
+                                self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty());
+                            let resolved_witness =
+                                self.infcx.shallow_resolve(args.as_coroutine().witness());
+                            if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() {
+                                // Not yet resolved.
+                                Ambiguous
+                            } else {
+                                let all = args
+                                    .as_coroutine()
+                                    .upvar_tys()
+                                    .iter()
+                                    .chain([args.as_coroutine().witness()])
+                                    .collect::<Vec<_>>();
+                                Where(obligation.predicate.rebind(all))
+                            }
+                        } else {
+                            None
+                        }
+                    }
+                }
+            }
+
+            ty::CoroutineWitness(def_id, args) => {
+                let hidden_types = bind_coroutine_hidden_types_above(
+                    self.infcx,
+                    def_id,
+                    args,
+                    obligation.predicate.bound_vars(),
+                );
+                Where(hidden_types)
+            }
+
+            ty::Closure(_, args) => {
+                // (*) binder moved here
+                let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty());
+                if let ty::Infer(ty::TyVar(_)) = ty.kind() {
+                    // Not yet resolved.
+                    Ambiguous
+                } else {
+                    Where(obligation.predicate.rebind(args.as_closure().upvar_tys().to_vec()))
+                }
+            }
+
+            ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {
+                // Fallback to whatever user-defined impls exist in this case.
+                None
+            }
+
+            ty::Infer(ty::TyVar(_)) => {
+                // Unbound type variable. Might or might not have
+                // applicable impls and so forth, depending on what
+                // those type variables wind up being bound to.
+                Ambiguous
+            }
+
+            // We can make this an ICE if/once we actually instantiate the trait obligation eagerly.
+            ty::Bound(..) => None,
+
+            ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+                bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty);
+            }
+        }
+    }
+
+    /// For default impls, we need to break apart a type into its
+    /// "constituent types" -- meaning, the types that it contains.
+    ///
+    /// Here are some (simple) examples:
+    ///
+    /// ```ignore (illustrative)
+    /// (i32, u32) -> [i32, u32]
+    /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32]
+    /// Bar<i32> where struct Bar<T> { x: T, y: u32 } -> [i32, u32]
+    /// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32]
+    /// ```
+    #[instrument(level = "debug", skip(self), ret)]
+    fn constituent_types_for_ty(
+        &self,
+        t: ty::Binder<'tcx, Ty<'tcx>>,
+    ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> {
+        Ok(match *t.skip_binder().kind() {
+            ty::Uint(_)
+            | ty::Int(_)
+            | ty::Bool
+            | ty::Float(_)
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::Error(_)
+            | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+            | ty::Never
+            | ty::Char => ty::Binder::dummy(Vec::new()),
+
+            // Treat this like `struct str([u8]);`
+            ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]),
+
+            ty::Placeholder(..)
+            | ty::Dynamic(..)
+            | ty::Param(..)
+            | ty::Foreign(..)
+            | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
+            | ty::Bound(..)
+            | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+                bug!("asked to assemble constituent types of unexpected type: {:?}", t);
+            }
+
+            ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => {
+                t.rebind(vec![element_ty])
+            }
+
+            ty::Array(element_ty, _) | ty::Slice(element_ty) => t.rebind(vec![element_ty]),
+
+            ty::Tuple(tys) => {
+                // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
+                t.rebind(tys.iter().collect())
+            }
+
+            ty::Closure(_, args) => {
+                let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty());
+                t.rebind(vec![ty])
+            }
+
+            ty::Coroutine(_, args) => {
+                let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty());
+                let witness = args.as_coroutine().witness();
+                t.rebind([ty].into_iter().chain(iter::once(witness)).collect())
+            }
+
+            ty::CoroutineWitness(def_id, args) => {
+                bind_coroutine_hidden_types_above(self.infcx, def_id, args, t.bound_vars())
+            }
+
+            // For `PhantomData<T>`, we pass `T`.
+            ty::Adt(def, args) if def.is_phantom_data() => t.rebind(args.types().collect()),
+
+            ty::Adt(def, args) => {
+                t.rebind(def.all_fields().map(|f| f.ty(self.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.
+                match self.tcx().type_of_opaque(def_id) {
+                    Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]),
+                    Err(_) => {
+                        return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
+                    }
+                }
+            }
+        })
+    }
+
+    fn collect_predicates_for_types(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: ObligationCause<'tcx>,
+        recursion_depth: usize,
+        trait_def_id: DefId,
+        types: ty::Binder<'tcx, Vec<Ty<'tcx>>>,
+    ) -> Vec<PredicateObligation<'tcx>> {
+        // Because the types were potentially derived from
+        // higher-ranked obligations they may reference late-bound
+        // regions. For example, `for<'a> Foo<&'a i32> : Copy` would
+        // yield a type like `for<'a> &'a i32`. In general, we
+        // maintain the invariant that we never manipulate bound
+        // regions, so we have to process these bound regions somehow.
+        //
+        // The strategy is to:
+        //
+        // 1. Instantiate those regions to placeholder regions (e.g.,
+        //    `for<'a> &'a i32` becomes `&0 i32`.
+        // 2. Produce something like `&'0 i32 : Copy`
+        // 3. Re-bind the regions back to `for<'a> &'a i32 : Copy`
+
+        types
+            .as_ref()
+            .skip_binder() // binder moved -\
+            .iter()
+            .flat_map(|ty| {
+                let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(*ty); // <----/
+
+                let placeholder_ty = self.infcx.instantiate_binder_with_placeholders(ty);
+                let Normalized { value: normalized_ty, mut obligations } =
+                    ensure_sufficient_stack(|| {
+                        project::normalize_with_depth(
+                            self,
+                            param_env,
+                            cause.clone(),
+                            recursion_depth,
+                            placeholder_ty,
+                        )
+                    });
+
+                let tcx = self.tcx();
+                let trait_ref = if tcx.generics_of(trait_def_id).params.len() == 1 {
+                    ty::TraitRef::new(tcx, trait_def_id, [normalized_ty])
+                } else {
+                    // If this is an ill-formed auto/built-in trait, then synthesize
+                    // new error args for the missing generics.
+                    let err_args = ty::GenericArgs::extend_with_error(
+                        tcx,
+                        trait_def_id,
+                        &[normalized_ty.into()],
+                    );
+                    ty::TraitRef::new(tcx, trait_def_id, err_args)
+                };
+
+                let obligation = Obligation::new(self.tcx(), cause.clone(), param_env, trait_ref);
+                obligations.push(obligation);
+                obligations
+            })
+            .collect()
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Matching
+    //
+    // Matching is a common path used for both evaluation and
+    // confirmation. It basically unifies types that appear in impls
+    // and traits. This does affect the surrounding environment;
+    // therefore, when used during evaluation, match routines must be
+    // run inside of a `probe()` so that their side-effects are
+    // contained.
+
+    fn rematch_impl(
+        &mut self,
+        impl_def_id: DefId,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Normalized<'tcx, GenericArgsRef<'tcx>> {
+        let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
+        match self.match_impl(impl_def_id, impl_trait_ref, obligation) {
+            Ok(args) => args,
+            Err(()) => {
+                // FIXME: A rematch may fail when a candidate cache hit occurs
+                // on the freshened form of the trait predicate, but the match
+                // fails for some reason that is not captured in the freshened
+                // cache key. For example, equating an impl trait ref against
+                // the placeholder trait ref may fail due the Generalizer relation
+                // raising a CyclicalTy error due to a sub_root_var relation
+                // for a variable being generalized...
+                let guar = self.infcx.dcx().span_delayed_bug(
+                    obligation.cause.span,
+                    format!(
+                        "Impl {impl_def_id:?} was matchable against {obligation:?} but now is not"
+                    ),
+                );
+                let value = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id);
+                let err = Ty::new_error(self.tcx(), guar);
+                let value = value.fold_with(&mut BottomUpFolder {
+                    tcx: self.tcx(),
+                    ty_op: |_| err,
+                    lt_op: |l| l,
+                    ct_op: |c| c,
+                });
+                Normalized { value, obligations: vec![] }
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip(self), ret)]
+    fn match_impl(
+        &mut self,
+        impl_def_id: DefId,
+        impl_trait_ref: EarlyBinder<ty::TraitRef<'tcx>>,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> Result<Normalized<'tcx, GenericArgsRef<'tcx>>, ()> {
+        let placeholder_obligation =
+            self.infcx.instantiate_binder_with_placeholders(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);
+
+        let impl_trait_ref = impl_trait_ref.instantiate(self.tcx(), impl_args);
+        if impl_trait_ref.references_error() {
+            return Err(());
+        }
+
+        debug!(?impl_trait_ref);
+
+        let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } =
+            ensure_sufficient_stack(|| {
+                project::normalize_with_depth(
+                    self,
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    impl_trait_ref,
+                )
+            });
+
+        debug!(?impl_trait_ref, ?placeholder_obligation_trait_ref);
+
+        let cause = ObligationCause::new(
+            obligation.cause.span,
+            obligation.cause.body_id,
+            ObligationCauseCode::MatchImpl(obligation.cause.clone(), impl_def_id),
+        );
+
+        let InferOk { obligations, .. } = self
+            .infcx
+            .at(&cause, obligation.param_env)
+            .eq(DefineOpaqueTypes::No, placeholder_obligation_trait_ref, impl_trait_ref)
+            .map_err(|e| {
+                debug!("match_impl: failed eq_trait_refs due to `{}`", e.to_string(self.tcx()))
+            })?;
+        nested_obligations.extend(obligations);
+
+        if !self.is_intercrate()
+            && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation
+        {
+            debug!("reservation impls only apply in intercrate mode");
+            return Err(());
+        }
+
+        Ok(Normalized { value: impl_args, obligations: nested_obligations })
+    }
+
+    fn match_upcast_principal(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        unnormalized_upcast_principal: ty::PolyTraitRef<'tcx>,
+        a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        a_region: ty::Region<'tcx>,
+        b_region: ty::Region<'tcx>,
+    ) -> SelectionResult<'tcx, Vec<PredicateObligation<'tcx>>> {
+        let tcx = self.tcx();
+        let mut nested = vec![];
+
+        let upcast_principal = normalize_with_depth_to(
+            self,
+            obligation.param_env,
+            obligation.cause.clone(),
+            obligation.recursion_depth + 1,
+            unnormalized_upcast_principal,
+            &mut nested,
+        );
+
+        for bound in b_data {
+            match bound.skip_binder() {
+                // Check that a_ty's supertrait (upcast_principal) is compatible
+                // with the target (b_ty).
+                ty::ExistentialPredicate::Trait(target_principal) => {
+                    nested.extend(
+                        self.infcx
+                            .at(&obligation.cause, obligation.param_env)
+                            .sup(
+                                DefineOpaqueTypes::No,
+                                upcast_principal.map_bound(|trait_ref| {
+                                    ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
+                                }),
+                                bound.rebind(target_principal),
+                            )
+                            .map_err(|_| SelectionError::Unimplemented)?
+                            .into_obligations(),
+                    );
+                }
+                // Check that b_ty's projection is satisfied by exactly one of
+                // a_ty's projections. First, we look through the list to see if
+                // any match. If not, error. Then, if *more* than one matches, we
+                // return ambiguity. Otherwise, if exactly one matches, equate
+                // it with b_ty's projection.
+                ty::ExistentialPredicate::Projection(target_projection) => {
+                    let target_projection = bound.rebind(target_projection);
+                    let mut matching_projections =
+                        a_data.projection_bounds().filter(|source_projection| {
+                            // Eager normalization means that we can just use can_eq
+                            // here instead of equating and processing obligations.
+                            source_projection.item_def_id() == target_projection.item_def_id()
+                                && self.infcx.can_eq(
+                                    obligation.param_env,
+                                    *source_projection,
+                                    target_projection,
+                                )
+                        });
+                    let Some(source_projection) = matching_projections.next() else {
+                        return Err(SelectionError::Unimplemented);
+                    };
+                    if matching_projections.next().is_some() {
+                        return Ok(None);
+                    }
+                    nested.extend(
+                        self.infcx
+                            .at(&obligation.cause, obligation.param_env)
+                            .sup(DefineOpaqueTypes::No, source_projection, target_projection)
+                            .map_err(|_| SelectionError::Unimplemented)?
+                            .into_obligations(),
+                    );
+                }
+                // Check that b_ty's auto traits are present in a_ty's bounds.
+                ty::ExistentialPredicate::AutoTrait(def_id) => {
+                    if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
+                        return Err(SelectionError::Unimplemented);
+                    }
+                }
+            }
+        }
+
+        nested.push(Obligation::with_depth(
+            tcx,
+            obligation.cause.clone(),
+            obligation.recursion_depth + 1,
+            obligation.param_env,
+            ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
+        ));
+
+        Ok(Some(nested))
+    }
+
+    /// Normalize `where_clause_trait_ref` and try to match it against
+    /// `obligation`. If successful, return any predicates that
+    /// result from the normalization.
+    fn match_where_clause_trait_ref(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        where_clause_trait_ref: ty::PolyTraitRef<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
+        self.match_poly_trait_ref(obligation, where_clause_trait_ref)
+    }
+
+    /// Returns `Ok` if `poly_trait_ref` being true implies that the
+    /// obligation is satisfied.
+    #[instrument(skip(self), level = "debug")]
+    fn match_poly_trait_ref(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        poly_trait_ref: ty::PolyTraitRef<'tcx>,
+    ) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
+        self.infcx
+            .at(&obligation.cause, obligation.param_env)
+            .sup(DefineOpaqueTypes::No, obligation.predicate.to_poly_trait_ref(), poly_trait_ref)
+            .map(|InferOk { obligations, .. }| obligations)
+            .map_err(|_| ())
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Miscellany
+
+    fn match_fresh_trait_refs(
+        &self,
+        previous: ty::PolyTraitPredicate<'tcx>,
+        current: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool {
+        let mut matcher = MatchAgainstFreshVars::new(self.tcx());
+        matcher.relate(previous, current).is_ok()
+    }
+
+    fn push_stack<'o>(
+        &mut self,
+        previous_stack: TraitObligationStackList<'o, 'tcx>,
+        obligation: &'o PolyTraitObligation<'tcx>,
+    ) -> TraitObligationStack<'o, 'tcx> {
+        let fresh_trait_pred = obligation.predicate.fold_with(&mut self.freshener);
+
+        let dfn = previous_stack.cache.next_dfn();
+        let depth = previous_stack.depth() + 1;
+        TraitObligationStack {
+            obligation,
+            fresh_trait_pred,
+            reached_depth: Cell::new(depth),
+            previous: previous_stack,
+            dfn,
+            depth,
+        }
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn closure_trait_ref_unnormalized(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        args: GenericArgsRef<'tcx>,
+        fn_host_effect: ty::Const<'tcx>,
+    ) -> ty::PolyTraitRef<'tcx> {
+        let closure_sig = args.as_closure().sig();
+
+        debug!(?closure_sig);
+
+        // NOTE: The self-type is an unboxed closure type and hence is
+        // in fact unparameterized (or at least does not reference any
+        // regions bound in the obligation).
+        let self_ty = obligation
+            .predicate
+            .self_ty()
+            .no_bound_vars()
+            .expect("unboxed closure type should not capture bound vars from the predicate");
+
+        closure_trait_ref_and_return_type(
+            self.tcx(),
+            obligation.predicate.def_id(),
+            self_ty,
+            closure_sig,
+            util::TupleArgumentsFlag::No,
+            fn_host_effect,
+        )
+        .map_bound(|(trait_ref, _)| trait_ref)
+    }
+
+    /// Returns the obligations that are implied by instantiating an
+    /// impl or trait. The obligations are substituted and fully
+    /// normalized. This is used when confirming an impl or default
+    /// impl.
+    #[instrument(level = "debug", skip(self, cause, param_env))]
+    fn impl_or_trait_obligations(
+        &mut self,
+        cause: &ObligationCause<'tcx>,
+        recursion_depth: usize,
+        param_env: ty::ParamEnv<'tcx>,
+        def_id: DefId,              // of impl or trait
+        args: GenericArgsRef<'tcx>, // for impl or trait
+        parent_trait_pred: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
+    ) -> Vec<PredicateObligation<'tcx>> {
+        let tcx = self.tcx();
+
+        // To allow for one-pass evaluation of the nested obligation,
+        // each predicate must be preceded by the obligations required
+        // to normalize it.
+        // for example, if we have:
+        //    impl<U: Iterator<Item: Copy>, V: Iterator<Item = U>> Foo for V
+        // the impl will have the following predicates:
+        //    <V as Iterator>::Item = U,
+        //    U: Iterator, U: Sized,
+        //    V: Iterator, V: Sized,
+        //    <U as Iterator>::Item: Copy
+        // When we substitute, say, `V => IntoIter<u32>, U => $0`, the last
+        // obligation will normalize to `<$0 as Iterator>::Item = $1` and
+        // `$1: Copy`, so we must ensure the obligations are emitted in
+        // that order.
+        let predicates = tcx.predicates_of(def_id);
+        assert_eq!(predicates.parent, None);
+        let predicates = predicates.instantiate_own(tcx, args);
+        let mut obligations = Vec::with_capacity(predicates.len());
+        for (index, (predicate, span)) in predicates.into_iter().enumerate() {
+            let cause =
+                if Some(parent_trait_pred.def_id()) == tcx.lang_items().coerce_unsized_trait() {
+                    cause.clone()
+                } else {
+                    cause.clone().derived_cause(parent_trait_pred, |derived| {
+                        ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
+                            derived,
+                            impl_or_alias_def_id: def_id,
+                            impl_def_predicate_index: Some(index),
+                            span,
+                        }))
+                    })
+                };
+            let clause = normalize_with_depth_to(
+                self,
+                param_env,
+                cause.clone(),
+                recursion_depth,
+                predicate,
+                &mut obligations,
+            );
+            obligations.push(Obligation {
+                cause,
+                recursion_depth,
+                param_env,
+                predicate: clause.as_predicate(),
+            });
+        }
+
+        obligations
+    }
+}
+
+impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> {
+    fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> {
+        TraitObligationStackList::with(self)
+    }
+
+    fn cache(&self) -> &'o ProvisionalEvaluationCache<'tcx> {
+        self.previous.cache
+    }
+
+    fn iter(&'o self) -> TraitObligationStackList<'o, 'tcx> {
+        self.list()
+    }
+
+    /// Indicates that attempting to evaluate this stack entry
+    /// required accessing something from the stack at depth `reached_depth`.
+    fn update_reached_depth(&self, reached_depth: usize) {
+        assert!(
+            self.depth >= reached_depth,
+            "invoked `update_reached_depth` with something under this stack: \
+             self.depth={} reached_depth={}",
+            self.depth,
+            reached_depth,
+        );
+        debug!(reached_depth, "update_reached_depth");
+        let mut p = self;
+        while reached_depth < p.depth {
+            debug!(?p.fresh_trait_pred, "update_reached_depth: marking as cycle participant");
+            p.reached_depth.set(p.reached_depth.get().min(reached_depth));
+            p = p.previous.head.unwrap();
+        }
+    }
+}
+
+/// The "provisional evaluation cache" is used to store intermediate cache results
+/// when solving auto traits. Auto traits are unusual in that they can support
+/// cycles. So, for example, a "proof tree" like this would be ok:
+///
+/// - `Foo<T>: Send` :-
+///   - `Bar<T>: Send` :-
+///     - `Foo<T>: Send` -- cycle, but ok
+///   - `Baz<T>: Send`
+///
+/// Here, to prove `Foo<T>: Send`, we have to prove `Bar<T>: Send` and
+/// `Baz<T>: Send`. Proving `Bar<T>: Send` in turn required `Foo<T>: Send`.
+/// For non-auto traits, this cycle would be an error, but for auto traits (because
+/// they are coinductive) it is considered ok.
+///
+/// However, there is a complication: at the point where we have
+/// "proven" `Bar<T>: Send`, we have in fact only proven it
+/// *provisionally*. In particular, we proved that `Bar<T>: Send`
+/// *under the assumption* that `Foo<T>: Send`. But what if we later
+/// find out this assumption is wrong?  Specifically, we could
+/// encounter some kind of error proving `Baz<T>: Send`. In that case,
+/// `Bar<T>: Send` didn't turn out to be true.
+///
+/// In Issue #60010, we found a bug in rustc where it would cache
+/// these intermediate results. This was fixed in #60444 by disabling
+/// *all* caching for things involved in a cycle -- in our example,
+/// that would mean we don't cache that `Bar<T>: Send`. But this led
+/// to large slowdowns.
+///
+/// Specifically, imagine this scenario, where proving `Baz<T>: Send`
+/// first requires proving `Bar<T>: Send` (which is true:
+///
+/// - `Foo<T>: Send` :-
+///   - `Bar<T>: Send` :-
+///     - `Foo<T>: Send` -- cycle, but ok
+///   - `Baz<T>: Send`
+///     - `Bar<T>: Send` -- would be nice for this to be a cache hit!
+///     - `*const T: Send` -- but what if we later encounter an error?
+///
+/// The *provisional evaluation cache* resolves this issue. It stores
+/// cache results that we've proven but which were involved in a cycle
+/// in some way. We track the minimal stack depth (i.e., the
+/// farthest from the top of the stack) that we are dependent on.
+/// The idea is that the cache results within are all valid -- so long as
+/// none of the nodes in between the current node and the node at that minimum
+/// depth result in an error (in which case the cached results are just thrown away).
+///
+/// During evaluation, we consult this provisional cache and rely on
+/// it. Accessing a cached value is considered equivalent to accessing
+/// a result at `reached_depth`, so it marks the *current* solution as
+/// provisional as well. If an error is encountered, we toss out any
+/// provisional results added from the subtree that encountered the
+/// error. When we pop the node at `reached_depth` from the stack, we
+/// can commit all the things that remain in the provisional cache.
+struct ProvisionalEvaluationCache<'tcx> {
+    /// next "depth first number" to issue -- just a counter
+    dfn: Cell<usize>,
+
+    /// Map from cache key to the provisionally evaluated thing.
+    /// The cache entries contain the result but also the DFN in which they
+    /// were added. The DFN is used to clear out values on failure.
+    ///
+    /// Imagine we have a stack like:
+    ///
+    /// - `A B C` and we add a cache for the result of C (DFN 2)
+    /// - Then we have a stack `A B D` where `D` has DFN 3
+    /// - We try to solve D by evaluating E: `A B D E` (DFN 4)
+    /// - `E` generates various cache entries which have cyclic dependencies on `B`
+    ///   - `A B D E F` and so forth
+    ///   - the DFN of `F` for example would be 5
+    /// - then we determine that `E` is in error -- we will then clear
+    ///   all cache values whose DFN is >= 4 -- in this case, that
+    ///   means the cached value for `F`.
+    map: RefCell<FxIndexMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>,
+
+    /// The stack of args that we assume to be true because a `WF(arg)` predicate
+    /// is on the stack above (and because of wellformedness is coinductive).
+    /// In an "ideal" world, this would share a stack with trait predicates in
+    /// `TraitObligationStack`. However, trait predicates are *much* hotter than
+    /// `WellFormed` predicates, and it's very likely that the additional matches
+    /// will have a perf effect. The value here is the well-formed `GenericArg`
+    /// and the depth of the trait predicate *above* that well-formed predicate.
+    wf_args: RefCell<Vec<(ty::GenericArg<'tcx>, usize)>>,
+}
+
+/// A cache value for the provisional cache: contains the depth-first
+/// number (DFN) and result.
+#[derive(Copy, Clone, Debug)]
+struct ProvisionalEvaluation {
+    from_dfn: usize,
+    reached_depth: usize,
+    result: EvaluationResult,
+}
+
+impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> {
+    fn default() -> Self {
+        Self { dfn: Cell::new(0), map: Default::default(), wf_args: Default::default() }
+    }
+}
+
+impl<'tcx> ProvisionalEvaluationCache<'tcx> {
+    /// Get the next DFN in sequence (basically a counter).
+    fn next_dfn(&self) -> usize {
+        let result = self.dfn.get();
+        self.dfn.set(result + 1);
+        result
+    }
+
+    /// Check the provisional cache for any result for
+    /// `fresh_trait_ref`. If there is a hit, then you must consider
+    /// it an access to the stack slots at depth
+    /// `reached_depth` (from the returned value).
+    fn get_provisional(
+        &self,
+        fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> Option<ProvisionalEvaluation> {
+        debug!(
+            ?fresh_trait_pred,
+            "get_provisional = {:#?}",
+            self.map.borrow().get(&fresh_trait_pred),
+        );
+        Some(*self.map.borrow().get(&fresh_trait_pred)?)
+    }
+
+    /// Insert a provisional result into the cache. The result came
+    /// from the node with the given DFN. It accessed a minimum depth
+    /// of `reached_depth` to compute. It evaluated `fresh_trait_pred`
+    /// and resulted in `result`.
+    fn insert_provisional(
+        &self,
+        from_dfn: usize,
+        reached_depth: usize,
+        fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        result: EvaluationResult,
+    ) {
+        debug!(?from_dfn, ?fresh_trait_pred, ?result, "insert_provisional");
+
+        let mut map = self.map.borrow_mut();
+
+        // Subtle: when we complete working on the DFN `from_dfn`, anything
+        // that remains in the provisional cache must be dependent on some older
+        // stack entry than `from_dfn`. We have to update their depth with our transitive
+        // depth in that case or else it would be referring to some popped note.
+        //
+        // Example:
+        // A (reached depth 0)
+        //   ...
+        //      B // depth 1 -- reached depth = 0
+        //          C // depth 2 -- reached depth = 1 (should be 0)
+        //              B
+        //          A // depth 0
+        //   D (reached depth 1)
+        //      C (cache -- reached depth = 2)
+        for (_k, v) in &mut *map {
+            if v.from_dfn >= from_dfn {
+                v.reached_depth = reached_depth.min(v.reached_depth);
+            }
+        }
+
+        map.insert(fresh_trait_pred, ProvisionalEvaluation { from_dfn, reached_depth, result });
+    }
+
+    /// Invoked when the node with dfn `dfn` does not get a successful
+    /// result. This will clear out any provisional cache entries
+    /// that were added since `dfn` was created. This is because the
+    /// provisional entries are things which must assume that the
+    /// things on the stack at the time of their creation succeeded --
+    /// since the failing node is presently at the top of the stack,
+    /// these provisional entries must either depend on it or some
+    /// ancestor of it.
+    fn on_failure(&self, dfn: usize) {
+        debug!(?dfn, "on_failure");
+        self.map.borrow_mut().retain(|key, eval| {
+            if !eval.from_dfn >= dfn {
+                debug!("on_failure: removing {:?}", key);
+                false
+            } else {
+                true
+            }
+        });
+    }
+
+    /// Invoked when the node at depth `depth` completed without
+    /// depending on anything higher in the stack (if that completion
+    /// was a failure, then `on_failure` should have been invoked
+    /// already).
+    ///
+    /// Note that we may still have provisional cache items remaining
+    /// in the cache when this is done. For example, if there is a
+    /// cycle:
+    ///
+    /// * A depends on...
+    ///     * B depends on A
+    ///     * C depends on...
+    ///         * D depends on C
+    ///     * ...
+    ///
+    /// Then as we complete the C node we will have a provisional cache
+    /// with results for A, B, C, and D. This method would clear out
+    /// the C and D results, but leave A and B provisional.
+    ///
+    /// This is determined based on the DFN: we remove any provisional
+    /// results created since `dfn` started (e.g., in our example, dfn
+    /// would be 2, representing the C node, and hence we would
+    /// remove the result for D, which has DFN 3, but not the results for
+    /// A and B, which have DFNs 0 and 1 respectively).
+    ///
+    /// Note that we *do not* attempt to cache these cycle participants
+    /// in the evaluation cache. Doing so would require carefully computing
+    /// the correct `DepNode` to store in the cache entry:
+    /// cycle participants may implicitly depend on query results
+    /// related to other participants in the cycle, due to our logic
+    /// which examines the evaluation stack.
+    ///
+    /// We used to try to perform this caching,
+    /// but it lead to multiple incremental compilation ICEs
+    /// (see #92987 and #96319), and was very hard to understand.
+    /// Fortunately, removing the caching didn't seem to
+    /// have a performance impact in practice.
+    fn on_completion(&self, dfn: usize) {
+        debug!(?dfn, "on_completion");
+        self.map.borrow_mut().retain(|fresh_trait_pred, eval| {
+            if eval.from_dfn >= dfn {
+                debug!(?fresh_trait_pred, ?eval, "on_completion");
+                return false;
+            }
+            true
+        });
+    }
+}
+
+#[derive(Copy, Clone)]
+struct TraitObligationStackList<'o, 'tcx> {
+    cache: &'o ProvisionalEvaluationCache<'tcx>,
+    head: Option<&'o TraitObligationStack<'o, 'tcx>>,
+}
+
+impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> {
+    fn empty(cache: &'o ProvisionalEvaluationCache<'tcx>) -> TraitObligationStackList<'o, 'tcx> {
+        TraitObligationStackList { cache, head: None }
+    }
+
+    fn with(r: &'o TraitObligationStack<'o, 'tcx>) -> TraitObligationStackList<'o, 'tcx> {
+        TraitObligationStackList { cache: r.cache(), head: Some(r) }
+    }
+
+    fn head(&self) -> Option<&'o TraitObligationStack<'o, 'tcx>> {
+        self.head
+    }
+
+    fn depth(&self) -> usize {
+        if let Some(head) = self.head { head.depth } else { 0 }
+    }
+}
+
+impl<'o, 'tcx> Iterator for TraitObligationStackList<'o, 'tcx> {
+    type Item = &'o TraitObligationStack<'o, 'tcx>;
+
+    fn next(&mut self) -> Option<&'o TraitObligationStack<'o, 'tcx>> {
+        let o = self.head?;
+        *self = o.previous;
+        Some(o)
+    }
+}
+
+impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "TraitObligationStack({:?})", self.obligation)
+    }
+}
+
+pub enum ProjectionMatchesProjection {
+    Yes,
+    Ambiguous,
+    No,
+}
+
+/// Replace all regions inside the coroutine interior with late bound regions.
+/// Note that each region slot in the types gets a new fresh late bound region, which means that
+/// none of the regions inside relate to any other, even if typeck had previously found constraints
+/// that would cause them to be related.
+#[instrument(level = "trace", skip(infcx), ret)]
+fn bind_coroutine_hidden_types_above<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    def_id: DefId,
+    args: ty::GenericArgsRef<'tcx>,
+    bound_vars: &ty::List<ty::BoundVariableKind>,
+) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
+    let tcx = infcx.tcx;
+    let mut seen_tys = FxHashSet::default();
+
+    let considering_regions = infcx.considering_regions;
+
+    let num_bound_variables = bound_vars.len() as u32;
+    let mut counter = num_bound_variables;
+
+    let hidden_types: Vec<_> = tcx
+        .coroutine_hidden_types(def_id)
+        // Deduplicate tys to avoid repeated work.
+        .filter(|bty| seen_tys.insert(*bty))
+        .map(|mut bty| {
+            // Only remap erased regions if we use them.
+            if considering_regions {
+                bty = bty.map_bound(|ty| {
+                    tcx.fold_regions(ty, |r, current_depth| match r.kind() {
+                        ty::ReErased => {
+                            let br = ty::BoundRegion {
+                                var: ty::BoundVar::from_u32(counter),
+                                kind: ty::BrAnon,
+                            };
+                            counter += 1;
+                            ty::Region::new_bound(tcx, current_depth, br)
+                        }
+                        r => bug!("unexpected region: {r:?}"),
+                    })
+                })
+            }
+
+            bty.instantiate(tcx, args)
+        })
+        .collect();
+    let bound_vars =
+        tcx.mk_bound_variable_kinds_from_iter(bound_vars.iter().chain(
+            (num_bound_variables..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon)),
+        ));
+    ty::Binder::bind_with_vars(hidden_types, bound_vars)
+}
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
new file mode 100644
index 00000000000..d43ab0c8e85
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -0,0 +1,514 @@
+//! Logic and data structures related to impl specialization, explained in
+//! greater detail below.
+//!
+//! At the moment, this implementation support only the simple "chain" rule:
+//! If any two impls overlap, one must be a strict subset of the other.
+//!
+//! See the [rustc dev guide] for a bit more detail on how specialization
+//! fits together with the rest of the trait machinery.
+//!
+//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
+
+pub mod specialization_graph;
+use rustc_infer::infer::DefineOpaqueTypes;
+use specialization_graph::GraphExt;
+
+use crate::errors::NegativePositiveConflict;
+use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt};
+use crate::traits::select::IntercrateAmbiguityCause;
+use crate::traits::{
+    self, coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt,
+};
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::{error_code, DelayDm, Diagnostic};
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{GenericArgs, GenericArgsRef};
+use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
+use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
+use rustc_span::{Span, DUMMY_SP};
+
+use super::util;
+use super::SelectionContext;
+
+/// Information pertinent to an overlapping impl error.
+#[derive(Debug)]
+pub struct OverlapError<'tcx> {
+    pub with_impl: DefId,
+    pub trait_ref: ty::TraitRef<'tcx>,
+    pub self_ty: Option<Ty<'tcx>>,
+    pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
+    pub involves_placeholder: bool,
+}
+
+/// Given a subst for the requested impl, translate it to a subst
+/// appropriate for the actual item definition (whether it be in that impl,
+/// a parent impl, or the trait).
+///
+/// When we have selected one impl, but are actually using item definitions from
+/// a parent impl providing a default, we need a way to translate between the
+/// type parameters of the two impls. Here the `source_impl` is the one we've
+/// selected, and `source_args` is a substitution of its generics.
+/// And `target_node` is the impl/trait we're actually going to get the
+/// definition from. The resulting substitution will map from `target_node`'s
+/// generics to `source_impl`'s generics as instantiated by `source_subst`.
+///
+/// For example, consider the following scenario:
+///
+/// ```ignore (illustrative)
+/// trait Foo { ... }
+/// impl<T, U> Foo for (T, U) { ... }  // target impl
+/// impl<V> Foo for (V, V) { ... }     // source impl
+/// ```
+///
+/// Suppose we have selected "source impl" with `V` instantiated with `u32`.
+/// This function will produce a substitution with `T` and `U` both mapping to `u32`.
+///
+/// where-clauses add some trickiness here, because they can be used to "define"
+/// an argument indirectly:
+///
+/// ```ignore (illustrative)
+/// impl<'a, I, T: 'a> Iterator for Cloned<I>
+///    where I: Iterator<Item = &'a T>, T: Clone
+/// ```
+///
+/// In a case like this, the substitution for `T` is determined indirectly,
+/// through associated type projection. We deal with such cases by using
+/// *fulfillment* to relate the two impls, requiring that all projections are
+/// resolved.
+pub fn translate_args<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    source_impl: DefId,
+    source_args: GenericArgsRef<'tcx>,
+    target_node: specialization_graph::Node,
+) -> GenericArgsRef<'tcx> {
+    translate_args_with_cause(infcx, param_env, source_impl, source_args, target_node, |_, _| {
+        ObligationCause::dummy()
+    })
+}
+
+/// Like [translate_args], but obligations from the parent implementation
+/// are registered with the provided `ObligationCause`.
+///
+/// This is for reporting *region* errors from those bounds. Type errors should
+/// not happen because the specialization graph already checks for those, and
+/// will result in an ICE.
+pub fn translate_args_with_cause<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    source_impl: DefId,
+    source_args: GenericArgsRef<'tcx>,
+    target_node: specialization_graph::Node,
+    cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
+) -> GenericArgsRef<'tcx> {
+    debug!(
+        "translate_args({:?}, {:?}, {:?}, {:?})",
+        param_env, source_impl, source_args, target_node
+    );
+    let source_trait_ref =
+        infcx.tcx.impl_trait_ref(source_impl).unwrap().instantiate(infcx.tcx, source_args);
+
+    // translate the Self and Param parts of the substitution, since those
+    // vary across impls
+    let target_args = match target_node {
+        specialization_graph::Node::Impl(target_impl) => {
+            // no need to translate if we're targeting the impl we started with
+            if source_impl == target_impl {
+                return source_args;
+            }
+
+            fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl, cause)
+                .unwrap_or_else(|()| {
+                    bug!(
+                        "When translating substitutions from {source_impl:?} to {target_impl:?}, \
+                        the expected specialization failed to hold"
+                    )
+                })
+        }
+        specialization_graph::Node::Trait(..) => source_trait_ref.args,
+    };
+
+    // directly inherent the method generics, since those do not vary across impls
+    source_args.rebase_onto(infcx.tcx, source_impl, target_args)
+}
+
+/// Is `impl1` a specialization of `impl2`?
+///
+/// Specialization is determined by the sets of types to which the impls apply;
+/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
+/// to.
+#[instrument(skip(tcx), level = "debug")]
+pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool {
+    // The feature gate should prevent introducing new specializations, but not
+    // taking advantage of upstream ones.
+    let features = tcx.features();
+    let specialization_enabled = features.specialization || features.min_specialization;
+    if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) {
+        return false;
+    }
+
+    // We determine whether there's a subset relationship by:
+    //
+    // - replacing bound vars with placeholders in impl1,
+    // - assuming the where clauses for impl1,
+    // - instantiating impl2 with fresh inference variables,
+    // - unifying,
+    // - attempting to prove the where clauses for impl2
+    //
+    // The last three steps are encapsulated in `fulfill_implication`.
+    //
+    // See RFC 1210 for more details and justification.
+
+    // Currently we do not allow e.g., a negative impl to specialize a positive one
+    if tcx.impl_polarity(impl1_def_id) != tcx.impl_polarity(impl2_def_id) {
+        return false;
+    }
+
+    // create a parameter environment corresponding to a (placeholder) instantiation of impl1
+    let penv = tcx.param_env(impl1_def_id);
+    let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap().instantiate_identity();
+
+    // Create an infcx, taking the predicates of impl1 as assumptions:
+    let infcx = tcx.infer_ctxt().build();
+
+    // Attempt to prove that impl2 applies, given all of the above.
+    fulfill_implication(&infcx, penv, impl1_trait_ref, impl1_def_id, impl2_def_id, |_, _| {
+        ObligationCause::dummy()
+    })
+    .is_ok()
+}
+
+/// Attempt to fulfill all obligations of `target_impl` after unification with
+/// `source_trait_ref`. If successful, returns a substitution for *all* the
+/// generics of `target_impl`, including both those needed to unify with
+/// `source_trait_ref` and those whose identity is determined via a where
+/// clause in the impl.
+fn fulfill_implication<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    source_trait_ref: ty::TraitRef<'tcx>,
+    source_impl: DefId,
+    target_impl: DefId,
+    error_cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
+) -> Result<GenericArgsRef<'tcx>, ()> {
+    debug!(
+        "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
+        param_env, source_trait_ref, target_impl
+    );
+
+    let source_trait_ref =
+        match traits::fully_normalize(infcx, ObligationCause::dummy(), param_env, source_trait_ref)
+        {
+            Ok(source_trait_ref) => source_trait_ref,
+            Err(_errors) => {
+                infcx.dcx().span_delayed_bug(
+                    infcx.tcx.def_span(source_impl),
+                    format!("failed to fully normalize {source_trait_ref}"),
+                );
+                source_trait_ref
+            }
+        };
+
+    let source_trait = ImplSubject::Trait(source_trait_ref);
+
+    let selcx = &mut SelectionContext::new(infcx);
+    let target_args = infcx.fresh_args_for_item(DUMMY_SP, target_impl);
+    let (target_trait, obligations) =
+        util::impl_subject_and_oblig(selcx, param_env, target_impl, target_args, error_cause);
+
+    // do the impls unify? If not, no specialization.
+    let Ok(InferOk { obligations: more_obligations, .. }) = infcx
+        .at(&ObligationCause::dummy(), param_env)
+        .eq(DefineOpaqueTypes::No, source_trait, target_trait)
+    else {
+        debug!("fulfill_implication: {:?} does not unify with {:?}", source_trait, target_trait);
+        return Err(());
+    };
+
+    // Needs to be `in_snapshot` because this function is used to rebase
+    // substitutions, which may happen inside of a select within a probe.
+    let ocx = ObligationCtxt::new(infcx);
+    // attempt to prove all of the predicates for impl2 given those for impl1
+    // (which are packed up in penv)
+    ocx.register_obligations(obligations.chain(more_obligations));
+
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        // no dice!
+        debug!(
+            "fulfill_implication: for impls on {:?} and {:?}, \
+                 could not fulfill: {:?} given {:?}",
+            source_trait,
+            target_trait,
+            errors,
+            param_env.caller_bounds()
+        );
+        return Err(());
+    }
+
+    debug!("fulfill_implication: an impl for {:?} specializes {:?}", source_trait, target_trait);
+
+    // Now resolve the *substitution* we built for the target earlier, replacing
+    // the inference variables inside with whatever we got from fulfillment.
+    Ok(infcx.resolve_vars_if_possible(target_args))
+}
+
+/// Query provider for `specialization_graph_of`.
+pub(super) fn specialization_graph_provider(
+    tcx: TyCtxt<'_>,
+    trait_id: DefId,
+) -> specialization_graph::Graph {
+    let mut sg = specialization_graph::Graph::new();
+    let overlap_mode = specialization_graph::OverlapMode::get(tcx, trait_id);
+
+    let mut trait_impls: Vec<_> = tcx.all_impls(trait_id).collect();
+
+    // The coherence checking implementation seems to rely on impls being
+    // iterated over (roughly) in definition order, so we are sorting by
+    // negated `CrateNum` (so remote definitions are visited first) and then
+    // by a flattened version of the `DefIndex`.
+    trait_impls
+        .sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index()));
+
+    for impl_def_id in trait_impls {
+        if let Some(impl_def_id) = impl_def_id.as_local() {
+            // This is where impl overlap checking happens:
+            let insert_result = sg.insert(tcx, impl_def_id.to_def_id(), overlap_mode);
+            // Report error if there was one.
+            let (overlap, used_to_be_allowed) = match insert_result {
+                Err(overlap) => (Some(overlap), None),
+                Ok(Some(overlap)) => (Some(overlap.error), Some(overlap.kind)),
+                Ok(None) => (None, None),
+            };
+
+            if let Some(overlap) = overlap {
+                report_overlap_conflict(tcx, overlap, impl_def_id, used_to_be_allowed, &mut sg);
+            }
+        } else {
+            let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id);
+            sg.record_impl_from_cstore(tcx, parent, impl_def_id)
+        }
+    }
+
+    sg
+}
+
+// This function is only used when
+// encountering errors and inlining
+// it negatively impacts perf.
+#[cold]
+#[inline(never)]
+fn report_overlap_conflict<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    overlap: OverlapError<'tcx>,
+    impl_def_id: LocalDefId,
+    used_to_be_allowed: Option<FutureCompatOverlapErrorKind>,
+    sg: &mut specialization_graph::Graph,
+) {
+    let impl_polarity = tcx.impl_polarity(impl_def_id.to_def_id());
+    let other_polarity = tcx.impl_polarity(overlap.with_impl);
+    match (impl_polarity, other_polarity) {
+        (ty::ImplPolarity::Negative, ty::ImplPolarity::Positive) => {
+            report_negative_positive_conflict(
+                tcx,
+                &overlap,
+                impl_def_id,
+                impl_def_id.to_def_id(),
+                overlap.with_impl,
+                sg,
+            );
+        }
+
+        (ty::ImplPolarity::Positive, ty::ImplPolarity::Negative) => {
+            report_negative_positive_conflict(
+                tcx,
+                &overlap,
+                impl_def_id,
+                overlap.with_impl,
+                impl_def_id.to_def_id(),
+                sg,
+            );
+        }
+
+        _ => {
+            report_conflicting_impls(tcx, overlap, impl_def_id, used_to_be_allowed, sg);
+        }
+    }
+}
+
+fn report_negative_positive_conflict<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    overlap: &OverlapError<'tcx>,
+    local_impl_def_id: LocalDefId,
+    negative_impl_def_id: DefId,
+    positive_impl_def_id: DefId,
+    sg: &mut specialization_graph::Graph,
+) {
+    let mut err = tcx.dcx().create_err(NegativePositiveConflict {
+        impl_span: tcx.def_span(local_impl_def_id),
+        trait_desc: overlap.trait_ref,
+        self_ty: overlap.self_ty,
+        negative_impl_span: tcx.span_of_impl(negative_impl_def_id),
+        positive_impl_span: tcx.span_of_impl(positive_impl_def_id),
+    });
+    sg.has_errored = Some(err.emit());
+}
+
+fn report_conflicting_impls<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    overlap: OverlapError<'tcx>,
+    impl_def_id: LocalDefId,
+    used_to_be_allowed: Option<FutureCompatOverlapErrorKind>,
+    sg: &mut specialization_graph::Graph,
+) {
+    let impl_span = tcx.def_span(impl_def_id);
+
+    // Work to be done after we've built the DiagnosticBuilder. We have to define it
+    // now because the struct_lint methods don't return back the DiagnosticBuilder
+    // that's passed in.
+    fn decorate<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        overlap: &OverlapError<'tcx>,
+        impl_span: Span,
+        err: &mut Diagnostic,
+    ) {
+        if (overlap.trait_ref, overlap.self_ty).references_error() {
+            err.downgrade_to_delayed_bug();
+        }
+
+        match tcx.span_of_impl(overlap.with_impl) {
+            Ok(span) => {
+                err.span_label(span, "first implementation here");
+
+                err.span_label(
+                    impl_span,
+                    format!(
+                        "conflicting implementation{}",
+                        overlap.self_ty.map_or_else(String::new, |ty| format!(" for `{ty}`"))
+                    ),
+                );
+            }
+            Err(cname) => {
+                let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
+                    Some(s) => {
+                        format!("conflicting implementation in crate `{cname}`:\n- {s}")
+                    }
+                    None => format!("conflicting implementation in crate `{cname}`"),
+                };
+                err.note(msg);
+            }
+        }
+
+        for cause in &overlap.intercrate_ambiguity_causes {
+            cause.add_intercrate_ambiguity_hint(err);
+        }
+
+        if overlap.involves_placeholder {
+            coherence::add_placeholder_note(err);
+        }
+    }
+
+    let msg = DelayDm(|| {
+        format!(
+            "conflicting implementations of trait `{}`{}{}",
+            overlap.trait_ref.print_trait_sugared(),
+            overlap.self_ty.map_or_else(String::new, |ty| format!(" for type `{ty}`")),
+            match used_to_be_allowed {
+                Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)",
+                _ => "",
+            }
+        )
+    });
+
+    match used_to_be_allowed {
+        None => {
+            let reported = if overlap.with_impl.is_local()
+                || tcx.orphan_check_impl(impl_def_id).is_ok()
+            {
+                let mut err = tcx.dcx().struct_span_err(impl_span, msg);
+                err.code(error_code!(E0119));
+                decorate(tcx, &overlap, impl_span, &mut err);
+                Some(err.emit())
+            } else {
+                Some(
+                    tcx.dcx()
+                        .span_delayed_bug(impl_span, "impl should have failed the orphan check"),
+                )
+            };
+            sg.has_errored = reported;
+        }
+        Some(kind) => {
+            let lint = match kind {
+                FutureCompatOverlapErrorKind::Issue33140 => ORDER_DEPENDENT_TRAIT_OBJECTS,
+                FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK,
+            };
+            tcx.struct_span_lint_hir(
+                lint,
+                tcx.local_def_id_to_hir_id(impl_def_id),
+                impl_span,
+                msg,
+                |err| {
+                    decorate(tcx, &overlap, impl_span, err);
+                },
+            );
+        }
+    };
+}
+
+/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a
+/// string.
+pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
+    use std::fmt::Write;
+
+    let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity();
+    let mut w = "impl".to_owned();
+
+    let args = GenericArgs::identity_for_item(tcx, impl_def_id);
+
+    // FIXME: Currently only handles ?Sized.
+    //        Needs to support ?Move and ?DynSized when they are implemented.
+    let mut types_without_default_bounds = FxIndexSet::default();
+    let sized_trait = tcx.lang_items().sized_trait();
+
+    let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>();
+    if !arg_names.is_empty() {
+        types_without_default_bounds.extend(args.types());
+        w.push('<');
+        w.push_str(&arg_names.join(", "));
+        w.push('>');
+    }
+
+    write!(
+        w,
+        " {} for {}",
+        trait_ref.print_only_trait_path(),
+        tcx.type_of(impl_def_id).instantiate_identity()
+    )
+    .unwrap();
+
+    // The predicates will contain default bounds like `T: Sized`. We need to
+    // remove these bounds, and add `T: ?Sized` to any untouched type parameters.
+    let predicates = tcx.predicates_of(impl_def_id).predicates;
+    let mut pretty_predicates =
+        Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
+
+    for (p, _) in predicates {
+        if let Some(poly_trait_ref) = p.as_trait_clause() {
+            if Some(poly_trait_ref.def_id()) == sized_trait {
+                types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder());
+                continue;
+            }
+        }
+        pretty_predicates.push(p.to_string());
+    }
+
+    pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized")));
+
+    if !pretty_predicates.is_empty() {
+        write!(w, "\n  where {}", pretty_predicates.join(", ")).unwrap();
+    }
+
+    w.push(';');
+    Some(w)
+}
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
new file mode 100644
index 00000000000..e9a592bdee7
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
@@ -0,0 +1,431 @@
+use super::OverlapError;
+
+use crate::traits;
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
+use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
+
+pub use rustc_middle::traits::specialization_graph::*;
+
+#[derive(Copy, Clone, Debug)]
+pub enum FutureCompatOverlapErrorKind {
+    Issue33140,
+    LeakCheck,
+}
+
+#[derive(Debug)]
+pub struct FutureCompatOverlapError<'tcx> {
+    pub error: OverlapError<'tcx>,
+    pub kind: FutureCompatOverlapErrorKind,
+}
+
+/// The result of attempting to insert an impl into a group of children.
+#[derive(Debug)]
+enum Inserted<'tcx> {
+    /// The impl was inserted as a new child in this group of children.
+    BecameNewSibling(Option<FutureCompatOverlapError<'tcx>>),
+
+    /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc.
+    ReplaceChildren(Vec<DefId>),
+
+    /// The impl is a specialization of an existing child.
+    ShouldRecurseOn(DefId),
+}
+
+trait ChildrenExt<'tcx> {
+    fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
+    fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
+
+    fn insert(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        impl_def_id: DefId,
+        simplified_self: Option<SimplifiedType>,
+        overlap_mode: OverlapMode,
+    ) -> Result<Inserted<'tcx>, OverlapError<'tcx>>;
+}
+
+impl<'tcx> ChildrenExt<'tcx> for Children {
+    /// Insert an impl into this set of children without comparing to any existing impls.
+    fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
+        let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
+        if let Some(st) =
+            fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey)
+        {
+            debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st);
+            self.non_blanket_impls.entry(st).or_default().push(impl_def_id)
+        } else {
+            debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id);
+            self.blanket_impls.push(impl_def_id)
+        }
+    }
+
+    /// Removes an impl from this set of children. Used when replacing
+    /// an impl with a parent. The impl must be present in the list of
+    /// children already.
+    fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
+        let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
+        let vec: &mut Vec<DefId>;
+        if let Some(st) =
+            fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey)
+        {
+            debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st);
+            vec = self.non_blanket_impls.get_mut(&st).unwrap();
+        } else {
+            debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id);
+            vec = &mut self.blanket_impls;
+        }
+
+        let index = vec.iter().position(|d| *d == impl_def_id).unwrap();
+        vec.remove(index);
+    }
+
+    /// Attempt to insert an impl into this set of children, while comparing for
+    /// specialization relationships.
+    #[instrument(level = "debug", skip(self, tcx), ret)]
+    fn insert(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        impl_def_id: DefId,
+        simplified_self: Option<SimplifiedType>,
+        overlap_mode: OverlapMode,
+    ) -> Result<Inserted<'tcx>, OverlapError<'tcx>> {
+        let mut last_lint = None;
+        let mut replace_children = Vec::new();
+
+        let possible_siblings = match simplified_self {
+            Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)),
+            None => PotentialSiblings::Unfiltered(iter_children(self)),
+        };
+
+        for possible_sibling in possible_siblings {
+            debug!(?possible_sibling);
+
+            let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| {
+                let trait_ref = overlap.impl_header.trait_ref.unwrap();
+                let self_ty = trait_ref.self_ty();
+
+                OverlapError {
+                    with_impl: possible_sibling,
+                    trait_ref,
+                    // Only report the `Self` type if it has at least
+                    // some outer concrete shell; otherwise, it's
+                    // not adding much information.
+                    self_ty: self_ty.has_concrete_skeleton().then_some(self_ty),
+                    intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
+                    involves_placeholder: overlap.involves_placeholder,
+                }
+            };
+
+            let report_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>,
+                                        last_lint: &mut _| {
+                // Found overlap, but no specialization; error out or report future-compat warning.
+
+                // Do we *still* get overlap if we disable the future-incompatible modes?
+                let should_err = traits::overlapping_impls(
+                    tcx,
+                    possible_sibling,
+                    impl_def_id,
+                    traits::SkipLeakCheck::default(),
+                    overlap_mode,
+                )
+                .is_some();
+
+                let error = create_overlap_error(overlap);
+
+                if should_err {
+                    Err(error)
+                } else {
+                    *last_lint = Some(FutureCompatOverlapError {
+                        error,
+                        kind: FutureCompatOverlapErrorKind::LeakCheck,
+                    });
+
+                    Ok((false, false))
+                }
+            };
+
+            let last_lint_mut = &mut last_lint;
+            let (le, ge) = traits::overlapping_impls(
+                tcx,
+                possible_sibling,
+                impl_def_id,
+                traits::SkipLeakCheck::Yes,
+                overlap_mode,
+            )
+            .map_or(Ok((false, false)), |overlap| {
+                if let Some(overlap_kind) =
+                    tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling)
+                {
+                    match overlap_kind {
+                        ty::ImplOverlapKind::Permitted { marker: _ } => {}
+                        ty::ImplOverlapKind::Issue33140 => {
+                            *last_lint_mut = Some(FutureCompatOverlapError {
+                                error: create_overlap_error(overlap),
+                                kind: FutureCompatOverlapErrorKind::Issue33140,
+                            });
+                        }
+                    }
+
+                    return Ok((false, false));
+                }
+
+                let le = tcx.specializes((impl_def_id, possible_sibling));
+                let ge = tcx.specializes((possible_sibling, impl_def_id));
+
+                if le == ge { report_overlap_error(overlap, last_lint_mut) } else { Ok((le, ge)) }
+            })?;
+
+            if le && !ge {
+                debug!(
+                    "descending as child of TraitRef {:?}",
+                    tcx.impl_trait_ref(possible_sibling).unwrap().instantiate_identity()
+                );
+
+                // The impl specializes `possible_sibling`.
+                return Ok(Inserted::ShouldRecurseOn(possible_sibling));
+            } else if ge && !le {
+                debug!(
+                    "placing as parent of TraitRef {:?}",
+                    tcx.impl_trait_ref(possible_sibling).unwrap().instantiate_identity()
+                );
+
+                replace_children.push(possible_sibling);
+            } else {
+                // Either there's no overlap, or the overlap was already reported by
+                // `overlap_error`.
+            }
+        }
+
+        if !replace_children.is_empty() {
+            return Ok(Inserted::ReplaceChildren(replace_children));
+        }
+
+        // No overlap with any potential siblings, so add as a new sibling.
+        debug!("placing as new sibling");
+        self.insert_blindly(tcx, impl_def_id);
+        Ok(Inserted::BecameNewSibling(last_lint))
+    }
+}
+
+fn iter_children(children: &mut Children) -> impl Iterator<Item = DefId> + '_ {
+    let nonblanket = children.non_blanket_impls.iter().flat_map(|(_, v)| v.iter());
+    children.blanket_impls.iter().chain(nonblanket).cloned()
+}
+
+fn filtered_children(
+    children: &mut Children,
+    st: SimplifiedType,
+) -> impl Iterator<Item = DefId> + '_ {
+    let nonblanket = children.non_blanket_impls.entry(st).or_default().iter();
+    children.blanket_impls.iter().chain(nonblanket).cloned()
+}
+
+// A custom iterator used by Children::insert
+enum PotentialSiblings<I, J>
+where
+    I: Iterator<Item = DefId>,
+    J: Iterator<Item = DefId>,
+{
+    Unfiltered(I),
+    Filtered(J),
+}
+
+impl<I, J> Iterator for PotentialSiblings<I, J>
+where
+    I: Iterator<Item = DefId>,
+    J: Iterator<Item = DefId>,
+{
+    type Item = DefId;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match *self {
+            PotentialSiblings::Unfiltered(ref mut iter) => iter.next(),
+            PotentialSiblings::Filtered(ref mut iter) => iter.next(),
+        }
+    }
+}
+
+pub trait GraphExt<'tcx> {
+    /// Insert a local impl into the specialization graph. If an existing impl
+    /// conflicts with it (has overlap, but neither specializes the other),
+    /// information about the area of overlap is returned in the `Err`.
+    fn insert(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        impl_def_id: DefId,
+        overlap_mode: OverlapMode,
+    ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>>;
+
+    /// Insert cached metadata mapping from a child impl back to its parent.
+    fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId);
+}
+
+impl<'tcx> GraphExt<'tcx> for Graph {
+    /// Insert a local impl into the specialization graph. If an existing impl
+    /// conflicts with it (has overlap, but neither specializes the other),
+    /// information about the area of overlap is returned in the `Err`.
+    fn insert(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        impl_def_id: DefId,
+        overlap_mode: OverlapMode,
+    ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>> {
+        assert!(impl_def_id.is_local());
+
+        // FIXME: use `EarlyBinder` in `self.children`
+        let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
+        let trait_def_id = trait_ref.def_id;
+
+        debug!(
+            "insert({:?}): inserting TraitRef {:?} into specialization graph",
+            impl_def_id, trait_ref
+        );
+
+        // If the reference itself contains an earlier error (e.g., due to a
+        // resolution failure), then we just insert the impl at the top level of
+        // the graph and claim that there's no overlap (in order to suppress
+        // bogus errors).
+        if trait_ref.references_error() {
+            debug!(
+                "insert: inserting dummy node for erroneous TraitRef {:?}, \
+                 impl_def_id={:?}, trait_def_id={:?}",
+                trait_ref, impl_def_id, trait_def_id
+            );
+
+            self.parent.insert(impl_def_id, trait_def_id);
+            self.children.entry(trait_def_id).or_default().insert_blindly(tcx, impl_def_id);
+            return Ok(None);
+        }
+
+        let mut parent = trait_def_id;
+        let mut last_lint = None;
+        let simplified =
+            fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey);
+
+        // Descend the specialization tree, where `parent` is the current parent node.
+        loop {
+            use self::Inserted::*;
+
+            let insert_result = self.children.entry(parent).or_default().insert(
+                tcx,
+                impl_def_id,
+                simplified,
+                overlap_mode,
+            )?;
+
+            match insert_result {
+                BecameNewSibling(opt_lint) => {
+                    last_lint = opt_lint;
+                    break;
+                }
+                ReplaceChildren(grand_children_to_be) => {
+                    // We currently have
+                    //
+                    //     P
+                    //     |
+                    //     G
+                    //
+                    // and we are inserting the impl N. We want to make it:
+                    //
+                    //     P
+                    //     |
+                    //     N
+                    //     |
+                    //     G
+
+                    // Adjust P's list of children: remove G and then add N.
+                    {
+                        let siblings = self.children.get_mut(&parent).unwrap();
+                        for &grand_child_to_be in &grand_children_to_be {
+                            siblings.remove_existing(tcx, grand_child_to_be);
+                        }
+                        siblings.insert_blindly(tcx, impl_def_id);
+                    }
+
+                    // Set G's parent to N and N's parent to P.
+                    for &grand_child_to_be in &grand_children_to_be {
+                        self.parent.insert(grand_child_to_be, impl_def_id);
+                    }
+                    self.parent.insert(impl_def_id, parent);
+
+                    // Add G as N's child.
+                    for &grand_child_to_be in &grand_children_to_be {
+                        self.children
+                            .entry(impl_def_id)
+                            .or_default()
+                            .insert_blindly(tcx, grand_child_to_be);
+                    }
+                    break;
+                }
+                ShouldRecurseOn(new_parent) => {
+                    parent = new_parent;
+                }
+            }
+        }
+
+        self.parent.insert(impl_def_id, parent);
+        Ok(last_lint)
+    }
+
+    /// Insert cached metadata mapping from a child impl back to its parent.
+    fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) {
+        if self.parent.insert(child, parent).is_some() {
+            bug!(
+                "When recording an impl from the crate store, information about its parent \
+                 was already present."
+            );
+        }
+
+        self.children.entry(parent).or_default().insert_blindly(tcx, child);
+    }
+}
+
+/// Locate the definition of an associated type in the specialization hierarchy,
+/// starting from the given impl.
+pub(crate) fn assoc_def(
+    tcx: TyCtxt<'_>,
+    impl_def_id: DefId,
+    assoc_def_id: DefId,
+) -> Result<LeafDef, ErrorGuaranteed> {
+    let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
+    let trait_def = tcx.trait_def(trait_def_id);
+
+    // This function may be called while we are still building the
+    // specialization graph that is queried below (via TraitDef::ancestors()),
+    // so, in order to avoid unnecessary infinite recursion, we manually look
+    // for the associated item at the given impl.
+    // If there is no such item in that impl, this function will fail with a
+    // cycle error if the specialization graph is currently being built.
+    if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) {
+        let item = tcx.associated_item(impl_item_id);
+        let impl_node = Node::Impl(impl_def_id);
+        return Ok(LeafDef {
+            item,
+            defining_node: impl_node,
+            finalizing_node: if item.defaultness(tcx).is_default() {
+                None
+            } else {
+                Some(impl_node)
+            },
+        });
+    }
+
+    let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
+    if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) {
+        Ok(assoc_item)
+    } else {
+        // This is saying that neither the trait nor
+        // the impl contain a definition for this
+        // associated type. Normally this situation
+        // could only arise through a compiler bug --
+        // if the user wrote a bad item name, it
+        // should have failed in astconv.
+        bug!(
+            "No associated type `{}` for {}",
+            tcx.item_name(assoc_def_id),
+            tcx.def_path_str(impl_def_id)
+        )
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
new file mode 100644
index 00000000000..868a8a3e8b8
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -0,0 +1,174 @@
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
+use rustc_span::Span;
+use std::ops::ControlFlow;
+
+/// This method traverses the structure of `ty`, trying to find an
+/// instance of an ADT (i.e. struct or enum) that doesn't implement
+/// the structural-match traits, or a generic type parameter
+/// (which cannot be determined to be structural-match).
+///
+/// The "structure of a type" includes all components that would be
+/// considered when doing a pattern match on a constant of that
+/// type.
+///
+///  * This means this method descends into fields of structs/enums,
+///    and also descends into the inner type `T` of `&T` and `&mut T`
+///
+///  * The traversal doesn't dereference unsafe pointers (`*const T`,
+///    `*mut T`), and it does not visit the type arguments of an
+///    instantiated generic like `PhantomData<T>`.
+///
+/// The reason we do this search is Rust currently require all ADTs
+/// reachable from a constant's type to implement the
+/// structural-match traits, which essentially say that
+/// the implementation of `PartialEq::eq` behaves *equivalently* to a
+/// comparison against the unfolded structure.
+///
+/// For more background on why Rust has this requirement, and issues
+/// that arose when the requirement was not enforced completely, see
+/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
+pub fn search_for_structural_match_violation<'tcx>(
+    span: Span,
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> Option<Ty<'tcx>> {
+    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value()
+}
+
+/// This implements the traversal over the structure of a given type to try to
+/// find instances of ADTs (specifically structs or enums) that do not implement
+/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`).
+struct Search<'tcx> {
+    span: Span,
+
+    tcx: TyCtxt<'tcx>,
+
+    /// Tracks ADTs previously encountered during search, so that
+    /// we will not recur on them again.
+    seen: FxHashSet<hir::def_id::DefId>,
+}
+
+impl<'tcx> Search<'tcx> {
+    fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool {
+        adt_ty.is_structural_eq_shallow(self.tcx)
+    }
+}
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> {
+    type BreakTy = Ty<'tcx>;
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        debug!("Search visiting ty: {:?}", ty);
+
+        let (adt_def, args) = match *ty.kind() {
+            ty::Adt(adt_def, args) => (adt_def, args),
+            ty::Param(_) => {
+                return ControlFlow::Break(ty);
+            }
+            ty::Dynamic(..) => {
+                return ControlFlow::Break(ty);
+            }
+            ty::Foreign(_) => {
+                return ControlFlow::Break(ty);
+            }
+            ty::Alias(..) => {
+                return ControlFlow::Break(ty);
+            }
+            ty::Closure(..) => {
+                return ControlFlow::Break(ty);
+            }
+            ty::Coroutine(..) | ty::CoroutineWitness(..) => {
+                return ControlFlow::Break(ty);
+            }
+            ty::FnDef(..) => {
+                // Types of formals and return in `fn(_) -> _` are also irrelevant;
+                // so we do not recur into them via `super_visit_with`
+                return ControlFlow::Continue(());
+            }
+            ty::Array(_, n)
+                if { n.try_eval_target_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } =>
+            {
+                // rust-lang/rust#62336: ignore type of contents
+                // for empty array.
+                return ControlFlow::Continue(());
+            }
+            ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => {
+                // These primitive types are always structural match.
+                //
+                // `Never` is kind of special here, but as it is not inhabitable, this should be fine.
+                return ControlFlow::Continue(());
+            }
+
+            ty::FnPtr(..) => {
+                return ControlFlow::Continue(());
+            }
+
+            ty::RawPtr(..) => {
+                // structural-match ignores substructure of
+                // `*const _`/`*mut _`, so skip `super_visit_with`.
+                //
+                // For example, if you have:
+                // ```
+                // struct NonStructural;
+                // #[derive(PartialEq, Eq)]
+                // struct T(*const NonStructural);
+                // const C: T = T(std::ptr::null());
+                // ```
+                //
+                // Even though `NonStructural` does not implement `PartialEq`,
+                // structural equality on `T` does not recur into the raw
+                // pointer. Therefore, one can still use `C` in a pattern.
+                return ControlFlow::Continue(());
+            }
+
+            ty::Float(_) => {
+                return ControlFlow::Continue(());
+            }
+
+            ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
+                // First check all contained types and then tell the caller to continue searching.
+                return ty.super_visit_with(self);
+            }
+            ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => {
+                bug!("unexpected type during structural-match checking: {:?}", ty);
+            }
+            ty::Error(_) => {
+                self.tcx.dcx().span_delayed_bug(self.span, "ty::Error in structural-match check");
+                // We still want to check other types after encountering an error,
+                // as this may still emit relevant errors.
+                return ControlFlow::Continue(());
+            }
+        };
+
+        if !self.seen.insert(adt_def.did()) {
+            debug!("Search already seen adt_def: {:?}", adt_def);
+            return ControlFlow::Continue(());
+        }
+
+        if !self.type_marked_structural(ty) {
+            debug!("Search found ty: {:?}", ty);
+            return ControlFlow::Break(ty);
+        }
+
+        // structural-match does not care about the
+        // instantiation of the generics in an ADT (it
+        // instead looks directly at its fields outside
+        // this match), so we skip super_visit_with.
+        //
+        // (Must not recur on args for `PhantomData<T>` cf
+        // rust-lang/rust#55028 and rust-lang/rust#55837; but also
+        // want to skip args when only uses of generic are
+        // behind unsafe pointers `*const T`/`*mut T`.)
+
+        // even though we skip super_visit_with, we must recur on
+        // fields of ADT.
+        let tcx = self.tcx;
+        adt_def.all_fields().map(|field| field.ty(tcx, args)).try_for_each(|field_ty| {
+            let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty);
+            debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty);
+            ty.visit_with(self)
+        })
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
new file mode 100644
index 00000000000..e0f9fdc3827
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
@@ -0,0 +1,60 @@
+use rustc_infer::infer::at::At;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::traits::{FulfillmentError, TraitEngine};
+use rustc_middle::ty::{self, Ty};
+
+use crate::traits::{NormalizeExt, Obligation};
+
+pub trait StructurallyNormalizeExt<'tcx> {
+    fn structurally_normalize(
+        &self,
+        ty: Ty<'tcx>,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>>;
+}
+
+impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
+    fn structurally_normalize(
+        &self,
+        ty: Ty<'tcx>,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
+        assert!(!ty.is_ty_var(), "should have resolved vars before calling");
+
+        if self.infcx.next_trait_solver() {
+            // FIXME(-Znext-solver): Should we resolve opaques here?
+            let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) = *ty.kind() else {
+                return Ok(ty);
+            };
+
+            let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin {
+                kind: TypeVariableOriginKind::NormalizeProjectionType,
+                span: self.cause.span,
+            });
+
+            // We simply emit an `alias-eq` goal here, since that will take care of
+            // normalizing the LHS of the projection until it is a rigid projection
+            // (or a not-yet-defined opaque in scope).
+            let obligation = Obligation::new(
+                self.infcx.tcx,
+                self.cause.clone(),
+                self.param_env,
+                ty::PredicateKind::AliasRelate(
+                    ty.into(),
+                    new_infer_ty.into(),
+                    ty::AliasRelationDirection::Equate,
+                ),
+            );
+
+            fulfill_cx.register_predicate_obligation(self.infcx, obligation);
+            let errors = fulfill_cx.select_where_possible(self.infcx);
+            if !errors.is_empty() {
+                return Err(errors);
+            }
+
+            Ok(self.infcx.resolve_vars_if_possible(new_infer_ty))
+        } else {
+            Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx))
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
new file mode 100644
index 00000000000..19eae93df9c
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -0,0 +1,384 @@
+use super::NormalizeExt;
+use super::{ObligationCause, PredicateObligation, SelectionContext};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Diagnostic;
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::InferOk;
+use rustc_middle::ty::GenericArgsRef;
+use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
+use rustc_span::Span;
+use smallvec::SmallVec;
+
+pub use rustc_infer::traits::util::*;
+
+///////////////////////////////////////////////////////////////////////////
+// `TraitAliasExpander` iterator
+///////////////////////////////////////////////////////////////////////////
+
+/// "Trait alias expansion" is the process of expanding a sequence of trait
+/// references into another sequence by transitively following all trait
+/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias
+/// `trait Foo = Bar + Sync;`, and another trait alias
+/// `trait Bar = Read + Write`, then the bounds would expand to
+/// `Read + Write + Sync + Send`.
+/// Expansion is done via a DFS (depth-first search), and the `visited` field
+/// is used to avoid cycles.
+pub struct TraitAliasExpander<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    stack: Vec<TraitAliasExpansionInfo<'tcx>>,
+}
+
+/// Stores information about the expansion of a trait via a path of zero or more trait aliases.
+#[derive(Debug, Clone)]
+pub struct TraitAliasExpansionInfo<'tcx> {
+    pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>,
+}
+
+impl<'tcx> TraitAliasExpansionInfo<'tcx> {
+    fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self {
+        Self { path: smallvec![(trait_ref, span)] }
+    }
+
+    /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate
+    /// trait aliases.
+    pub fn label_with_exp_info(
+        &self,
+        diag: &mut Diagnostic,
+        top_label: &'static str,
+        use_desc: &str,
+    ) {
+        diag.span_label(self.top().1, top_label);
+        if self.path.len() > 1 {
+            for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) {
+                diag.span_label(*sp, format!("referenced here ({use_desc})"));
+            }
+        }
+        if self.top().1 != self.bottom().1 {
+            // When the trait object is in a return type these two spans match, we don't want
+            // redundant labels.
+            diag.span_label(
+                self.bottom().1,
+                format!("trait alias used in trait object type ({use_desc})"),
+            );
+        }
+    }
+
+    pub fn trait_ref(&self) -> ty::PolyTraitRef<'tcx> {
+        self.top().0
+    }
+
+    pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) {
+        self.path.last().unwrap()
+    }
+
+    pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) {
+        self.path.first().unwrap()
+    }
+
+    fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self {
+        let mut path = self.path.clone();
+        path.push((trait_ref, span));
+
+        Self { path }
+    }
+}
+
+pub fn expand_trait_aliases<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_refs: impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)>,
+) -> TraitAliasExpander<'tcx> {
+    let items: Vec<_> =
+        trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect();
+    TraitAliasExpander { tcx, stack: items }
+}
+
+impl<'tcx> TraitAliasExpander<'tcx> {
+    /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item`
+    /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`.
+    /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a
+    /// trait alias.
+    /// The return value indicates whether `item` should be yielded to the user.
+    fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool {
+        let tcx = self.tcx;
+        let trait_ref = item.trait_ref();
+        let pred = trait_ref.to_predicate(tcx);
+
+        debug!("expand_trait_aliases: trait_ref={:?}", trait_ref);
+
+        // Don't recurse if this bound is not a trait alias.
+        let is_alias = tcx.is_trait_alias(trait_ref.def_id());
+        if !is_alias {
+            return true;
+        }
+
+        // Don't recurse if this trait alias is already on the stack for the DFS search.
+        let anon_pred = anonymize_predicate(tcx, pred);
+        if item
+            .path
+            .iter()
+            .rev()
+            .skip(1)
+            .any(|&(tr, _)| anonymize_predicate(tcx, tr.to_predicate(tcx)) == anon_pred)
+        {
+            return false;
+        }
+
+        // Get components of trait alias.
+        let predicates = tcx.implied_predicates_of(trait_ref.def_id());
+        debug!(?predicates);
+
+        let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
+            pred.subst_supertrait(tcx, &trait_ref)
+                .as_trait_clause()
+                .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span))
+        });
+        debug!("expand_trait_aliases: items={:?}", items.clone().collect::<Vec<_>>());
+
+        self.stack.extend(items);
+
+        false
+    }
+}
+
+impl<'tcx> Iterator for TraitAliasExpander<'tcx> {
+    type Item = TraitAliasExpansionInfo<'tcx>;
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.stack.len(), None)
+    }
+
+    fn next(&mut self) -> Option<TraitAliasExpansionInfo<'tcx>> {
+        while let Some(item) = self.stack.pop() {
+            if self.expand(&item) {
+                return Some(item);
+            }
+        }
+        None
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Iterator over def-IDs of supertraits
+///////////////////////////////////////////////////////////////////////////
+
+pub struct SupertraitDefIds<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    stack: Vec<DefId>,
+    visited: FxHashSet<DefId>,
+}
+
+pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> {
+    SupertraitDefIds {
+        tcx,
+        stack: vec![trait_def_id],
+        visited: Some(trait_def_id).into_iter().collect(),
+    }
+}
+
+impl Iterator for SupertraitDefIds<'_> {
+    type Item = DefId;
+
+    fn next(&mut self) -> Option<DefId> {
+        let def_id = self.stack.pop()?;
+        let predicates = self.tcx.super_predicates_of(def_id);
+        let visited = &mut self.visited;
+        self.stack.extend(
+            predicates
+                .predicates
+                .iter()
+                .filter_map(|(pred, _)| pred.as_trait_clause())
+                .map(|trait_ref| trait_ref.def_id())
+                .filter(|&super_def_id| visited.insert(super_def_id)),
+        );
+        Some(def_id)
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Other
+///////////////////////////////////////////////////////////////////////////
+
+/// Instantiate all bound parameters of the impl subject with the given args,
+/// returning the resulting subject and all obligations that arise.
+/// The obligations are closed under normalization.
+pub fn impl_subject_and_oblig<'a, 'tcx>(
+    selcx: &mut SelectionContext<'a, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    impl_def_id: DefId,
+    impl_args: GenericArgsRef<'tcx>,
+    cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
+) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
+    let subject = selcx.tcx().impl_subject(impl_def_id);
+    let subject = subject.instantiate(selcx.tcx(), impl_args);
+
+    let InferOk { value: subject, obligations: normalization_obligations1 } =
+        selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(subject);
+
+    let predicates = selcx.tcx().predicates_of(impl_def_id);
+    let predicates = predicates.instantiate(selcx.tcx(), impl_args);
+    let InferOk { value: predicates, obligations: normalization_obligations2 } =
+        selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates);
+    let impl_obligations = super::predicates_for_generics(cause, param_env, predicates);
+
+    let impl_obligations =
+        impl_obligations.chain(normalization_obligations1).chain(normalization_obligations2);
+
+    (subject, impl_obligations)
+}
+
+/// Casts a trait reference into a reference to one of its super
+/// traits; returns `None` if `target_trait_def_id` is not a
+/// supertrait.
+pub fn upcast_choices<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    source_trait_ref: ty::PolyTraitRef<'tcx>,
+    target_trait_def_id: DefId,
+) -> Vec<ty::PolyTraitRef<'tcx>> {
+    if source_trait_ref.def_id() == target_trait_def_id {
+        return vec![source_trait_ref]; // Shortcut the most common case.
+    }
+
+    supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect()
+}
+
+/// Given an upcast trait object described by `object`, returns the
+/// index of the method `method_def_id` (which should be part of
+/// `object.upcast_trait_ref`) within the vtable for `object`.
+pub fn get_vtable_index_of_object_method<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    vtable_base: usize,
+    method_def_id: DefId,
+) -> Option<usize> {
+    // Count number of methods preceding the one we are selecting and
+    // add them to the total offset.
+    tcx.own_existential_vtable_entries(tcx.parent(method_def_id))
+        .iter()
+        .copied()
+        .position(|def_id| def_id == method_def_id)
+        .map(|index| vtable_base + index)
+}
+
+pub fn closure_trait_ref_and_return_type<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_trait_def_id: DefId,
+    self_ty: Ty<'tcx>,
+    sig: ty::PolyFnSig<'tcx>,
+    tuple_arguments: TupleArgumentsFlag,
+    fn_host_effect: ty::Const<'tcx>,
+) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> {
+    assert!(!self_ty.has_escaping_bound_vars());
+    let arguments_tuple = match tuple_arguments {
+        TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
+        TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()),
+    };
+    let trait_ref = if tcx.generics_of(fn_trait_def_id).host_effect_index.is_some() {
+        ty::TraitRef::new(
+            tcx,
+            fn_trait_def_id,
+            [
+                ty::GenericArg::from(self_ty),
+                ty::GenericArg::from(arguments_tuple),
+                ty::GenericArg::from(fn_host_effect),
+            ],
+        )
+    } else {
+        ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple])
+    };
+    sig.map_bound(|sig| (trait_ref, sig.output()))
+}
+
+pub fn coroutine_trait_ref_and_outputs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_trait_def_id: DefId,
+    self_ty: Ty<'tcx>,
+    sig: ty::GenSig<'tcx>,
+) -> (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>) {
+    assert!(!self_ty.has_escaping_bound_vars());
+    let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, sig.resume_ty]);
+    (trait_ref, sig.yield_ty, sig.return_ty)
+}
+
+pub fn future_trait_ref_and_outputs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_trait_def_id: DefId,
+    self_ty: Ty<'tcx>,
+    sig: ty::GenSig<'tcx>,
+) -> (ty::TraitRef<'tcx>, Ty<'tcx>) {
+    assert!(!self_ty.has_escaping_bound_vars());
+    let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty]);
+    (trait_ref, sig.return_ty)
+}
+
+pub fn iterator_trait_ref_and_outputs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    iterator_def_id: DefId,
+    self_ty: Ty<'tcx>,
+    sig: ty::GenSig<'tcx>,
+) -> (ty::TraitRef<'tcx>, Ty<'tcx>) {
+    assert!(!self_ty.has_escaping_bound_vars());
+    let trait_ref = ty::TraitRef::new(tcx, iterator_def_id, [self_ty]);
+    (trait_ref, sig.yield_ty)
+}
+
+pub fn async_iterator_trait_ref_and_outputs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    async_iterator_def_id: DefId,
+    self_ty: Ty<'tcx>,
+    sig: ty::GenSig<'tcx>,
+) -> (ty::TraitRef<'tcx>, Ty<'tcx>) {
+    assert!(!self_ty.has_escaping_bound_vars());
+    let trait_ref = ty::TraitRef::new(tcx, async_iterator_def_id, [self_ty]);
+    (trait_ref, sig.yield_ty)
+}
+
+pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
+    assoc_item.defaultness(tcx).is_final()
+        && tcx.defaultness(assoc_item.container_id(tcx)).is_final()
+}
+
+pub enum TupleArgumentsFlag {
+    Yes,
+    No,
+}
+
+// Verify that the trait item and its implementation have compatible args lists
+pub fn check_args_compatible<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    assoc_item: ty::AssocItem,
+    args: ty::GenericArgsRef<'tcx>,
+) -> bool {
+    fn check_args_compatible_inner<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        generics: &'tcx ty::Generics,
+        args: &'tcx [ty::GenericArg<'tcx>],
+    ) -> bool {
+        if generics.count() != args.len() {
+            return false;
+        }
+
+        let (parent_args, own_args) = args.split_at(generics.parent_count);
+
+        if let Some(parent) = generics.parent
+            && let parent_generics = tcx.generics_of(parent)
+            && !check_args_compatible_inner(tcx, parent_generics, parent_args)
+        {
+            return false;
+        }
+
+        for (param, arg) in std::iter::zip(&generics.params, own_args) {
+            match (&param.kind, arg.unpack()) {
+                (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
+                | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
+                | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
+                _ => return false,
+            }
+        }
+
+        true
+    }
+
+    let generics = tcx.generics_of(assoc_item.def_id);
+    // Chop off any additional args (RPITIT) args
+    let args = &args[0..generics.count().min(args.len())];
+    check_args_compatible_inner(tcx, generics, args)
+}
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
new file mode 100644
index 00000000000..d39583b5c7d
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -0,0 +1,408 @@
+use crate::errors::DumpVTableEntries;
+use crate::traits::{impossible_predicates, is_vtable_safe_method};
+use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
+use rustc_infer::traits::util::PredicateSet;
+use rustc_infer::traits::ImplSource;
+use rustc_middle::query::Providers;
+use rustc_middle::traits::BuiltinImplSource;
+use rustc_middle::ty::visit::TypeVisitableExt;
+use rustc_middle::ty::GenericArgs;
+use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry};
+use rustc_span::{sym, Span};
+use smallvec::SmallVec;
+
+use std::fmt::Debug;
+use std::ops::ControlFlow;
+
+#[derive(Clone, Debug)]
+pub enum VtblSegment<'tcx> {
+    MetadataDSA,
+    TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
+}
+
+/// Prepare the segments for a vtable
+pub fn prepare_vtable_segments<'tcx, T>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+    segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
+) -> Option<T> {
+    prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value()
+}
+
+/// Helper for [`prepare_vtable_segments`] that returns `ControlFlow`,
+/// such that we can use `?` in the body.
+fn prepare_vtable_segments_inner<'tcx, T>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+    mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
+) -> ControlFlow<T> {
+    // The following constraints holds for the final arrangement.
+    // 1. The whole virtual table of the first direct super trait is included as the
+    //    the prefix. If this trait doesn't have any super traits, then this step
+    //    consists of the dsa metadata.
+    // 2. Then comes the proper pointer metadata(vptr) and all own methods for all
+    //    other super traits except those already included as part of the first
+    //    direct super trait virtual table.
+    // 3. finally, the own methods of this trait.
+
+    // This has the advantage that trait upcasting to the first direct super trait on each level
+    // is zero cost, and to another trait includes only replacing the pointer with one level indirection,
+    // while not using too much extra memory.
+
+    // For a single inheritance relationship like this,
+    //   D --> C --> B --> A
+    // The resulting vtable will consists of these segments:
+    //  DSA, A, B, C, D
+
+    // For a multiple inheritance relationship like this,
+    //   D --> C --> A
+    //           \-> B
+    // The resulting vtable will consists of these segments:
+    //  DSA, A, B, B-vptr, C, D
+
+    // For a diamond inheritance relationship like this,
+    //   D --> B --> A
+    //     \-> C -/
+    // The resulting vtable will consists of these segments:
+    //  DSA, A, B, C, C-vptr, D
+
+    // For a more complex inheritance relationship like this:
+    //   O --> G --> C --> A
+    //     \     \     \-> B
+    //     |     |-> F --> D
+    //     |           \-> E
+    //     |-> N --> J --> H
+    //           \     \-> I
+    //           |-> M --> K
+    //                 \-> L
+    // The resulting vtable will consists of these segments:
+    //  DSA, A, B, B-vptr, C, D, D-vptr, E, E-vptr, F, F-vptr, G,
+    //  H, H-vptr, I, I-vptr, J, J-vptr, K, K-vptr, L, L-vptr, M, M-vptr,
+    //  N, N-vptr, O
+
+    // emit dsa segment first.
+    segment_visitor(VtblSegment::MetadataDSA)?;
+
+    let mut emit_vptr_on_new_entry = false;
+    let mut visited = PredicateSet::new(tcx);
+    let predicate = trait_ref.to_predicate(tcx);
+    let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> =
+        smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))];
+    visited.insert(predicate);
+
+    // the main traversal loop:
+    // basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes
+    // such that each node is emitted after all its descendants have been emitted.
+    // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set.
+    // this is done on the fly.
+    // Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it
+    // stops after it finds a node that has a next-sibling node.
+    // This next-sibling node will used as the starting point of next slice.
+
+    // Example:
+    // For a diamond inheritance relationship like this,
+    //   D#1 --> B#0 --> A#0
+    //     \-> C#1 -/
+
+    // Starting point 0 stack [D]
+    // Loop run #0: Stack after diving in is [D B A], A is "childless"
+    // after this point, all newly visited nodes won't have a vtable that equals to a prefix of this one.
+    // Loop run #0: Emitting the slice [B A] (in reverse order), B has a next-sibling node, so this slice stops here.
+    // Loop run #0: Stack after exiting out is [D C], C is the next starting point.
+    // Loop run #1: Stack after diving in is [D C], C is "childless", since its child A is skipped(already emitted).
+    // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node.
+    // Loop run #1: Stack after exiting out is []. Now the function exits.
+
+    'outer: loop {
+        // dive deeper into the stack, recording the path
+        'diving_in: loop {
+            let &(inner_most_trait_ref, _, _) = stack.last().unwrap();
+
+            let mut direct_super_traits_iter = tcx
+                .super_predicates_of(inner_most_trait_ref.def_id())
+                .predicates
+                .into_iter()
+                .filter_map(move |(pred, _)| {
+                    pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause()
+                });
+
+            // Find an unvisited supertrait
+            match direct_super_traits_iter
+                .find(|&super_trait| visited.insert(super_trait.to_predicate(tcx)))
+            {
+                // Push it to the stack for the next iteration of 'diving_in to pick up
+                Some(unvisited_super_trait) => {
+                    // We're throwing away potential constness of super traits here.
+                    // FIXME: handle ~const super traits
+                    let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref);
+                    stack.push((
+                        next_super_trait,
+                        emit_vptr_on_new_entry,
+                        maybe_iter(Some(direct_super_traits_iter)),
+                    ))
+                }
+
+                // There are no more unvisited direct super traits, dive-in finished
+                None => break 'diving_in,
+            }
+        }
+
+        // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
+        while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
+            segment_visitor(VtblSegment::TraitOwnEntries {
+                trait_ref: inner_most_trait_ref,
+                emit_vptr: emit_vptr && !tcx.sess.opts.unstable_opts.no_trait_vptr,
+            })?;
+
+            // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable,
+            // we'll need to emit vptrs from now on.
+            if !emit_vptr_on_new_entry
+                && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id())
+            {
+                emit_vptr_on_new_entry = true;
+            }
+
+            if let Some(next_inner_most_trait_ref) =
+                siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx)))
+            {
+                // We're throwing away potential constness of super traits here.
+                // FIXME: handle ~const super traits
+                let next_inner_most_trait_ref =
+                    next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
+
+                stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings));
+
+                // just pushed a new trait onto the stack, so we need to go through its super traits
+                continue 'outer;
+            }
+        }
+
+        // the stack is empty, all done
+        return ControlFlow::Continue(());
+    }
+}
+
+/// Turns option of iterator into an iterator (this is just flatten)
+fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> {
+    // Flatten is bad perf-vise, we could probably implement a special case here that is better
+    i.into_iter().flatten()
+}
+
+fn dump_vtable_entries<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    sp: Span,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+    entries: &[VtblEntry<'tcx>],
+) {
+    tcx.dcx().emit_err(DumpVTableEntries { span: sp, trait_ref, entries: format!("{entries:#?}") });
+}
+
+fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
+    own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some()
+}
+
+fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[DefId] {
+    tcx.arena.alloc_from_iter(own_existential_vtable_entries_iter(tcx, trait_def_id))
+}
+
+fn own_existential_vtable_entries_iter(
+    tcx: TyCtxt<'_>,
+    trait_def_id: DefId,
+) -> impl Iterator<Item = DefId> + '_ {
+    let trait_methods = tcx
+        .associated_items(trait_def_id)
+        .in_definition_order()
+        .filter(|item| item.kind == ty::AssocKind::Fn);
+
+    // Now list each method's DefId (for within its trait).
+    let own_entries = trait_methods.filter_map(move |&trait_method| {
+        debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
+        let def_id = trait_method.def_id;
+
+        // Some methods cannot be called on an object; skip those.
+        if !is_vtable_safe_method(tcx, trait_def_id, trait_method) {
+            debug!("own_existential_vtable_entry: not vtable safe");
+            return None;
+        }
+
+        Some(def_id)
+    });
+
+    own_entries
+}
+
+/// Given a trait `trait_ref`, iterates the vtable entries
+/// that come from `trait_ref`, including its supertraits.
+fn vtable_entries<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+) -> &'tcx [VtblEntry<'tcx>] {
+    debug!("vtable_entries({:?})", trait_ref);
+
+    let mut entries = vec![];
+
+    let vtable_segment_callback = |segment| -> ControlFlow<()> {
+        match segment {
+            VtblSegment::MetadataDSA => {
+                entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
+            }
+            VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+                let existential_trait_ref = trait_ref
+                    .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
+
+                // Lookup the shape of vtable for the trait.
+                let own_existential_entries =
+                    tcx.own_existential_vtable_entries(existential_trait_ref.def_id());
+
+                let own_entries = own_existential_entries.iter().copied().map(|def_id| {
+                    debug!("vtable_entries: trait_method={:?}", def_id);
+
+                    // The method may have some early-bound lifetimes; add regions for those.
+                    let args = trait_ref.map_bound(|trait_ref| {
+                        GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
+                            GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
+                            GenericParamDefKind::Type { .. }
+                            | GenericParamDefKind::Const { .. } => {
+                                trait_ref.args[param.index as usize]
+                            }
+                        })
+                    });
+
+                    // The trait type may have higher-ranked lifetimes in it;
+                    // erase them if they appear, so that we get the type
+                    // at some particular call site.
+                    let args =
+                        tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), args);
+
+                    // It's possible that the method relies on where-clauses that
+                    // do not hold for this particular set of type parameters.
+                    // Note that this method could then never be called, so we
+                    // do not want to try and codegen it, in that case (see #23435).
+                    let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
+                    if impossible_predicates(
+                        tcx,
+                        predicates.map(|(predicate, _)| predicate).collect(),
+                    ) {
+                        debug!("vtable_entries: predicates do not hold");
+                        return VtblEntry::Vacant;
+                    }
+
+                    let instance = ty::Instance::resolve_for_vtable(
+                        tcx,
+                        ty::ParamEnv::reveal_all(),
+                        def_id,
+                        args,
+                    )
+                    .expect("resolution failed during building vtable representation");
+                    VtblEntry::Method(instance)
+                });
+
+                entries.extend(own_entries);
+
+                if emit_vptr {
+                    entries.push(VtblEntry::TraitVPtr(trait_ref));
+                }
+            }
+        }
+
+        ControlFlow::Continue(())
+    };
+
+    let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
+
+    if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) {
+        let sp = tcx.def_span(trait_ref.def_id());
+        dump_vtable_entries(tcx, sp, trait_ref, &entries);
+    }
+
+    tcx.arena.alloc_from_iter(entries)
+}
+
+/// Find slot base for trait methods within vtable entries of another trait
+pub(super) fn vtable_trait_first_method_offset<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    key: (
+        ty::PolyTraitRef<'tcx>, // trait_to_be_found
+        ty::PolyTraitRef<'tcx>, // trait_owning_vtable
+    ),
+) -> usize {
+    let (trait_to_be_found, trait_owning_vtable) = key;
+
+    // #90177
+    let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found);
+
+    let vtable_segment_callback = {
+        let mut vtable_base = 0;
+
+        move |segment| {
+            match segment {
+                VtblSegment::MetadataDSA => {
+                    vtable_base += TyCtxt::COMMON_VTABLE_ENTRIES.len();
+                }
+                VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+                    if tcx.erase_regions(trait_ref) == trait_to_be_found_erased {
+                        return ControlFlow::Break(vtable_base);
+                    }
+                    vtable_base += count_own_vtable_entries(tcx, trait_ref);
+                    if emit_vptr {
+                        vtable_base += 1;
+                    }
+                }
+            }
+            ControlFlow::Continue(())
+        }
+    };
+
+    if let Some(vtable_base) =
+        prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback)
+    {
+        vtable_base
+    } else {
+        bug!("Failed to find info for expected trait in vtable");
+    }
+}
+
+/// Find slot offset for trait vptr within vtable entries of another trait
+pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    key: (
+        Ty<'tcx>, // trait object type whose trait owning vtable
+        Ty<'tcx>, // trait object for supertrait
+    ),
+) -> Option<usize> {
+    let (source, target) = key;
+    assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.has_infer());
+    assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.has_infer());
+
+    // this has been typecked-before, so diagnostics is not really needed.
+    let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);
+
+    let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]);
+
+    match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
+        Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => {
+            *vtable_vptr_slot
+        }
+        otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"),
+    }
+}
+
+/// Given a trait `trait_ref`, returns the number of vtable entries
+/// that come from `trait_ref`, excluding its supertraits. Used in
+/// computing the vtable base for an upcast trait of a trait object.
+pub(crate) fn count_own_vtable_entries<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+) -> usize {
+    tcx.own_existential_vtable_entries(trait_ref.def_id()).len()
+}
+
+pub(super) fn provide(providers: &mut Providers) {
+    *providers = Providers {
+        own_existential_vtable_entries,
+        vtable_entries,
+        vtable_trait_upcasting_coercion_new_vptr_slot,
+        ..*providers
+    };
+}
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
new file mode 100644
index 00000000000..0f8d9c6bf4b
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -0,0 +1,977 @@
+use crate::infer::InferCtxt;
+use crate::traits;
+use rustc_hir as hir;
+use rustc_hir::lang_items::LangItem;
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
+use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
+use rustc_span::{Span, DUMMY_SP};
+
+use std::iter;
+/// Returns the set of obligations needed to make `arg` well-formed.
+/// If `arg` contains unresolved inference variables, this may include
+/// further WF obligations. However, if `arg` IS an unresolved
+/// inference variable, returns `None`, because we are not able to
+/// make any progress at all. This is to prevent "livelock" where we
+/// say "$0 is WF if $0 is WF".
+pub fn obligations<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    body_id: LocalDefId,
+    recursion_depth: usize,
+    arg: GenericArg<'tcx>,
+    span: Span,
+) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
+    // Handle the "livelock" case (see comment above) by bailing out if necessary.
+    let arg = match arg.unpack() {
+        GenericArgKind::Type(ty) => {
+            match ty.kind() {
+                ty::Infer(ty::TyVar(_)) => {
+                    let resolved_ty = infcx.shallow_resolve(ty);
+                    if resolved_ty == ty {
+                        // No progress, bail out to prevent "livelock".
+                        return None;
+                    } else {
+                        resolved_ty
+                    }
+                }
+                _ => ty,
+            }
+            .into()
+        }
+        GenericArgKind::Const(ct) => {
+            match ct.kind() {
+                ty::ConstKind::Infer(_) => {
+                    let resolved = infcx.shallow_resolve(ct);
+                    if resolved == ct {
+                        // No progress.
+                        return None;
+                    } else {
+                        resolved
+                    }
+                }
+                _ => ct,
+            }
+            .into()
+        }
+        // There is nothing we have to do for lifetimes.
+        GenericArgKind::Lifetime(..) => return Some(Vec::new()),
+    };
+
+    let mut wf =
+        WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None };
+    wf.compute(arg);
+    debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out);
+
+    let result = wf.normalize(infcx);
+    debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result);
+    Some(result)
+}
+
+/// Compute the predicates that are required for a type to be well-formed.
+///
+/// This is only intended to be used in the new solver, since it does not
+/// take into account recursion depth or proper error-reporting spans.
+pub fn unnormalized_obligations<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    arg: GenericArg<'tcx>,
+) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
+    debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
+
+    // However, if `arg` IS an unresolved inference variable, returns `None`,
+    // because we are not able to make any progress at all. This is to prevent
+    // "livelock" where we say "$0 is WF if $0 is WF".
+    if arg.is_non_region_infer() {
+        return None;
+    }
+
+    if let ty::GenericArgKind::Lifetime(..) = arg.unpack() {
+        return Some(vec![]);
+    }
+
+    let mut wf = WfPredicates {
+        infcx,
+        param_env,
+        body_id: CRATE_DEF_ID,
+        span: DUMMY_SP,
+        out: vec![],
+        recursion_depth: 0,
+        item: None,
+    };
+    wf.compute(arg);
+    Some(wf.out)
+}
+
+/// Returns the obligations that make this trait reference
+/// well-formed. For example, if there is a trait `Set` defined like
+/// `trait Set<K: Eq>`, then the trait bound `Foo: Set<Bar>` is WF
+/// if `Bar: Eq`.
+pub fn trait_obligations<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    body_id: LocalDefId,
+    trait_pred: ty::TraitPredicate<'tcx>,
+    span: Span,
+    item: &'tcx hir::Item<'tcx>,
+) -> Vec<traits::PredicateObligation<'tcx>> {
+    let mut wf = WfPredicates {
+        infcx,
+        param_env,
+        body_id,
+        span,
+        out: vec![],
+        recursion_depth: 0,
+        item: Some(item),
+    };
+    wf.compute_trait_pred(trait_pred, Elaborate::All);
+    debug!(obligations = ?wf.out);
+    wf.normalize(infcx)
+}
+
+/// Returns the requirements for `clause` to be well-formed.
+///
+/// For example, if there is a trait `Set` defined like
+/// `trait Set<K: Eq>`, then the trait bound `Foo: Set<Bar>` is WF
+/// if `Bar: Eq`.
+#[instrument(skip(infcx), ret)]
+pub fn clause_obligations<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    body_id: LocalDefId,
+    clause: ty::Clause<'tcx>,
+    span: Span,
+) -> Vec<traits::PredicateObligation<'tcx>> {
+    let mut wf = WfPredicates {
+        infcx,
+        param_env,
+        body_id,
+        span,
+        out: vec![],
+        recursion_depth: 0,
+        item: None,
+    };
+
+    // It's ok to skip the binder here because wf code is prepared for it
+    match clause.kind().skip_binder() {
+        ty::ClauseKind::Trait(t) => {
+            wf.compute_trait_pred(t, Elaborate::None);
+        }
+        ty::ClauseKind::RegionOutlives(..) => {}
+        ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => {
+            wf.compute(ty.into());
+        }
+        ty::ClauseKind::Projection(t) => {
+            wf.compute_projection(t.projection_ty);
+            wf.compute(match t.term.unpack() {
+                ty::TermKind::Ty(ty) => ty.into(),
+                ty::TermKind::Const(c) => c.into(),
+            })
+        }
+        ty::ClauseKind::ConstArgHasType(ct, ty) => {
+            wf.compute(ct.into());
+            wf.compute(ty.into());
+        }
+        ty::ClauseKind::WellFormed(arg) => {
+            wf.compute(arg);
+        }
+
+        ty::ClauseKind::ConstEvaluatable(ct) => {
+            wf.compute(ct.into());
+        }
+    }
+
+    wf.normalize(infcx)
+}
+
+struct WfPredicates<'a, 'tcx> {
+    infcx: &'a InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    body_id: LocalDefId,
+    span: Span,
+    out: Vec<traits::PredicateObligation<'tcx>>,
+    recursion_depth: usize,
+    item: Option<&'tcx hir::Item<'tcx>>,
+}
+
+/// Controls whether we "elaborate" supertraits and so forth on the WF
+/// predicates. This is a kind of hack to address #43784. The
+/// underlying problem in that issue was a trait structure like:
+///
+/// ```ignore (illustrative)
+/// trait Foo: Copy { }
+/// trait Bar: Foo { }
+/// impl<T: Bar> Foo for T { }
+/// impl<T> Bar for T { }
+/// ```
+///
+/// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but
+/// we decide that this is true because `T: Bar` is in the
+/// where-clauses (and we can elaborate that to include `T:
+/// Copy`). This wouldn't be a problem, except that when we check the
+/// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo`
+/// impl. And so nowhere did we check that `T: Copy` holds!
+///
+/// To resolve this, we elaborate the WF requirements that must be
+/// proven when checking impls. This means that (e.g.) the `impl Bar
+/// for T` will be forced to prove not only that `T: Foo` but also `T:
+/// Copy` (which it won't be able to do, because there is no `Copy`
+/// impl for `T`).
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+enum Elaborate {
+    All,
+    None,
+}
+
+fn extend_cause_with_original_assoc_item_obligation<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
+    item: Option<&hir::Item<'tcx>>,
+    cause: &mut traits::ObligationCause<'tcx>,
+    pred: ty::Predicate<'tcx>,
+) {
+    debug!(
+        "extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}",
+        trait_ref, item, cause, pred
+    );
+    let (items, impl_def_id) = match item {
+        Some(hir::Item { kind: hir::ItemKind::Impl(impl_), owner_id, .. }) => {
+            (impl_.items, *owner_id)
+        }
+        _ => return,
+    };
+    let fix_span =
+        |impl_item_ref: &hir::ImplItemRef| match tcx.hir().impl_item(impl_item_ref.id).kind {
+            hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::Type(ty) => ty.span,
+            _ => impl_item_ref.span,
+        };
+
+    // It is fine to skip the binder as we don't care about regions here.
+    match pred.kind().skip_binder() {
+        ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) => {
+            // The obligation comes not from the current `impl` nor the `trait` being implemented,
+            // but rather from a "second order" obligation, where an associated type has a
+            // projection coming from another associated type. See
+            // `tests/ui/associated-types/point-at-type-on-obligation-failure.rs` and
+            // `traits-assoc-type-in-supertrait-bad.rs`.
+            if let Some(ty::Alias(ty::Projection, projection_ty)) =
+                proj.term.ty().map(|ty| ty.kind())
+                && let Some(&impl_item_id) =
+                    tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.def_id)
+                && let Some(impl_item_span) = items
+                    .iter()
+                    .find(|item| item.id.owner_id.to_def_id() == impl_item_id)
+                    .map(fix_span)
+            {
+                cause.span = impl_item_span;
+            }
+        }
+        ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
+            // An associated item obligation born out of the `trait` failed to be met. An example
+            // can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`.
+            debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred);
+            if let ty::Alias(ty::Projection, ty::AliasTy { def_id, .. }) = *pred.self_ty().kind()
+                && let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&def_id)
+                && let Some(impl_item_span) = items
+                    .iter()
+                    .find(|item| item.id.owner_id.to_def_id() == impl_item_id)
+                    .map(fix_span)
+            {
+                cause.span = impl_item_span;
+            }
+        }
+        _ => {}
+    }
+}
+
+impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
+        traits::ObligationCause::new(self.span, self.body_id, code)
+    }
+
+    fn normalize(self, infcx: &InferCtxt<'tcx>) -> Vec<traits::PredicateObligation<'tcx>> {
+        // Do not normalize `wf` obligations with the new solver.
+        //
+        // The current deep normalization routine with the new solver does not
+        // handle ambiguity and the new solver correctly deals with unnnormalized goals.
+        // If the user relies on normalized types, e.g. for `fn implied_outlives_bounds`,
+        // it is their responsibility to normalize while avoiding ambiguity.
+        if infcx.next_trait_solver() {
+            return self.out;
+        }
+
+        let cause = self.cause(traits::WellFormed(None));
+        let param_env = self.param_env;
+        let mut obligations = Vec::with_capacity(self.out.len());
+        for mut obligation in self.out {
+            assert!(!obligation.has_escaping_bound_vars());
+            let mut selcx = traits::SelectionContext::new(infcx);
+            // Don't normalize the whole obligation, the param env is either
+            // already normalized, or we're currently normalizing the
+            // param_env. Either way we should only normalize the predicate.
+            let normalized_predicate = traits::project::normalize_with_depth_to(
+                &mut selcx,
+                param_env,
+                cause.clone(),
+                self.recursion_depth,
+                obligation.predicate,
+                &mut obligations,
+            );
+            obligation.predicate = normalized_predicate;
+            obligations.push(obligation);
+        }
+        obligations
+    }
+
+    /// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
+    fn compute_trait_pred(&mut self, trait_pred: ty::TraitPredicate<'tcx>, elaborate: Elaborate) {
+        let tcx = self.tcx();
+        let trait_ref = trait_pred.trait_ref;
+
+        // Negative trait predicates don't require supertraits to hold, just
+        // that their args are WF.
+        if trait_pred.polarity == ty::ImplPolarity::Negative {
+            self.compute_negative_trait_pred(trait_ref);
+            return;
+        }
+
+        // if the trait predicate is not const, the wf obligations should not be const as well.
+        let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.args);
+
+        debug!("compute_trait_pred obligations {:?}", obligations);
+        let param_env = self.param_env;
+        let depth = self.recursion_depth;
+
+        let item = self.item;
+
+        let extend = |traits::PredicateObligation { predicate, mut cause, .. }| {
+            if let Some(parent_trait_pred) = predicate.to_opt_poly_trait_pred() {
+                cause = cause.derived_cause(
+                    parent_trait_pred,
+                    traits::ObligationCauseCode::DerivedObligation,
+                );
+            }
+            extend_cause_with_original_assoc_item_obligation(
+                tcx, trait_ref, item, &mut cause, predicate,
+            );
+            traits::Obligation::with_depth(tcx, cause, depth, param_env, predicate)
+        };
+
+        if let Elaborate::All = elaborate {
+            let implied_obligations = traits::util::elaborate(tcx, obligations);
+            let implied_obligations = implied_obligations.map(extend);
+            self.out.extend(implied_obligations);
+        } else {
+            self.out.extend(obligations);
+        }
+
+        self.out.extend(
+            trait_ref
+                .args
+                .iter()
+                .enumerate()
+                .filter(|(_, arg)| {
+                    matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
+                })
+                .filter(|(_, arg)| !arg.has_escaping_bound_vars())
+                .map(|(i, arg)| {
+                    let mut cause = traits::ObligationCause::misc(self.span, self.body_id);
+                    // The first subst is the self ty - use the correct span for it.
+                    if i == 0 {
+                        if let Some(hir::ItemKind::Impl(hir::Impl { self_ty, .. })) =
+                            item.map(|i| &i.kind)
+                        {
+                            cause.span = self_ty.span;
+                        }
+                    }
+                    traits::Obligation::with_depth(
+                        tcx,
+                        cause,
+                        depth,
+                        param_env,
+                        ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+                            arg,
+                        ))),
+                    )
+                }),
+        );
+    }
+
+    // Compute the obligations that are required for `trait_ref` to be WF,
+    // given that it is a *negative* trait predicate.
+    fn compute_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) {
+        for arg in trait_ref.args {
+            self.compute(arg);
+        }
+    }
+
+    /// Pushes the obligations required for `trait_ref::Item` to be WF
+    /// into `self.out`.
+    fn compute_projection(&mut self, data: ty::AliasTy<'tcx>) {
+        // A projection is well-formed if
+        //
+        // (a) its predicates hold (*)
+        // (b) its args are wf
+        //
+        // (*) The predicates of an associated type include the predicates of
+        //     the trait that it's contained in. For example, given
+        //
+        // trait A<T>: Clone {
+        //     type X where T: Copy;
+        // }
+        //
+        // The predicates of `<() as A<i32>>::X` are:
+        // [
+        //     `(): Sized`
+        //     `(): Clone`
+        //     `(): A<i32>`
+        //     `i32: Sized`
+        //     `i32: Clone`
+        //     `i32: Copy`
+        // ]
+        let obligations = self.nominal_obligations(data.def_id, data.args);
+        self.out.extend(obligations);
+
+        self.compute_projection_args(data.args);
+    }
+
+    fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) {
+        // An inherent projection is well-formed if
+        //
+        // (a) its predicates hold (*)
+        // (b) its args are wf
+        //
+        // (*) The predicates of an inherent associated type include the
+        //     predicates of the impl that it's contained in.
+
+        if !data.self_ty().has_escaping_bound_vars() {
+            // FIXME(inherent_associated_types): Should this happen inside of a snapshot?
+            // FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm!
+            let args = traits::project::compute_inherent_assoc_ty_args(
+                &mut traits::SelectionContext::new(self.infcx),
+                self.param_env,
+                data,
+                self.cause(traits::WellFormed(None)),
+                self.recursion_depth,
+                &mut self.out,
+            );
+            let obligations = self.nominal_obligations(data.def_id, args);
+            self.out.extend(obligations);
+        }
+
+        self.compute_projection_args(data.args);
+    }
+
+    fn compute_projection_args(&mut self, args: GenericArgsRef<'tcx>) {
+        let tcx = self.tcx();
+        let cause = self.cause(traits::WellFormed(None));
+        let param_env = self.param_env;
+        let depth = self.recursion_depth;
+
+        self.out.extend(
+            args.iter()
+                .filter(|arg| {
+                    matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
+                })
+                .filter(|arg| !arg.has_escaping_bound_vars())
+                .map(|arg| {
+                    traits::Obligation::with_depth(
+                        tcx,
+                        cause.clone(),
+                        depth,
+                        param_env,
+                        ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+                            arg,
+                        ))),
+                    )
+                }),
+        );
+    }
+
+    fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) {
+        if !subty.has_escaping_bound_vars() {
+            let cause = self.cause(cause);
+            let trait_ref =
+                ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [subty]);
+            self.out.push(traits::Obligation::with_depth(
+                self.tcx(),
+                cause,
+                self.recursion_depth,
+                self.param_env,
+                ty::Binder::dummy(trait_ref),
+            ));
+        }
+    }
+
+    /// Pushes all the predicates needed to validate that `ty` is WF into `out`.
+    #[instrument(level = "debug", skip(self))]
+    fn compute(&mut self, arg: GenericArg<'tcx>) {
+        let mut walker = arg.walk();
+        let param_env = self.param_env;
+        let depth = self.recursion_depth;
+        while let Some(arg) = walker.next() {
+            debug!(?arg, ?self.out);
+            let ty = match arg.unpack() {
+                GenericArgKind::Type(ty) => ty,
+
+                // No WF constraints for lifetimes being present, any outlives
+                // obligations are handled by the parent (e.g. `ty::Ref`).
+                GenericArgKind::Lifetime(_) => continue,
+
+                GenericArgKind::Const(ct) => {
+                    match ct.kind() {
+                        ty::ConstKind::Unevaluated(uv) => {
+                            if !ct.has_escaping_bound_vars() {
+                                let obligations = self.nominal_obligations(uv.def, uv.args);
+                                self.out.extend(obligations);
+
+                                let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
+                                    ty::ClauseKind::ConstEvaluatable(ct),
+                                ));
+                                let cause = self.cause(traits::WellFormed(None));
+                                self.out.push(traits::Obligation::with_depth(
+                                    self.tcx(),
+                                    cause,
+                                    self.recursion_depth,
+                                    self.param_env,
+                                    predicate,
+                                ));
+                            }
+                        }
+                        ty::ConstKind::Infer(_) => {
+                            let cause = self.cause(traits::WellFormed(None));
+
+                            self.out.push(traits::Obligation::with_depth(
+                                self.tcx(),
+                                cause,
+                                self.recursion_depth,
+                                self.param_env,
+                                ty::Binder::dummy(ty::PredicateKind::Clause(
+                                    ty::ClauseKind::WellFormed(ct.into()),
+                                )),
+                            ));
+                        }
+                        ty::ConstKind::Expr(_) => {
+                            // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
+                            // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
+                            // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
+                            // which means that the `DefId` would have been typeck'd elsewhere. However in
+                            // the future we may allow directly lowering to `ConstKind::Expr` in which case
+                            // we would not be proving bounds we should.
+
+                            let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
+                                ty::ClauseKind::ConstEvaluatable(ct),
+                            ));
+                            let cause = self.cause(traits::WellFormed(None));
+                            self.out.push(traits::Obligation::with_depth(
+                                self.tcx(),
+                                cause,
+                                self.recursion_depth,
+                                self.param_env,
+                                predicate,
+                            ));
+                        }
+
+                        ty::ConstKind::Error(_)
+                        | ty::ConstKind::Param(_)
+                        | ty::ConstKind::Bound(..)
+                        | ty::ConstKind::Placeholder(..) => {
+                            // These variants are trivially WF, so nothing to do here.
+                        }
+                        ty::ConstKind::Value(..) => {
+                            // FIXME: Enforce that values are structurally-matchable.
+                        }
+                    }
+                    continue;
+                }
+            };
+
+            debug!("wf bounds for ty={:?} ty.kind={:#?}", ty, ty.kind());
+
+            match *ty.kind() {
+                ty::Bool
+                | ty::Char
+                | ty::Int(..)
+                | ty::Uint(..)
+                | ty::Float(..)
+                | ty::Error(_)
+                | ty::Str
+                | ty::CoroutineWitness(..)
+                | ty::Never
+                | ty::Param(_)
+                | ty::Bound(..)
+                | ty::Placeholder(..)
+                | ty::Foreign(..) => {
+                    // WfScalar, WfParameter, etc
+                }
+
+                // Can only infer to `ty::Int(_) | ty::Uint(_)`.
+                ty::Infer(ty::IntVar(_)) => {}
+
+                // Can only infer to `ty::Float(_)`.
+                ty::Infer(ty::FloatVar(_)) => {}
+
+                ty::Slice(subty) => {
+                    self.require_sized(subty, traits::SliceOrArrayElem);
+                }
+
+                ty::Array(subty, _) => {
+                    self.require_sized(subty, traits::SliceOrArrayElem);
+                    // Note that we handle the len is implicitly checked while walking `arg`.
+                }
+
+                ty::Tuple(tys) => {
+                    if let Some((_last, rest)) = tys.split_last() {
+                        for &elem in rest {
+                            self.require_sized(elem, traits::TupleElem);
+                        }
+                    }
+                }
+
+                ty::RawPtr(_) => {
+                    // Simple cases that are WF if their type args are WF.
+                }
+
+                ty::Alias(ty::Projection, data) => {
+                    walker.skip_current_subtree(); // Subtree handled by compute_projection.
+                    self.compute_projection(data);
+                }
+                ty::Alias(ty::Inherent, data) => {
+                    walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection.
+                    self.compute_inherent_projection(data);
+                }
+
+                ty::Adt(def, args) => {
+                    // WfNominalType
+                    let obligations = self.nominal_obligations(def.did(), args);
+                    self.out.extend(obligations);
+                }
+
+                ty::FnDef(did, args) => {
+                    let obligations = self.nominal_obligations(did, args);
+                    self.out.extend(obligations);
+                }
+
+                ty::Ref(r, rty, _) => {
+                    // WfReference
+                    if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
+                        let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
+                        self.out.push(traits::Obligation::with_depth(
+                            self.tcx(),
+                            cause,
+                            depth,
+                            param_env,
+                            ty::Binder::dummy(ty::PredicateKind::Clause(
+                                ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)),
+                            )),
+                        ));
+                    }
+                }
+
+                ty::Coroutine(did, args, ..) => {
+                    // Walk ALL the types in the coroutine: this will
+                    // include the upvar types as well as the yield
+                    // type. Note that this is mildly distinct from
+                    // the closure case, where we have to be careful
+                    // about the signature of the closure. We don't
+                    // have the problem of implied bounds here since
+                    // coroutines don't take arguments.
+                    let obligations = self.nominal_obligations(did, args);
+                    self.out.extend(obligations);
+                }
+
+                ty::Closure(did, args) => {
+                    // Only check the upvar types for WF, not the rest
+                    // of the types within. This is needed because we
+                    // capture the signature and it may not be WF
+                    // without the implied bounds. Consider a closure
+                    // like `|x: &'a T|` -- it may be that `T: 'a` is
+                    // not known to hold in the creator's context (and
+                    // indeed the closure may not be invoked by its
+                    // creator, but rather turned to someone who *can*
+                    // verify that).
+                    //
+                    // The special treatment of closures here really
+                    // ought not to be necessary either; the problem
+                    // is related to #25860 -- there is no way for us
+                    // to express a fn type complete with the implied
+                    // bounds that it is assuming. I think in reality
+                    // the WF rules around fn are a bit messed up, and
+                    // that is the rot problem: `fn(&'a T)` should
+                    // probably always be WF, because it should be
+                    // shorthand for something like `where(T: 'a) {
+                    // fn(&'a T) }`, as discussed in #25860.
+                    walker.skip_current_subtree(); // subtree handled below
+                    // FIXME(eddyb) add the type to `walker` instead of recursing.
+                    self.compute(args.as_closure().tupled_upvars_ty().into());
+                    // Note that we cannot skip the generic types
+                    // types. Normally, within the fn
+                    // body where they are created, the generics will
+                    // always be WF, and outside of that fn body we
+                    // are not directly inspecting closure types
+                    // anyway, except via auto trait matching (which
+                    // only inspects the upvar types).
+                    // But when a closure is part of a type-alias-impl-trait
+                    // then the function that created the defining site may
+                    // have had more bounds available than the type alias
+                    // specifies. This may cause us to have a closure in the
+                    // hidden type that is not actually well formed and
+                    // can cause compiler crashes when the user abuses unsafe
+                    // code to procure such a closure.
+                    // See tests/ui/type-alias-impl-trait/wf_check_closures.rs
+                    let obligations = self.nominal_obligations(did, args);
+                    self.out.extend(obligations);
+                }
+
+                ty::FnPtr(_) => {
+                    // let the loop iterate into the argument/return
+                    // types appearing in the fn signature
+                }
+
+                ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
+                    // All of the requirements on type parameters
+                    // have already been checked for `impl Trait` in
+                    // return position. We do need to check type-alias-impl-trait though.
+                    if self.tcx().is_type_alias_impl_trait(def_id) {
+                        let obligations = self.nominal_obligations(def_id, args);
+                        self.out.extend(obligations);
+                    }
+                }
+
+                ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => {
+                    let obligations = self.nominal_obligations(def_id, args);
+                    self.out.extend(obligations);
+                }
+
+                ty::Dynamic(data, r, _) => {
+                    // WfObject
+                    //
+                    // Here, we defer WF checking due to higher-ranked
+                    // regions. This is perhaps not ideal.
+                    self.from_object_ty(ty, data, r);
+
+                    // FIXME(#27579) RFC also considers adding trait
+                    // obligations that don't refer to Self and
+                    // checking those
+
+                    let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
+
+                    if !defer_to_coercion {
+                        if let Some(principal) = data.principal_def_id() {
+                            self.out.push(traits::Obligation::with_depth(
+                                self.tcx(),
+                                self.cause(traits::WellFormed(None)),
+                                depth,
+                                param_env,
+                                ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)),
+                            ));
+                        }
+                    }
+                }
+
+                // Inference variables are the complicated case, since we don't
+                // know what type they are. We do two things:
+                //
+                // 1. Check if they have been resolved, and if so proceed with
+                //    THAT type.
+                // 2. If not, we've at least simplified things (e.g., we went
+                //    from `Vec<$0>: WF` to `$0: WF`), so we can
+                //    register a pending obligation and keep
+                //    moving. (Goal is that an "inductive hypothesis"
+                //    is satisfied to ensure termination.)
+                // See also the comment on `fn obligations`, describing "livelock"
+                // prevention, which happens before this can be reached.
+                ty::Infer(_) => {
+                    let cause = self.cause(traits::WellFormed(None));
+                    self.out.push(traits::Obligation::with_depth(
+                        self.tcx(),
+                        cause,
+                        self.recursion_depth,
+                        param_env,
+                        ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+                            ty.into(),
+                        ))),
+                    ));
+                }
+            }
+
+            debug!(?self.out);
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn nominal_obligations(
+        &mut self,
+        def_id: DefId,
+        args: GenericArgsRef<'tcx>,
+    ) -> Vec<traits::PredicateObligation<'tcx>> {
+        let predicates = self.tcx().predicates_of(def_id);
+        let mut origins = vec![def_id; predicates.predicates.len()];
+        let mut head = predicates;
+        while let Some(parent) = head.parent {
+            head = self.tcx().predicates_of(parent);
+            origins.extend(iter::repeat(parent).take(head.predicates.len()));
+        }
+
+        let predicates = predicates.instantiate(self.tcx(), args);
+        trace!("{:#?}", predicates);
+        debug_assert_eq!(predicates.predicates.len(), origins.len());
+
+        iter::zip(predicates, origins.into_iter().rev())
+            .map(|((pred, span), origin_def_id)| {
+                let code = if span.is_dummy() {
+                    traits::ItemObligation(origin_def_id)
+                } else {
+                    traits::BindingObligation(origin_def_id, span)
+                };
+                let cause = self.cause(code);
+                traits::Obligation::with_depth(
+                    self.tcx(),
+                    cause,
+                    self.recursion_depth,
+                    self.param_env,
+                    pred,
+                )
+            })
+            .filter(|pred| !pred.has_escaping_bound_vars())
+            .collect()
+    }
+
+    fn from_object_ty(
+        &mut self,
+        ty: Ty<'tcx>,
+        data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        region: ty::Region<'tcx>,
+    ) {
+        // Imagine a type like this:
+        //
+        //     trait Foo { }
+        //     trait Bar<'c> : 'c { }
+        //
+        //     &'b (Foo+'c+Bar<'d>)
+        //         ^
+        //
+        // In this case, the following relationships must hold:
+        //
+        //     'b <= 'c
+        //     'd <= 'c
+        //
+        // The first conditions is due to the normal region pointer
+        // rules, which say that a reference cannot outlive its
+        // referent.
+        //
+        // The final condition may be a bit surprising. In particular,
+        // you may expect that it would have been `'c <= 'd`, since
+        // usually lifetimes of outer things are conservative
+        // approximations for inner things. However, it works somewhat
+        // differently with trait objects: here the idea is that if the
+        // user specifies a region bound (`'c`, in this case) it is the
+        // "master bound" that *implies* that bounds from other traits are
+        // all met. (Remember that *all bounds* in a type like
+        // `Foo+Bar+Zed` must be met, not just one, hence if we write
+        // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
+        // 'y.)
+        //
+        // Note: in fact we only permit builtin traits, not `Bar<'d>`, I
+        // am looking forward to the future here.
+        if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
+            let implicit_bounds = object_region_bounds(self.tcx(), data);
+
+            let explicit_bound = region;
+
+            self.out.reserve(implicit_bounds.len());
+            for implicit_bound in implicit_bounds {
+                let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound));
+                let outlives =
+                    ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound));
+                self.out.push(traits::Obligation::with_depth(
+                    self.tcx(),
+                    cause,
+                    self.recursion_depth,
+                    self.param_env,
+                    outlives,
+                ));
+            }
+        }
+    }
+}
+
+/// Given an object type like `SomeTrait + Send`, computes the lifetime
+/// bounds that must hold on the elided self type. These are derived
+/// from the declarations of `SomeTrait`, `Send`, and friends -- if
+/// they declare `trait SomeTrait : 'static`, for example, then
+/// `'static` would appear in the list. The hard work is done by
+/// `infer::required_region_bounds`, see that for more information.
+pub fn object_region_bounds<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+) -> Vec<ty::Region<'tcx>> {
+    let predicates = existential_predicates.iter().filter_map(|predicate| {
+        if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() {
+            None
+        } else {
+            Some(predicate.with_self_ty(tcx, tcx.types.trait_object_dummy_self))
+        }
+    });
+
+    required_region_bounds(tcx, tcx.types.trait_object_dummy_self, predicates)
+}
+
+/// Given a set of predicates that apply to an object type, returns
+/// the region bounds that the (erased) `Self` type must
+/// outlive. Precisely *because* the `Self` type is erased, the
+/// parameter `erased_self_ty` must be supplied to indicate what type
+/// has been used to represent `Self` in the predicates
+/// themselves. This should really be a unique type; `FreshTy(0)` is a
+/// popular choice.
+///
+/// N.B., in some cases, particularly around higher-ranked bounds,
+/// this function returns a kind of conservative approximation.
+/// That is, all regions returned by this function are definitely
+/// required, but there may be other region bounds that are not
+/// returned, as well as requirements like `for<'a> T: 'a`.
+///
+/// Requires that trait definitions have been processed so that we can
+/// elaborate predicates and walk supertraits.
+#[instrument(skip(tcx, predicates), level = "debug", ret)]
+pub(crate) fn required_region_bounds<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    erased_self_ty: Ty<'tcx>,
+    predicates: impl Iterator<Item = ty::Clause<'tcx>>,
+) -> Vec<ty::Region<'tcx>> {
+    assert!(!erased_self_ty.has_escaping_bound_vars());
+
+    traits::elaborate(tcx, predicates)
+        .filter_map(|pred| {
+            debug!(?pred);
+            match pred.kind().skip_binder() {
+                ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => {
+                    // Search for a bound of the form `erased_self_ty
+                    // : 'a`, but be wary of something like `for<'a>
+                    // erased_self_ty : 'a` (we interpret a
+                    // higher-ranked bound like that as 'static,
+                    // though at present the code in `fulfill.rs`
+                    // considers such bounds to be unsatisfiable, so
+                    // it's kind of a moot point since you could never
+                    // construct such an object, but this seems
+                    // correct even if that code changes).
+                    if t == &erased_self_ty && !r.has_escaping_bound_vars() {
+                        Some(*r)
+                    } else {
+                        None
+                    }
+                }
+                ty::ClauseKind::Trait(_)
+                | ty::ClauseKind::RegionOutlives(_)
+                | ty::ClauseKind::Projection(_)
+                | ty::ClauseKind::ConstArgHasType(_, _)
+                | ty::ClauseKind::WellFormed(_)
+                | ty::ClauseKind::ConstEvaluatable(_) => None,
+            }
+        })
+        .collect()
+}