about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-06-17 17:59:08 -0400
committerMichael Goulet <michael@errs.io>2024-06-18 10:55:34 -0400
commit532149eb88e6d1d69d883727a216c22839cdf6cc (patch)
tree3aca210ef22ddec18a40a0b040e40efdf1582233
parentbaf94bddf0503bb97376534d10883dbf678bfc6a (diff)
downloadrust-532149eb88e6d1d69d883727a216c22839cdf6cc.tar.gz
rust-532149eb88e6d1d69d883727a216c22839cdf6cc.zip
Uplift the new trait solver
-rw-r--r--Cargo.lock9
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs10
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs6
-rw-r--r--compiler/rustc_middle/src/ty/context.rs363
-rw-r--r--compiler/rustc_middle/src/ty/generic_args.rs14
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs4
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs10
-rw-r--r--compiler/rustc_middle/src/ty/predicate.rs42
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs16
-rw-r--r--compiler/rustc_middle/src/ty/util.rs36
-rw-r--r--compiler/rustc_next_trait_solver/Cargo.toml18
-rw-r--r--compiler/rustc_next_trait_solver/src/infcx.rs116
-rw-r--r--compiler/rustc_next_trait_solver/src/lib.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/alias_relate.rs (renamed from compiler/rustc_trait_selection/src/solve/alias_relate.rs)19
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/assembly/mod.rs)514
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs (renamed from compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs)12
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs (renamed from compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs)234
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs)359
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs (renamed from compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs)13
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/inspect/build.rs (renamed from compiler/rustc_trait_selection/src/solve/inspect/build.rs)38
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs4
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/mod.rs)128
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs)18
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs)26
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs)523
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs)68
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs)23
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/project_goals.rs (renamed from compiler/rustc_trait_selection/src/solve/project_goals.rs)18
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/search_graph.rs (renamed from compiler/rustc_trait_selection/src/solve/search_graph.rs)19
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs (renamed from compiler/rustc_trait_selection/src/solve/trait_goals.rs)423
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/mod.rs7
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs182
-rw-r--r--compiler/rustc_type_ir/src/interner.rs124
-rw-r--r--compiler/rustc_type_ir/src/lang_items.rs32
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs8
36 files changed, 2000 insertions, 1443 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2e10b4c49ab..14ee031ad04 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4520,7 +4520,16 @@ dependencies = [
 name = "rustc_next_trait_solver"
 version = "0.0.0"
 dependencies = [
+ "bitflags 2.5.0",
+ "derivative",
+ "rustc_ast_ir",
+ "rustc_data_structures",
+ "rustc_index",
+ "rustc_macros",
+ "rustc_serialize",
  "rustc_type_ir",
+ "rustc_type_ir_macros",
+ "tracing",
 ]
 
 [[package]]
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 684b3233cfd..8e221cdc603 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -205,6 +205,14 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
         self.did()
     }
 
+    fn is_struct(self) -> bool {
+        self.is_struct()
+    }
+
+    fn struct_tail_ty(self, interner: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
+        Some(interner.type_of(self.non_enum_variant().tail_opt()?.did))
+    }
+
     fn is_phantom_data(self) -> bool {
         self.is_phantom_data()
     }
@@ -212,7 +220,7 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
     fn all_field_tys(
         self,
         tcx: TyCtxt<'tcx>,
-    ) -> ty::EarlyBinder<'tcx, impl Iterator<Item = Ty<'tcx>>> {
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = Ty<'tcx>>> {
         ty::EarlyBinder::bind(
             self.all_fields().map(move |field| tcx.type_of(field.did).skip_binder()),
         )
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 12f0c38b054..32d01d07c17 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -16,8 +16,8 @@ mod valtree;
 
 pub use int::*;
 pub use kind::*;
-use rustc_span::Span;
 use rustc_span::DUMMY_SP;
+use rustc_span::{ErrorGuaranteed, Span};
 pub use valtree::*;
 
 pub type ConstKind<'tcx> = ir::ConstKind<TyCtxt<'tcx>>;
@@ -176,6 +176,10 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
     fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self {
         Const::new_expr(interner, expr)
     }
+
+    fn new_error(interner: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
+        Const::new_error(interner, guar)
+    }
 }
 
 impl<'tcx> Const<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 828cd761c19..2663dca7460 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -90,46 +90,65 @@ use std::ops::{Bound, Deref};
 impl<'tcx> Interner for TyCtxt<'tcx> {
     type DefId = DefId;
     type LocalDefId = LocalDefId;
-    type AdtDef = ty::AdtDef<'tcx>;
-
     type GenericArgs = ty::GenericArgsRef<'tcx>;
+
     type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>];
     type GenericArg = ty::GenericArg<'tcx>;
     type Term = ty::Term<'tcx>;
-
     type BoundVarKinds = &'tcx List<ty::BoundVariableKind>;
-    type BoundVarKind = ty::BoundVariableKind;
 
-    type CanonicalVars = CanonicalVarInfos<'tcx>;
+    type BoundVarKind = ty::BoundVariableKind;
     type PredefinedOpaques = solve::PredefinedOpaques<'tcx>;
+
+    fn mk_predefined_opaques_in_body(
+        self,
+        data: PredefinedOpaquesData<Self>,
+    ) -> Self::PredefinedOpaques {
+        self.mk_predefined_opaques_in_body(data)
+    }
     type DefiningOpaqueTypes = &'tcx ty::List<LocalDefId>;
-    type ExternalConstraints = ExternalConstraints<'tcx>;
     type CanonicalGoalEvaluationStepRef =
         &'tcx solve::inspect::CanonicalGoalEvaluationStep<TyCtxt<'tcx>>;
+    type CanonicalVars = CanonicalVarInfos<'tcx>;
+    fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
+        self.mk_canonical_var_infos(infos)
+    }
 
+    type ExternalConstraints = ExternalConstraints<'tcx>;
+    fn mk_external_constraints(
+        self,
+        data: ExternalConstraintsData<Self>,
+    ) -> ExternalConstraints<'tcx> {
+        self.mk_external_constraints(data)
+    }
+    type DepNodeIndex = DepNodeIndex;
+    fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) {
+        self.dep_graph.with_anon_task(self, crate::dep_graph::dep_kinds::TraitSelect, task)
+    }
     type Ty = Ty<'tcx>;
     type Tys = &'tcx List<Ty<'tcx>>;
+
     type FnInputTys = &'tcx [Ty<'tcx>];
     type ParamTy = ParamTy;
     type BoundTy = ty::BoundTy;
-    type PlaceholderTy = ty::PlaceholderType;
 
+    type PlaceholderTy = ty::PlaceholderType;
     type ErrorGuaranteed = ErrorGuaranteed;
     type BoundExistentialPredicates = &'tcx List<PolyExistentialPredicate<'tcx>>;
-    type AllocId = crate::mir::interpret::AllocId;
 
+    type AllocId = crate::mir::interpret::AllocId;
     type Pat = Pattern<'tcx>;
     type Safety = hir::Safety;
     type Abi = abi::Abi;
-
     type Const = ty::Const<'tcx>;
     type PlaceholderConst = ty::PlaceholderConst;
+
     type ParamConst = ty::ParamConst;
     type BoundConst = ty::BoundVar;
     type ValueConst = ty::ValTree<'tcx>;
     type ExprConst = ty::Expr<'tcx>;
-
     type Region = Region<'tcx>;
+
     type EarlyParamRegion = ty::EarlyParamRegion;
     type LateParamRegion = ty::LateParamRegion;
     type BoundRegion = ty::BoundRegion;
@@ -137,15 +156,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
 
     type ParamEnv = ty::ParamEnv<'tcx>;
     type Predicate = Predicate<'tcx>;
+
     type Clause = Clause<'tcx>;
     type Clauses = ty::Clauses<'tcx>;
 
-    type DepNodeIndex = DepNodeIndex;
-    fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) {
-        self.dep_graph.with_anon_task(self, crate::dep_graph::dep_kinds::TraitSelect, task)
-    }
-
     type EvaluationCache = &'tcx solve::EvaluationCache<'tcx>;
+
     fn evaluation_cache(self, mode: SolverMode) -> &'tcx solve::EvaluationCache<'tcx> {
         match mode {
             SolverMode::Normal => &self.new_solver_evaluation_cache,
@@ -157,17 +173,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.expand_abstract_consts(t)
     }
 
-    fn mk_external_constraints(
-        self,
-        data: ExternalConstraintsData<Self>,
-    ) -> ExternalConstraints<'tcx> {
-        self.mk_external_constraints(data)
-    }
-
-    fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
-        self.mk_canonical_var_infos(infos)
-    }
-
     type GenericsOf = &'tcx ty::Generics;
 
     fn generics_of(self, def_id: DefId) -> &'tcx ty::Generics {
@@ -184,6 +189,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.type_of(def_id)
     }
 
+    type AdtDef = ty::AdtDef<'tcx>;
+    fn adt_def(self, adt_def_id: DefId) -> Self::AdtDef {
+        self.adt_def(adt_def_id)
+    }
+
     fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind {
         match self.def_kind(alias.def_id) {
             DefKind::AssocTy => {
@@ -221,8 +231,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     fn trait_ref_and_own_args_for_alias(
         self,
         def_id: DefId,
-        args: Self::GenericArgs,
-    ) -> (rustc_type_ir::TraitRef<Self>, Self::GenericArgsSlice) {
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) {
         assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst);
         let trait_def_id = self.parent(def_id);
         assert_matches!(self.def_kind(trait_def_id), DefKind::Trait);
@@ -233,18 +243,22 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         )
     }
 
-    fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs {
+    fn mk_args(self, args: &[Self::GenericArg]) -> ty::GenericArgsRef<'tcx> {
         self.mk_args(args)
     }
 
     fn mk_args_from_iter<I, T>(self, args: I) -> T::Output
     where
         I: Iterator<Item = T>,
-        T: CollectAndApply<Self::GenericArg, Self::GenericArgs>,
+        T: CollectAndApply<Self::GenericArg, ty::GenericArgsRef<'tcx>>,
     {
         self.mk_args_from_iter(args)
     }
 
+    fn check_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> bool {
+        self.check_args_compatible(def_id, args)
+    }
+
     fn check_and_mk_args(
         self,
         def_id: DefId,
@@ -263,7 +277,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
     where
         I: Iterator<Item = T>,
-        T: CollectAndApply<Self::Ty, Self::Tys>,
+        T: CollectAndApply<Ty<'tcx>, &'tcx List<Ty<'tcx>>>,
     {
         self.mk_type_list_from_iter(args)
     }
@@ -312,6 +326,24 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.item_bounds(def_id).map_bound(IntoIterator::into_iter)
     }
 
+    fn predicates_of(
+        self,
+        def_id: DefId,
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> {
+        ty::EarlyBinder::bind(
+            self.predicates_of(def_id).instantiate_identity(self).predicates.into_iter(),
+        )
+    }
+
+    fn own_predicates_of(
+        self,
+        def_id: DefId,
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> {
+        ty::EarlyBinder::bind(
+            self.predicates_of(def_id).instantiate_own_identity().map(|(clause, _)| clause),
+        )
+    }
+
     fn super_predicates_of(
         self,
         def_id: DefId,
@@ -326,15 +358,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     }
 
     fn require_lang_item(self, lang_item: TraitSolverLangItem) -> DefId {
-        self.require_lang_item(
-            match lang_item {
-                TraitSolverLangItem::Future => hir::LangItem::Future,
-                TraitSolverLangItem::FutureOutput => hir::LangItem::FutureOutput,
-                TraitSolverLangItem::AsyncFnKindHelper => hir::LangItem::AsyncFnKindHelper,
-                TraitSolverLangItem::AsyncFnKindUpvars => hir::LangItem::AsyncFnKindUpvars,
-            },
-            None,
-        )
+        self.require_lang_item(trait_lang_item_to_lang_item(lang_item), None)
+    }
+
+    fn is_lang_item(self, def_id: DefId, lang_item: TraitSolverLangItem) -> bool {
+        self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item))
     }
 
     fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator<Item = DefId> {
@@ -343,6 +371,257 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
             .filter(|assoc_item| matches!(assoc_item.kind, ty::AssocKind::Type))
             .map(|assoc_item| assoc_item.def_id)
     }
+
+    fn args_may_unify_deep(
+        self,
+        obligation_args: ty::GenericArgsRef<'tcx>,
+        impl_args: ty::GenericArgsRef<'tcx>,
+    ) -> bool {
+        ty::fast_reject::DeepRejectCtxt {
+            treat_obligation_params: ty::fast_reject::TreatParams::ForLookup,
+        }
+        .args_may_unify(obligation_args, impl_args)
+    }
+
+    // This implementation is a bit different from `TyCtxt::for_each_relevant_impl`,
+    // since we want to skip over blanket impls for non-rigid aliases, and also we
+    // only want to consider types that *actually* unify with float/int vars.
+    fn for_each_relevant_impl(
+        self,
+        trait_def_id: DefId,
+        self_ty: Ty<'tcx>,
+        mut f: impl FnMut(DefId),
+    ) {
+        let tcx = self;
+        let trait_impls = tcx.trait_impls_of(trait_def_id);
+        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 {
+                    f(impl_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::Pat(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_, _)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Closure(..)
+            | ty::CoroutineClosure(..)
+            | ty::Coroutine(_, _)
+            | ty::Never
+            | ty::Tuple(_) => {
+                let simp = ty::fast_reject::simplify_type(
+                    tcx,
+                    self_ty,
+                    ty::fast_reject::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
+                    ty::SimplifiedType::Int(I8),
+                    ty::SimplifiedType::Int(I16),
+                    ty::SimplifiedType::Int(I32),
+                    ty::SimplifiedType::Int(I64),
+                    ty::SimplifiedType::Int(I128),
+                    ty::SimplifiedType::Int(Isize),
+                    // unsigned integers
+                    ty::SimplifiedType::Uint(U8),
+                    ty::SimplifiedType::Uint(U16),
+                    ty::SimplifiedType::Uint(U32),
+                    ty::SimplifiedType::Uint(U64),
+                    ty::SimplifiedType::Uint(U128),
+                    ty::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::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128);
+                let possible_floats = [
+                    ty::SimplifiedType::Float(ty::FloatTy::F16),
+                    ty::SimplifiedType::Float(ty::FloatTy::F32),
+                    ty::SimplifiedType::Float(ty::FloatTy::F64),
+                    ty::SimplifiedType::Float(ty::FloatTy::F128),
+                ];
+
+                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}"),
+        }
+
+        let trait_impls = tcx.trait_impls_of(trait_def_id);
+        for &impl_def_id in trait_impls.blanket_impls() {
+            f(impl_def_id);
+        }
+    }
+
+    fn has_item_definition(self, def_id: DefId) -> bool {
+        self.defaultness(def_id).has_value()
+    }
+
+    fn impl_is_default(self, impl_def_id: DefId) -> bool {
+        self.defaultness(impl_def_id).is_default()
+    }
+
+    fn impl_trait_ref(self, impl_def_id: DefId) -> ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>> {
+        self.impl_trait_ref(impl_def_id).unwrap()
+    }
+
+    fn impl_polarity(self, impl_def_id: DefId) -> ty::ImplPolarity {
+        self.impl_polarity(impl_def_id)
+    }
+
+    fn trait_is_auto(self, trait_def_id: DefId) -> bool {
+        self.trait_is_auto(trait_def_id)
+    }
+
+    fn trait_is_alias(self, trait_def_id: DefId) -> bool {
+        self.trait_is_alias(trait_def_id)
+    }
+
+    fn trait_is_object_safe(self, trait_def_id: DefId) -> bool {
+        self.is_object_safe(trait_def_id)
+    }
+
+    fn trait_may_be_implemented_via_object(self, trait_def_id: DefId) -> bool {
+        self.trait_def(trait_def_id).implement_via_object
+    }
+
+    fn fn_trait_kind_from_def_id(self, trait_def_id: DefId) -> Option<ty::ClosureKind> {
+        self.fn_trait_kind_from_def_id(trait_def_id)
+    }
+
+    fn async_fn_trait_kind_from_def_id(self, trait_def_id: DefId) -> Option<ty::ClosureKind> {
+        self.async_fn_trait_kind_from_def_id(trait_def_id)
+    }
+
+    fn supertrait_def_ids(self, trait_def_id: DefId) -> impl IntoIterator<Item = DefId> {
+        self.supertrait_def_ids(trait_def_id)
+    }
+
+    fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed {
+        self.dcx().span_delayed_bug(DUMMY_SP, msg.to_string())
+    }
+
+    fn is_general_coroutine(self, coroutine_def_id: DefId) -> bool {
+        self.is_general_coroutine(coroutine_def_id)
+    }
+
+    fn coroutine_is_async(self, coroutine_def_id: DefId) -> bool {
+        self.coroutine_is_async(coroutine_def_id)
+    }
+
+    fn coroutine_is_gen(self, coroutine_def_id: DefId) -> bool {
+        self.coroutine_is_gen(coroutine_def_id)
+    }
+
+    fn coroutine_is_async_gen(self, coroutine_def_id: DefId) -> bool {
+        self.coroutine_is_async_gen(coroutine_def_id)
+    }
+
+    fn layout_is_pointer_like(self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+        self.layout_of(self.erase_regions(param_env.and(ty)))
+            .is_ok_and(|layout| layout.layout.is_pointer_like(&self.data_layout))
+    }
+
+    type UnsizingParams = &'tcx rustc_index::bit_set::BitSet<u32>;
+    fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams {
+        self.unsizing_params_for_adt(adt_def_id)
+    }
+
+    fn find_const_ty_from_env(
+        self,
+        param_env: ty::ParamEnv<'tcx>,
+        placeholder: Self::PlaceholderConst,
+    ) -> Ty<'tcx> {
+        placeholder.find_const_ty_from_env(param_env)
+    }
+}
+
+fn trait_lang_item_to_lang_item(lang_item: TraitSolverLangItem) -> LangItem {
+    match lang_item {
+        TraitSolverLangItem::AsyncDestruct => LangItem::AsyncDestruct,
+        TraitSolverLangItem::AsyncFnKindHelper => LangItem::AsyncFnKindHelper,
+        TraitSolverLangItem::AsyncFnKindUpvars => LangItem::AsyncFnKindUpvars,
+        TraitSolverLangItem::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput,
+        TraitSolverLangItem::AsyncIterator => LangItem::AsyncIterator,
+        TraitSolverLangItem::CallOnceFuture => LangItem::CallOnceFuture,
+        TraitSolverLangItem::CallRefFuture => LangItem::CallRefFuture,
+        TraitSolverLangItem::Clone => LangItem::Clone,
+        TraitSolverLangItem::Copy => LangItem::Copy,
+        TraitSolverLangItem::Coroutine => LangItem::Coroutine,
+        TraitSolverLangItem::CoroutineReturn => LangItem::CoroutineReturn,
+        TraitSolverLangItem::CoroutineYield => LangItem::CoroutineYield,
+        TraitSolverLangItem::Destruct => LangItem::Destruct,
+        TraitSolverLangItem::DiscriminantKind => LangItem::DiscriminantKind,
+        TraitSolverLangItem::DynMetadata => LangItem::DynMetadata,
+        TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait,
+        TraitSolverLangItem::FusedIterator => LangItem::FusedIterator,
+        TraitSolverLangItem::Future => LangItem::Future,
+        TraitSolverLangItem::FutureOutput => LangItem::FutureOutput,
+        TraitSolverLangItem::Iterator => LangItem::Iterator,
+        TraitSolverLangItem::Metadata => LangItem::Metadata,
+        TraitSolverLangItem::Option => LangItem::Option,
+        TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait,
+        TraitSolverLangItem::PointerLike => LangItem::PointerLike,
+        TraitSolverLangItem::Poll => LangItem::Poll,
+        TraitSolverLangItem::Sized => LangItem::Sized,
+        TraitSolverLangItem::TransmuteTrait => LangItem::TransmuteTrait,
+        TraitSolverLangItem::Tuple => LangItem::Tuple,
+        TraitSolverLangItem::Unpin => LangItem::Unpin,
+        TraitSolverLangItem::Unsize => LangItem::Unsize,
+    }
+}
+
+impl<'tcx> rustc_type_ir::inherent::DefId<TyCtxt<'tcx>> for DefId {
+    fn as_local(self) -> Option<LocalDefId> {
+        self.as_local()
+    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::Abi<TyCtxt<'tcx>> for abi::Abi {
@@ -377,6 +656,10 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
     fn coroutine_clone(self) -> bool {
         self.coroutine_clone
     }
+
+    fn associated_const_equality(self) -> bool {
+        self.associated_const_equality
+    }
 }
 
 type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs
index 54c88e48614..4155b120e51 100644
--- a/compiler/rustc_middle/src/ty/generic_args.rs
+++ b/compiler/rustc_middle/src/ty/generic_args.rs
@@ -44,10 +44,23 @@ pub struct GenericArg<'tcx> {
 impl<'tcx> rustc_type_ir::inherent::GenericArg<TyCtxt<'tcx>> for GenericArg<'tcx> {}
 
 impl<'tcx> rustc_type_ir::inherent::GenericArgs<TyCtxt<'tcx>> for ty::GenericArgsRef<'tcx> {
+    fn rebase_onto(
+        self,
+        tcx: TyCtxt<'tcx>,
+        source_ancestor: DefId,
+        target_args: GenericArgsRef<'tcx>,
+    ) -> GenericArgsRef<'tcx> {
+        self.rebase_onto(tcx, source_ancestor, target_args)
+    }
+
     fn type_at(self, i: usize) -> Ty<'tcx> {
         self.type_at(i)
     }
 
+    fn region_at(self, i: usize) -> ty::Region<'tcx> {
+        self.region_at(i)
+    }
+
     fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::GenericArgsRef<'tcx> {
         GenericArgs::identity_for_item(tcx, def_id)
     }
@@ -281,6 +294,7 @@ impl<'tcx> GenericArg<'tcx> {
     pub fn is_non_region_infer(self) -> bool {
         match self.unpack() {
             GenericArgKind::Lifetime(_) => false,
+            // FIXME: This shouldn't return numerical/float.
             GenericArgKind::Type(ty) => ty.is_ty_or_numeric_infer(),
             GenericArgKind::Const(ct) => ct.is_ct_infer(),
         }
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 185dbe44735..6467689a8aa 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -392,6 +392,10 @@ impl<'tcx> GenericPredicates<'tcx> {
         EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args)
     }
 
+    pub fn instantiate_own_identity(&self) -> impl Iterator<Item = (Clause<'tcx>, Span)> {
+        EarlyBinder::bind(self.predicates).instantiate_identity_iter_copied()
+    }
+
     #[instrument(level = "debug", skip(self, tcx))]
     fn instantiate_into(
         &self,
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index d07541bad93..9c2bfc12a18 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -990,6 +990,16 @@ pub struct ParamEnv<'tcx> {
     packed: CopyTaggedPtr<Clauses<'tcx>, ParamTag, true>,
 }
 
+impl<'tcx> rustc_type_ir::inherent::ParamEnv<TyCtxt<'tcx>> for ParamEnv<'tcx> {
+    fn reveal(self) -> Reveal {
+        self.reveal()
+    }
+
+    fn caller_bounds(self) -> impl IntoIterator<Item = ty::Clause<'tcx>> {
+        self.caller_bounds()
+    }
+}
+
 #[derive(Copy, Clone)]
 struct ParamTag {
     reveal: traits::Reveal,
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index ae36f2624ca..e9b37503bb3 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -175,6 +175,14 @@ pub struct Clause<'tcx>(
 
 impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {}
 
+impl<'tcx> rustc_type_ir::inherent::IntoKind for Clause<'tcx> {
+    type Kind = ty::Binder<'tcx, ClauseKind<'tcx>>;
+
+    fn kind(self) -> Self::Kind {
+        self.kind()
+    }
+}
+
 impl<'tcx> Clause<'tcx> {
     pub fn as_predicate(self) -> Predicate<'tcx> {
         Predicate(self.0)
@@ -251,6 +259,28 @@ impl<'tcx> ExistentialPredicate<'tcx> {
 
 pub type PolyExistentialPredicate<'tcx> = ty::Binder<'tcx, ExistentialPredicate<'tcx>>;
 
+impl<'tcx> rustc_type_ir::inherent::BoundExistentialPredicates<TyCtxt<'tcx>>
+    for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>
+{
+    fn principal_def_id(self) -> Option<DefId> {
+        self.principal_def_id()
+    }
+
+    fn principal(self) -> Option<ty::PolyExistentialTraitRef<'tcx>> {
+        self.principal()
+    }
+
+    fn auto_traits(self) -> impl IntoIterator<Item = DefId> {
+        self.auto_traits()
+    }
+
+    fn projection_bounds(
+        self,
+    ) -> impl IntoIterator<Item = ty::Binder<'tcx, ExistentialProjection<'tcx>>> {
+        self.projection_bounds()
+    }
+}
+
 impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> {
     /// Returns the "principal `DefId`" of this set of existential predicates.
     ///
@@ -481,12 +511,6 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TraitRef<'tcx>> for Predicate<'tcx> {
     }
 }
 
-impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TraitRef<'tcx>> for TraitPredicate<'tcx> {
-    fn upcast_from(from: TraitRef<'tcx>, _tcx: TyCtxt<'tcx>) -> Self {
-        TraitPredicate { trait_ref: from, polarity: PredicatePolarity::Positive }
-    }
-}
-
 impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TraitRef<'tcx>> for Clause<'tcx> {
     fn upcast_from(from: TraitRef<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
         let p: Predicate<'tcx> = from.upcast(tcx);
@@ -543,6 +567,12 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, PolyTraitPredicate<'tcx>> for Clause<'tcx> {
     }
 }
 
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, RegionOutlivesPredicate<'tcx>> for Predicate<'tcx> {
+    fn upcast_from(from: RegionOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
+        ty::Binder::dummy(PredicateKind::Clause(ClauseKind::RegionOutlives(from))).upcast(tcx)
+    }
+}
+
 impl<'tcx> UpcastFrom<TyCtxt<'tcx>, PolyRegionOutlivesPredicate<'tcx>> for Predicate<'tcx> {
     fn upcast_from(from: PolyRegionOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
         from.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).upcast(tcx)
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 8308e537e5e..9c8a3484aa5 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -930,6 +930,22 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
     fn new_pat(interner: TyCtxt<'tcx>, ty: Self, pat: ty::Pattern<'tcx>) -> Self {
         Ty::new_pat(interner, ty, pat)
     }
+
+    fn new_unit(interner: TyCtxt<'tcx>) -> Self {
+        interner.types.unit
+    }
+
+    fn new_usize(interner: TyCtxt<'tcx>) -> Self {
+        interner.types.usize
+    }
+
+    fn discriminant_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> {
+        self.discriminant_ty(interner)
+    }
+
+    fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> {
+        self.async_destructor_ty(interner)
+    }
 }
 
 /// Type utilities
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 52a0e72e17e..b079ed521d3 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -565,42 +565,6 @@ impl<'tcx> TyCtxt<'tcx> {
         Ok(())
     }
 
-    /// Checks whether each generic argument is simply a unique generic placeholder.
-    ///
-    /// This is used in the new solver, which canonicalizes params to placeholders
-    /// for better caching.
-    pub fn uses_unique_placeholders_ignoring_regions(
-        self,
-        args: GenericArgsRef<'tcx>,
-    ) -> Result<(), NotUniqueParam<'tcx>> {
-        let mut seen = GrowableBitSet::default();
-        for arg in args {
-            match arg.unpack() {
-                // Ignore regions, since we can't resolve those in a canonicalized
-                // query in the trait solver.
-                GenericArgKind::Lifetime(_) => {}
-                GenericArgKind::Type(t) => match t.kind() {
-                    ty::Placeholder(p) => {
-                        if !seen.insert(p.bound.var) {
-                            return Err(NotUniqueParam::DuplicateParam(t.into()));
-                        }
-                    }
-                    _ => return Err(NotUniqueParam::NotParam(t.into())),
-                },
-                GenericArgKind::Const(c) => match c.kind() {
-                    ty::ConstKind::Placeholder(p) => {
-                        if !seen.insert(p.bound) {
-                            return Err(NotUniqueParam::DuplicateParam(c.into()));
-                        }
-                    }
-                    _ => return Err(NotUniqueParam::NotParam(c.into())),
-                },
-            }
-        }
-
-        Ok(())
-    }
-
     /// Returns `true` if `def_id` refers to a closure, coroutine, or coroutine-closure
     /// (i.e. an async closure). These are all represented by `hir::Closure`, and all
     /// have the same `DefKind`.
diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml
index 50dbc991f8f..3a5f438b432 100644
--- a/compiler/rustc_next_trait_solver/Cargo.toml
+++ b/compiler/rustc_next_trait_solver/Cargo.toml
@@ -5,9 +5,25 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
+bitflags = "2.4.1"
+derivative = "2.2.0"
+rustc_ast_ir = { path = "../rustc_ast_ir" }
+rustc_data_structures = { path = "../rustc_data_structures", optional = true }
+rustc_index = { path = "../rustc_index", default-features = false }
+rustc_macros = { path = "../rustc_macros", optional = true }
+rustc_serialize = { path = "../rustc_serialize", optional = true }
 rustc_type_ir = { path = "../rustc_type_ir", default-features = false }
+rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [features]
 default = ["nightly"]
-nightly = ["rustc_type_ir/nightly"]
+nightly = [
+    "rustc_ast_ir/nightly",
+    "rustc_data_structures",
+    "rustc_index/nightly",
+    "rustc_macros",
+    "rustc_serialize",
+    "rustc_type_ir/nightly",
+]
diff --git a/compiler/rustc_next_trait_solver/src/infcx.rs b/compiler/rustc_next_trait_solver/src/infcx.rs
index cb46d8f8f73..e1d5c37fada 100644
--- a/compiler/rustc_next_trait_solver/src/infcx.rs
+++ b/compiler/rustc_next_trait_solver/src/infcx.rs
@@ -1,13 +1,27 @@
+use std::fmt::Debug;
+
 use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::relate::Relate;
-use rustc_type_ir::solve::{Goal, NoSolution};
+use rustc_type_ir::solve::{Goal, NoSolution, SolverMode};
 use rustc_type_ir::{self as ty, Interner};
 
 pub trait SolverDelegate: Sized {
     type Interner: Interner;
-
     fn interner(&self) -> Self::Interner;
 
+    fn solver_mode(&self) -> SolverMode;
+
+    fn build_with_canonical<V>(
+        interner: Self::Interner,
+        solver_mode: SolverMode,
+        canonical: &ty::Canonical<Self::Interner, V>,
+    ) -> (Self, V, ty::CanonicalVarValues<Self::Interner>)
+    where
+        V: TypeFoldable<Self::Interner>;
+
+    fn universe(&self) -> ty::UniverseIndex;
+    fn create_next_universe(&self) -> ty::UniverseIndex;
+
     fn universe_of_ty(&self, ty: ty::TyVid) -> Option<ty::UniverseIndex>;
     fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex>;
     fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex>;
@@ -74,4 +88,102 @@ pub trait SolverDelegate: Sized {
         T: TypeFoldable<Self::Interner>;
 
     fn probe<T>(&self, probe: impl FnOnce() -> T) -> T;
+
+    // FIXME: Uplift the leak check into this crate.
+    fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>;
+
+    // FIXME: This is only here because elaboration lives in `rustc_infer`!
+    fn elaborate_supertraits(
+        interner: Self::Interner,
+        trait_ref: ty::Binder<Self::Interner, ty::TraitRef<Self::Interner>>,
+    ) -> impl Iterator<Item = ty::Binder<Self::Interner, ty::TraitRef<Self::Interner>>>;
+
+    fn try_const_eval_resolve(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        unevaluated: ty::UnevaluatedConst<Self::Interner>,
+    ) -> Option<<Self::Interner as Interner>::Const>;
+
+    fn sub_regions(
+        &self,
+        sub: <Self::Interner as Interner>::Region,
+        sup: <Self::Interner as Interner>::Region,
+    );
+
+    fn register_ty_outlives(
+        &self,
+        ty: <Self::Interner as Interner>::Ty,
+        r: <Self::Interner as Interner>::Region,
+    );
+
+    // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`!
+    fn well_formed_goals(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        arg: <Self::Interner as Interner>::GenericArg,
+    ) -> Option<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>>;
+
+    fn clone_opaque_types_for_query_response(
+        &self,
+    ) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
+
+    fn make_deduplicated_outlives_constraints(
+        &self,
+    ) -> Vec<ty::OutlivesPredicate<Self::Interner, <Self::Interner as Interner>::GenericArg>>;
+
+    fn instantiate_canonical<V>(
+        &self,
+        canonical: ty::Canonical<Self::Interner, V>,
+        values: ty::CanonicalVarValues<Self::Interner>,
+    ) -> V
+    where
+        V: TypeFoldable<Self::Interner>;
+
+    fn instantiate_canonical_var_with_infer(
+        &self,
+        cv_info: ty::CanonicalVarInfo<Self::Interner>,
+        universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
+    ) -> <Self::Interner as Interner>::GenericArg;
+
+    // FIXME: Can we implement this in terms of `add` and `inject`?
+    fn insert_hidden_type(
+        &self,
+        opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        hidden_ty: <Self::Interner as Interner>::Ty,
+        goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+    ) -> Result<(), NoSolution>;
+
+    fn add_item_bounds_for_hidden_type(
+        &self,
+        def_id: <Self::Interner as Interner>::DefId,
+        args: <Self::Interner as Interner>::GenericArgs,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        hidden_ty: <Self::Interner as Interner>::Ty,
+        goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+    );
+
+    fn inject_new_hidden_type_unchecked(
+        &self,
+        key: ty::OpaqueTypeKey<Self::Interner>,
+        hidden_ty: <Self::Interner as Interner>::Ty,
+    );
+
+    fn reset_opaque_types(&self);
+
+    fn trait_ref_is_knowable<E: Debug>(
+        &self,
+        trait_ref: ty::TraitRef<Self::Interner>,
+        lazily_normalize_ty: impl FnMut(
+            <Self::Interner as Interner>::Ty,
+        ) -> Result<<Self::Interner as Interner>::Ty, E>,
+    ) -> Result<bool, E>;
+
+    fn fetch_eligible_assoc_item(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        goal_trait_ref: ty::TraitRef<Self::Interner>,
+        trait_assoc_def_id: <Self::Interner as Interner>::DefId,
+        impl_def_id: <Self::Interner as Interner>::DefId,
+    ) -> Result<Option<<Self::Interner as Interner>::DefId>, NoSolution>;
 }
diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs
index ea3e18872fa..a6002bfd7ca 100644
--- a/compiler/rustc_next_trait_solver/src/lib.rs
+++ b/compiler/rustc_next_trait_solver/src/lib.rs
@@ -4,6 +4,12 @@
 //! but were uplifted in the process of making the new trait solver generic.
 //! So if you got to this crate from the old solver, it's totally normal.
 
+#![feature(let_chains)]
+
+// TODO: remove this, use explicit imports.
+#[macro_use]
+extern crate tracing;
+
 pub mod canonicalizer;
 pub mod infcx;
 pub mod resolve;
diff --git a/compiler/rustc_next_trait_solver/src/solve.rs b/compiler/rustc_next_trait_solver/src/solve.rs
deleted file mode 100644
index eba96facabc..00000000000
--- a/compiler/rustc_next_trait_solver/src/solve.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub use rustc_type_ir::solve::*;
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs
index 722e86f5b84..3228146c689 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs
@@ -15,17 +15,22 @@
 //! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
 //! relate them structurally.
 
-use super::infcx::SolverDelegate;
-use super::EvalCtxt;
-use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
-use rustc_middle::ty;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::{self as ty, Interner};
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
+
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", 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> {
+        goal: Goal<I, (I::Term, I::Term, ty::AliasRelationDirection)>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
         debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some());
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index 45d21285ff1..2664b3916e1 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -1,33 +1,25 @@
 //! Code shared by trait and projection goals for candidate assembly.
 
-use crate::solve::infcx::SolverDelegate;
-use derivative::Derivative;
-use rustc_hir::def_id::DefId;
-use rustc_hir::LangItem;
-use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::util::supertraits;
-use rustc_middle::bug;
-use rustc_middle::traits::solve::inspect::ProbeKind;
-use rustc_middle::traits::solve::{Certainty, Goal, MaybeCause, 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::{TypeVisitableExt, Upcast};
-use rustc_span::{ErrorGuaranteed, DUMMY_SP};
-use rustc_type_ir::solve::{CandidateSource, CanonicalResponse};
-use rustc_type_ir::Interner;
-
-use crate::solve::GoalSource;
-use crate::solve::{EvalCtxt, SolverMode};
-
 pub(super) mod structural_traits;
 
+use rustc_type_ir::fold::TypeFoldable;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::visit::TypeVisitableExt as _;
+use rustc_type_ir::{self as ty, Interner, Upcast as _};
+
+use crate::infcx::SolverDelegate;
+use crate::solve::inspect::ProbeKind;
+use crate::solve::{
+    BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource,
+    MaybeCause, NoSolution, QueryResult, SolverMode,
+};
+
 /// 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(Derivative)]
+#[derive(derivative::Derivative)]
 #[derivative(Debug(bound = ""), Clone(bound = ""))]
 pub(super) struct Candidate<I: Interner> {
     pub(super) source: CandidateSource<I>,
@@ -35,39 +27,42 @@ pub(super) struct Candidate<I: Interner> {
 }
 
 /// Methods used to assemble candidates for either trait or projection goals.
-pub(super) trait GoalKind<'tcx>:
-    TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
+pub(super) trait GoalKind<Infcx, I = <Infcx as SolverDelegate>::Interner>:
+    TypeFoldable<I> + Copy + Eq + std::fmt::Display
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
 {
-    fn self_ty(self) -> Ty<'tcx>;
+    fn self_ty(self) -> I::Ty;
 
-    fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>;
+    fn trait_ref(self, tcx: I) -> ty::TraitRef<I>;
 
-    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
+    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self;
 
-    fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
+    fn trait_def_id(self, tcx: I) -> I::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<'_, SolverDelegate<'tcx>>,
-        source: CandidateSource<TyCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-        then: impl FnOnce(&mut EvalCtxt<'_, SolverDelegate<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+        then: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult<I>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// 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 probe_and_consider_implied_clause(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        parent_source: CandidateSource<TyCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-        requirements: impl IntoIterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        parent_source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+        requirements: impl IntoIterator<Item = (GoalSource, Goal<I, I::Predicate>)>,
+    ) -> Result<Candidate<I>, NoSolution> {
         Self::probe_and_match_goal_against_assumption(ecx, parent_source, goal, assumption, |ecx| {
             for (nested_source, goal) in requirements {
                 ecx.add_goal(nested_source, goal);
@@ -80,15 +75,15 @@ pub(super) trait GoalKind<'tcx>:
     /// 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 probe_and_consider_object_bound_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        source: CandidateSource<TyCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+    ) -> Result<Candidate<I>, NoSolution> {
         Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
             let tcx = ecx.interner();
-            let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
-                bug!("expected object type in `probe_and_consider_object_bound_candidate`");
+            let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else {
+                panic!("expected object type in `probe_and_consider_object_bound_candidate`");
             };
             ecx.add_goals(
                 GoalSource::ImplWhereBound,
@@ -104,10 +99,10 @@ pub(super) trait GoalKind<'tcx>:
     }
 
     fn consider_impl_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-        impl_def_id: DefId,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+        impl_def_id: I::DefId,
+    ) -> Result<Candidate<I>, 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
@@ -116,85 +111,85 @@ pub(super) trait GoalKind<'tcx>:
     /// 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<'_, SolverDelegate<'tcx>>,
-        guar: ErrorGuaranteed,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        guar: I::ErrorGuaranteed,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// 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<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A trait alias holds if the RHS traits and `where` clauses hold.
     fn consider_trait_alias_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A type is `Sized` if its tail component is `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<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// 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<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// 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<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A type is a `FnPtr` if it is of `FnPtr` type.
     fn consider_builtin_fn_ptr_trait_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// 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<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// An async closure is known to implement the `AsyncFn<A>` family of traits
     /// where `A` is given by the signature of the type.
     fn consider_builtin_async_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// Compute the built-in logic of the `AsyncFnKindHelper` helper trait, which
     /// is used internally to delay computation for async closures until after
     /// upvar analysis is performed in HIR typeck.
     fn consider_builtin_async_fn_kind_helper_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// `Tuple` is implemented if the `Self` type is a tuple.
     fn consider_builtin_tuple_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// `Pointee` is always implemented.
     ///
@@ -202,65 +197,65 @@ pub(super) trait GoalKind<'tcx>:
     /// the built-in types. For structs, the metadata type is given by the struct
     /// tail.
     fn consider_builtin_pointee_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// 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<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// 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<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A coroutine (that comes from a `gen` desugaring) is known to implement
     /// `FusedIterator`
     fn consider_builtin_fused_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_async_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// 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<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_discriminant_kind_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_async_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_transmute_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// Consider (possibly several) candidates to upcast or unsize a type to another
     /// type, excluding the coercion of a sized type into a `dyn Trait`.
@@ -270,16 +265,20 @@ pub(super) trait GoalKind<'tcx>:
     /// 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<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Vec<Candidate<TyCtxt<'tcx>>>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Vec<Candidate<I>>;
 }
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
-    pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>(
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
+    pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
+        goal: Goal<I, G>,
+    ) -> Vec<Candidate<I>> {
         let Ok(normalized_self_ty) =
             self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
         else {
@@ -291,7 +290,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect();
         }
 
-        let goal: Goal<'tcx, G> = goal.with(
+        let goal: Goal<I, G> = goal.with(
             self.interner(),
             goal.predicate.with_self_ty(self.interner(), normalized_self_ty),
         );
@@ -301,7 +300,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
 
         let mut candidates = vec![];
 
-        self.assemble_non_blanket_impl_candidates(goal, &mut candidates);
+        self.assemble_impl_candidates(goal, &mut candidates);
 
         self.assemble_builtin_impl_candidates(goal, &mut candidates);
 
@@ -309,8 +308,6 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
 
         self.assemble_object_bound_candidates(goal, &mut candidates);
 
-        self.assemble_blanket_impl_candidates(goal, &mut candidates);
-
         self.assemble_param_env_candidates(goal, &mut candidates);
 
         match self.solver_mode() {
@@ -326,7 +323,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     pub(super) fn forced_ambiguity(
         &mut self,
         cause: MaybeCause,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         // This may fail if `try_evaluate_added_goals` overflows because it
         // fails to reach a fixpoint but ends up getting an error after
         // running for some additional step.
@@ -339,149 +336,36 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>(
+    fn assemble_impl_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
-        let self_ty = goal.predicate.self_ty();
-        let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
-        let mut consider_impls_for_simplified_type = |simp| {
-            if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) {
-                for &impl_def_id in impls_for_type {
-                    // For every `default impl`, there's always a non-default `impl`
-                    // that will *also* apply. There's no reason to register a candidate
-                    // for this impl, since it is *not* proof that the trait goal holds.
-                    if tcx.defaultness(impl_def_id).is_default() {
-                        return;
-                    }
-
-                    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::Pat(_, _)
-            | ty::Slice(_)
-            | ty::RawPtr(_, _)
-            | ty::Ref(_, _, _)
-            | ty::FnDef(_, _)
-            | ty::FnPtr(_)
-            | ty::Dynamic(_, _, _)
-            | ty::Closure(..)
-            | ty::CoroutineClosure(..)
-            | 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);
+        tcx.for_each_relevant_impl(
+            goal.predicate.trait_def_id(tcx),
+            goal.predicate.self_ty(),
+            |impl_def_id| {
+                // For every `default impl`, there's always a non-default `impl`
+                // that will *also* apply. There's no reason to register a candidate
+                // for this impl, since it is *not* proof that the trait goal holds.
+                if tcx.impl_is_default(impl_def_id) {
+                    return;
                 }
-            }
 
-            ty::Infer(ty::FloatVar(_)) => {
-                // This causes a compiler error if any new float kinds are added.
-                let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128);
-                let possible_floats = [
-                    SimplifiedType::Float(ty::FloatTy::F16),
-                    SimplifiedType::Float(ty::FloatTy::F32),
-                    SimplifiedType::Float(ty::FloatTy::F64),
-                    SimplifiedType::Float(ty::FloatTy::F128),
-                ];
-
-                for simp in possible_floats {
-                    consider_impls_for_simplified_type(simp);
+                match G::consider_impl_candidate(self, goal, impl_def_id) {
+                    Ok(candidate) => candidates.push(candidate),
+                    Err(NoSolution) => (),
                 }
-            }
-
-            // 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 = "trace", skip_all)]
-    fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
-        &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
-    ) {
-        let tcx = self.interner();
-        let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
-        for &impl_def_id in trait_impls.blanket_impls() {
-            // For every `default impl`, there's always a non-default `impl`
-            // that will *also* apply. There's no reason to register a candidate
-            // for this impl, since it is *not* proof that the trait goal holds.
-            if tcx.defaultness(impl_def_id).is_default() {
-                return;
-            }
-
-            match G::consider_impl_candidate(self, goal, impl_def_id) {
-                Ok(candidate) => candidates.push(candidate),
-                Err(NoSolution) => (),
-            }
-        }
+            },
+        );
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
+    fn assemble_builtin_impl_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
         let trait_def_id = goal.predicate.trait_def_id(tcx);
@@ -499,43 +383,43 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             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 tcx.is_lang_item(trait_def_id, LangItem::Sized) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Sized) {
             G::consider_builtin_sized_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Copy)
-            || tcx.is_lang_item(trait_def_id, LangItem::Clone)
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Copy)
+            || tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Clone)
         {
             G::consider_builtin_copy_clone_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::PointerLike) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointerLike) {
             G::consider_builtin_pointer_like_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::FnPtrTrait) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FnPtrTrait) {
             G::consider_builtin_fn_ptr_trait_candidate(self, goal)
         } else if let Some(kind) = self.interner().fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_fn_trait_candidates(self, goal, kind)
         } else if let Some(kind) = self.interner().async_fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_async_fn_trait_candidates(self, goal, kind)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncFnKindHelper) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncFnKindHelper) {
             G::consider_builtin_async_fn_kind_helper_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Tuple) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Tuple) {
             G::consider_builtin_tuple_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::PointeeTrait) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointeeTrait) {
             G::consider_builtin_pointee_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Future) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Future) {
             G::consider_builtin_future_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Iterator) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Iterator) {
             G::consider_builtin_iterator_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::FusedIterator) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FusedIterator) {
             G::consider_builtin_fused_iterator_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncIterator) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncIterator) {
             G::consider_builtin_async_iterator_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Coroutine) {
             G::consider_builtin_coroutine_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::DiscriminantKind) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::DiscriminantKind) {
             G::consider_builtin_discriminant_kind_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncDestruct) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncDestruct) {
             G::consider_builtin_async_destruct_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Destruct) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Destruct) {
             G::consider_builtin_destruct_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::TransmuteTrait) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::TransmuteTrait) {
             G::consider_builtin_transmute_candidate(self, goal)
         } else {
             Err(NoSolution)
@@ -545,18 +429,18 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
 
         // 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 tcx.is_lang_item(trait_def_id, LangItem::Unsize) {
+        if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Unsize) {
             candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal));
         }
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_param_env_candidates<G: GoalKind<'tcx>>(
+    fn assemble_param_env_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
-        for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
+        for (i, assumption) in goal.param_env.caller_bounds().into_iter().enumerate() {
             candidates.extend(G::probe_and_consider_implied_clause(
                 self,
                 CandidateSource::ParamEnv(i),
@@ -568,10 +452,10 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_alias_bound_candidates<G: GoalKind<'tcx>>(
+    fn assemble_alias_bound_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
             ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates);
@@ -587,13 +471,13 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// If so, continue searching by recursively calling after normalization.
     // FIXME: This may recurse infinitely, but I can't seem to trigger it without
     // hitting another overflow error something. Add a depth parameter needed later.
-    fn assemble_alias_bound_candidates_recur<G: GoalKind<'tcx>>(
+    fn assemble_alias_bound_candidates_recur<G: GoalKind<Infcx>>(
         &mut self,
-        self_ty: Ty<'tcx>,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        self_ty: I::Ty,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
-        let (kind, alias_ty) = match *self_ty.kind() {
+        let (kind, alias_ty) = match self_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(_)
@@ -621,7 +505,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
             | ty::Error(_) => return,
             ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) | ty::Bound(..) => {
-                bug!("unexpected self type for `{goal:?}`")
+                panic!("unexpected self type for `{goal:?}`")
             }
 
             ty::Infer(ty::TyVar(_)) => {
@@ -638,16 +522,15 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
 
             ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty),
             ty::Alias(ty::Inherent | ty::Weak, _) => {
-                self.interner().sess.dcx().span_delayed_bug(
-                    DUMMY_SP,
-                    format!("could not normalize {self_ty}, it is not WF"),
-                );
+                self.interner().delay_bug(format!("could not normalize {self_ty:?}, it is not WF"));
                 return;
             }
         };
 
-        for assumption in
-            self.interner().item_bounds(alias_ty.def_id).instantiate(self.interner(), alias_ty.args)
+        for assumption in self
+            .interner()
+            .item_bounds(alias_ty.def_id)
+            .iter_instantiated(self.interner(), &alias_ty.args)
         {
             candidates.extend(G::probe_and_consider_implied_clause(
                 self,
@@ -672,18 +555,18 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_object_bound_candidates<G: GoalKind<'tcx>>(
+    fn assemble_object_bound_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
-        if !tcx.trait_def(goal.predicate.trait_def_id(tcx)).implement_via_object {
+        if !tcx.trait_may_be_implemented_via_object(goal.predicate.trait_def_id(tcx)) {
             return;
         }
 
         let self_ty = goal.predicate.self_ty();
-        let bounds = match *self_ty.kind() {
+        let bounds = match self_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(_)
@@ -711,12 +594,12 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             | 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::Bound(..) => panic!("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.is_object_safe(def_id)) {
+        if bounds.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) {
             return;
         }
 
@@ -745,7 +628,9 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
         // a projection goal.
         if let Some(principal) = bounds.principal() {
             let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
-            for (idx, assumption) in supertraits(self.interner(), principal_trait_ref).enumerate() {
+            for (idx, assumption) in
+                Infcx::elaborate_supertraits(tcx, principal_trait_ref).enumerate()
+            {
                 candidates.extend(G::probe_and_consider_object_bound_candidate(
                     self,
                     CandidateSource::BuiltinImpl(BuiltinImplSource::Object(idx)),
@@ -763,10 +648,10 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// To do so we add an ambiguous candidate in case such an unknown impl could
     /// apply to the current goal.
     #[instrument(level = "trace", skip_all)]
-    fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>(
+    fn assemble_coherence_unknowable_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
 
@@ -792,13 +677,13 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     // to improve this however. However, this should make it fairly straightforward to refine
     // the filtering going forward, so it seems alright-ish for now.
     #[instrument(level = "debug", skip(self, goal))]
-    fn discard_impls_shadowed_by_env<G: GoalKind<'tcx>>(
+    fn discard_impls_shadowed_by_env<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
-        let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> =
+        let trait_goal: Goal<I, ty::TraitPredicate<I>> =
             goal.with(tcx, goal.predicate.trait_ref(tcx));
 
         let mut trait_candidates_from_env = vec![];
@@ -823,7 +708,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                             false
                         }
                         CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true,
-                        CandidateSource::CoherenceUnknowable => bug!("uh oh"),
+                        CandidateSource::CoherenceUnknowable => panic!("uh oh"),
                     });
                 }
                 // If it is still ambiguous we instead just force the whole goal
@@ -841,10 +726,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// 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,
-        candidates: Vec<Candidate<TyCtxt<'tcx>>>,
-    ) -> QueryResult<'tcx> {
+    pub(super) fn merge_candidates(&mut self, candidates: Vec<Candidate<I>>) -> QueryResult<I> {
         // 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) {
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
index 496be3af573..eb37add61cc 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
@@ -3,20 +3,16 @@
 
 use rustc_ast_ir::{Movability, Mutability};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_next_trait_solver::infcx::SolverDelegate;
-use rustc_next_trait_solver::solve::{Goal, NoSolution};
 use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
-use rustc_type_ir::{self as ty, Interner, Upcast};
+use rustc_type_ir::{self as ty, Interner, Upcast as _};
 use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
 
-use crate::solve::EvalCtxt;
+use crate::infcx::SolverDelegate;
+use crate::solve::{EvalCtxt, Goal, NoSolution};
 
 // 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 = "trace", skip(ecx), ret)]
 pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<Infcx, I>(
     ecx: &EvalCtxt<'_, Infcx>,
@@ -745,7 +741,7 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I>
                     )
                     .expect("expected to be able to unify goal projection with dyn's projection"),
             );
-            proj.term.expect_type()
+            proj.term.expect_ty()
         } else {
             ty.super_fold_with(self)
         }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index 3ba17a0dcf9..e4b54fff0b3 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -8,60 +8,52 @@
 //! 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 std::iter;
+
+use rustc_index::IndexVec;
+use rustc_type_ir::fold::TypeFoldable;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, Interner};
+
+use crate::canonicalizer::{CanonicalizeMode, Canonicalizer};
+use crate::infcx::SolverDelegate;
+use crate::resolve::EagerResolver;
 use crate::solve::eval_ctxt::NestedGoals;
-use crate::solve::infcx::SolverDelegate;
+use crate::solve::inspect;
 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::{CanonicalExt, QueryRegionConstraints};
-use rustc_infer::infer::RegionVariableOrigin;
-use rustc_infer::infer::{InferCtxt, InferOk};
-use rustc_infer::traits::solve::NestedNormalizationGoals;
-use rustc_middle::bug;
-use rustc_middle::infer::canonical::Canonical;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::solve::{
-    ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
+    response_no_constraints_raw, CanonicalInput, CanonicalResponse, Certainty, EvalCtxt,
+    ExternalConstraintsData, Goal, MaybeCause, NestedNormalizationGoals, NoSolution,
+    PredefinedOpaquesData, QueryInput, QueryResult, Response,
 };
-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_next_trait_solver::infcx::SolverDelegate as IrSolverDelegate;
-use rustc_next_trait_solver::resolve::EagerResolver;
-use rustc_span::{Span, DUMMY_SP};
-use rustc_type_ir::CanonicalVarValues;
-use rustc_type_ir::Interner;
-use std::assert_matches::assert_matches;
-use std::iter;
-use std::ops::Deref;
 
-trait ResponseT<'tcx> {
-    fn var_values(&self) -> CanonicalVarValues<TyCtxt<'tcx>>;
+trait ResponseT<I: Interner> {
+    fn var_values(&self) -> CanonicalVarValues<I>;
 }
 
-impl<'tcx> ResponseT<'tcx> for Response<TyCtxt<'tcx>> {
-    fn var_values(&self) -> CanonicalVarValues<TyCtxt<'tcx>> {
+impl<I: Interner> ResponseT<I> for Response<I> {
+    fn var_values(&self) -> CanonicalVarValues<I> {
         self.var_values
     }
 }
 
-impl<'tcx, T> ResponseT<'tcx> for inspect::State<TyCtxt<'tcx>, T> {
-    fn var_values(&self) -> CanonicalVarValues<TyCtxt<'tcx>> {
+impl<I: Interner, T> ResponseT<I> for inspect::State<I, T> {
+    fn var_values(&self) -> CanonicalVarValues<I> {
         self.var_values
     }
 }
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     /// Canonicalizes the goal remembering the original values
     /// for each bound variable.
-    pub(super) fn canonicalize_goal<T: TypeFoldable<TyCtxt<'tcx>>>(
+    pub(super) fn canonicalize_goal<T: TypeFoldable<I>>(
         &self,
-        goal: Goal<'tcx, T>,
-    ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
+        goal: Goal<I, T>,
+    ) -> (Vec<I::GenericArg>, CanonicalInput<I, 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));
@@ -91,7 +83,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response(
         &mut self,
         certainty: Certainty,
-    ) -> QueryResult<'tcx> {
+    ) -> QueryResult<I> {
         self.inspect.make_canonical_response(certainty);
 
         let goals_certainty = self.try_evaluate_added_goals()?;
@@ -104,8 +96,8 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
 
         // We only check for leaks from universes which were entered inside
         // of the query.
-        self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
-            trace!(?e, "failed the leak check");
+        self.infcx.leak_check(self.max_input_universe).map_err(|NoSolution| {
+            trace!("failed the leak check");
             NoSolution
         })?;
 
@@ -121,7 +113,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             if cfg!(debug_assertions) {
                 assert!(normalizes_to_goals.is_empty());
                 if goals.is_empty() {
-                    assert_matches!(goals_certainty, Certainty::Yes);
+                    assert!(matches!(goals_certainty, Certainty::Yes));
                 }
             }
             (certainty, NestedNormalizationGoals(goals))
@@ -160,7 +152,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     pub(in crate::solve) fn make_ambiguous_response_no_constraints(
         &self,
         maybe_cause: MaybeCause,
-    ) -> CanonicalResponse<'tcx> {
+    ) -> CanonicalResponse<I> {
         response_no_constraints_raw(
             self.interner(),
             self.max_input_universe,
@@ -180,8 +172,8 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     fn compute_external_query_constraints(
         &self,
         certainty: Certainty,
-        normalization_nested_goals: NestedNormalizationGoals<TyCtxt<'tcx>>,
-    ) -> ExternalConstraintsData<TyCtxt<'tcx>> {
+        normalization_nested_goals: NestedNormalizationGoals<I>,
+    ) -> ExternalConstraintsData<I> {
         // We only return region constraints once the certainty is `Yes`. This
         // is necessary as we may drop nested goals on ambiguity, which may result
         // in unconstrained inference variables in the region constraints. It also
@@ -191,26 +183,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
         // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and
         // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`.
         let region_constraints = if certainty == Certainty::Yes {
-            // Cannot use `take_registered_region_obligations` as we may compute the response
-            // inside of a `probe` whenever we have multiple choices inside of the solver.
-            let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
-            let QueryRegionConstraints { outlives, member_constraints } =
-                self.infcx.with_region_constraints(|region_constraints| {
-                    make_query_region_constraints(
-                        self.interner(),
-                        region_obligations.iter().map(|r_o| {
-                            (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())
-                        }),
-                        region_constraints,
-                    )
-                });
-            assert_eq!(member_constraints, vec![]);
-            let mut seen = FxHashSet::default();
-            outlives
-                .into_iter()
-                .filter(|(outlives, _)| seen.insert(*outlives))
-                .map(|(outlives, _origin)| outlives)
-                .collect()
+            self.infcx.make_deduplicated_outlives_constraints()
         } else {
             Default::default()
         };
@@ -240,10 +213,10 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     ///   the `normalization_nested_goals`
     pub(super) fn instantiate_and_apply_query_response(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        original_values: Vec<ty::GenericArg<'tcx>>,
-        response: CanonicalResponse<'tcx>,
-    ) -> (NestedNormalizationGoals<TyCtxt<'tcx>>, Certainty) {
+        param_env: I::ParamEnv,
+        original_values: Vec<I::GenericArg>,
+        response: CanonicalResponse<I>,
+    ) -> (NestedNormalizationGoals<I>, Certainty) {
         let instantiation = Self::compute_query_response_instantiation_values(
             self.infcx,
             &original_values,
@@ -251,7 +224,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
         );
 
         let Response { var_values, external_constraints, certainty } =
-            response.instantiate(self.interner(), &instantiation);
+            self.infcx.instantiate_canonical(response, instantiation);
 
         Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values);
 
@@ -259,7 +232,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             region_constraints,
             opaque_types,
             normalization_nested_goals,
-        } = external_constraints.deref();
+        } = &*external_constraints;
         self.register_region_constraints(region_constraints);
         self.register_new_opaque_types(opaque_types);
         (normalization_nested_goals.clone(), certainty)
@@ -268,11 +241,11 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// This returns the canoncial variable values to instantiate the bound variables of
     /// the canonical response. This depends on the `original_values` for the
     /// bound variables.
-    fn compute_query_response_instantiation_values<T: ResponseT<'tcx>>(
-        infcx: &InferCtxt<'tcx>,
-        original_values: &[ty::GenericArg<'tcx>],
-        response: &Canonical<'tcx, T>,
-    ) -> CanonicalVarValues<TyCtxt<'tcx>> {
+    fn compute_query_response_instantiation_values<T: ResponseT<I>>(
+        infcx: &Infcx,
+        original_values: &[I::GenericArg],
+        response: &Canonical<I, T>,
+    ) -> CanonicalVarValues<I> {
         // FIXME: Longterm canonical queries should deal with all placeholders
         // created inside of the query directly instead of returning them to the
         // caller.
@@ -294,35 +267,35 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
         // 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() {
+            match result_value.kind() {
+                ty::GenericArgKind::Type(t) => {
+                    if let ty::Bound(debruijn, b) = t.kind() {
                         assert_eq!(debruijn, ty::INNERMOST);
-                        opt_values[b.var] = Some(*original_value);
+                        opt_values[b.var()] = Some(*original_value);
                     }
                 }
-                GenericArgKind::Lifetime(r) => {
-                    if let ty::ReBound(debruijn, br) = *r {
+                ty::GenericArgKind::Lifetime(r) => {
+                    if let ty::ReBound(debruijn, br) = r.kind() {
                         assert_eq!(debruijn, ty::INNERMOST);
-                        opt_values[br.var] = Some(*original_value);
+                        opt_values[br.var()] = Some(*original_value);
                     }
                 }
-                GenericArgKind::Const(c) => {
-                    if let ty::ConstKind::Bound(debruijn, b) = c.kind() {
+                ty::GenericArgKind::Const(c) => {
+                    if let ty::ConstKind::Bound(debruijn, bv) = c.kind() {
                         assert_eq!(debruijn, ty::INNERMOST);
-                        opt_values[b] = Some(*original_value);
+                        opt_values[bv.var()] = Some(*original_value);
                     }
                 }
             }
         }
 
-        let var_values = infcx.tcx.mk_args_from_iter(response.variables.iter().enumerate().map(
-            |(index, info)| {
+        let var_values = infcx.interner().mk_args_from_iter(
+            response.variables.into_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| {
+                    infcx.instantiate_canonical_var_with_infer(info, |idx| {
                         ty::UniverseIndex::from(prev_universe.index() + idx.index())
                     })
                 } else if info.is_existential() {
@@ -333,18 +306,18 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                     // 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)] {
+                    if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] {
                         v
                     } else {
-                        infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe)
+                        infcx.instantiate_canonical_var_with_infer(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 }
     }
@@ -363,40 +336,35 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// always relate them structurally here.
     #[instrument(level = "trace", skip(infcx))]
     fn unify_query_var_values(
-        infcx: &InferCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        original_values: &[ty::GenericArg<'tcx>],
-        var_values: CanonicalVarValues<TyCtxt<'tcx>>,
+        infcx: &Infcx,
+        param_env: I::ParamEnv,
+        original_values: &[I::GenericArg],
+        var_values: CanonicalVarValues<I>,
     ) {
         assert_eq!(original_values.len(), var_values.len());
 
-        let cause = ObligationCause::dummy();
         for (&orig, response) in iter::zip(original_values, var_values.var_values) {
-            let InferOk { value: (), obligations } = infcx
-                .at(&cause, param_env)
-                .eq_structurally_relating_aliases(orig, response)
-                .unwrap();
-            assert!(obligations.is_empty());
+            let goals = infcx.eq_structurally_relating_aliases(param_env, orig, response).unwrap();
+            assert!(goals.is_empty());
         }
     }
 
     fn register_region_constraints(
         &mut self,
-        outlives: &[ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>],
+        outlives: &[ty::OutlivesPredicate<I, I::GenericArg>],
     ) {
         for &ty::OutlivesPredicate(lhs, rhs) in 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:?}"),
+            match lhs.kind() {
+                ty::GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs),
+                ty::GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs),
+                ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"),
             }
         }
     }
 
-    fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)]) {
+    fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)]) {
         for &(key, ty) in opaque_types {
-            let hidden_ty = ty::OpaqueHiddenType { ty, span: DUMMY_SP };
-            self.infcx.inject_new_hidden_type_unchecked(key, hidden_ty);
+            self.infcx.inject_new_hidden_type_unchecked(key, ty);
         }
     }
 }
@@ -412,7 +380,7 @@ pub(in crate::solve) fn make_canonical_state<Infcx, T, I>(
     data: T,
 ) -> inspect::CanonicalState<I, T>
 where
-    Infcx: IrSolverDelegate<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
     T: TypeFoldable<I>,
 {
@@ -426,47 +394,3 @@ where
         state,
     )
 }
-
-/// Instantiate a `CanonicalState`.
-///
-/// Unlike for query responses, `CanonicalState` also track fresh inference
-/// variables created while evaluating a goal. When creating two separate
-/// `CanonicalState` during a single evaluation both may reference this
-/// fresh inference variable. When instantiating them we now create separate
-/// inference variables for it and have to unify them somehow. We do this
-/// by extending the `var_values` while building the proof tree.
-///
-/// This currently assumes that unifying the var values trivially succeeds.
-/// Adding any inference constraints which weren't present when originally
-/// computing the canonical query can result in bugs.
-#[instrument(level = "trace", skip(infcx, span, param_env))]
-pub(in crate::solve) fn instantiate_canonical_state<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
-    infcx: &InferCtxt<'tcx>,
-    span: Span,
-    param_env: ty::ParamEnv<'tcx>,
-    orig_values: &mut Vec<ty::GenericArg<'tcx>>,
-    state: inspect::CanonicalState<TyCtxt<'tcx>, T>,
-) -> T {
-    // In case any fresh inference variables have been created between `state`
-    // and the previous instantiation, extend `orig_values` for it.
-    assert!(orig_values.len() <= state.value.var_values.len());
-    for i in orig_values.len()..state.value.var_values.len() {
-        let unconstrained = match state.value.var_values.var_values[i].unpack() {
-            ty::GenericArgKind::Lifetime(_) => {
-                infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)).into()
-            }
-            ty::GenericArgKind::Type(_) => infcx.next_ty_var(span).into(),
-            ty::GenericArgKind::Const(_) => infcx.next_const_var(span).into(),
-        };
-
-        orig_values.push(unconstrained);
-    }
-
-    let instantiation =
-        EvalCtxt::compute_query_response_instantiation_values(infcx, orig_values, &state);
-
-    let inspect::State { var_values, data } = state.instantiate(infcx.tcx, &instantiation);
-
-    EvalCtxt::unify_query_var_values(infcx, param_env, orig_values, var_values);
-    data
-}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index c1fca092a08..6d0fee955b9 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -1,41 +1,29 @@
+use std::ops::ControlFlow;
+
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
-use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals};
-use rustc_infer::traits::ObligationCause;
-use rustc_macros::{extension, HashStable, HashStable_NoContext, TyDecodable, TyEncodable};
-use rustc_middle::bug;
-use rustc_middle::traits::solve::{
-    inspect, CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaquesData, QueryResult,
-};
-use rustc_middle::ty::AliasRelationDirection;
-use rustc_middle::ty::TypeFolder;
-use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
-use rustc_next_trait_solver::infcx::SolverDelegate as IrSolverDelegate;
-use rustc_span::DUMMY_SP;
-use rustc_type_ir::fold::TypeSuperFoldable;
+use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
+use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::relate::Relate;
-use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
-use rustc_type_ir::{self as ir, CanonicalVarValues, Interner};
+use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
+use rustc_type_ir::{self as ty, CanonicalVarValues, Interner};
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
-use std::ops::ControlFlow;
-
-use crate::solve::infcx::SolverDelegate;
-use crate::traits::coherence;
 
-use super::inspect::ProofTreeBuilder;
-use super::{search_graph, GoalEvaluationKind, FIXPOINT_STEP_LIMIT};
-use super::{search_graph::SearchGraph, Goal};
-use super::{GoalSource, SolverMode};
+use crate::infcx::SolverDelegate;
+use crate::solve::inspect::{self, ProofTreeBuilder};
+use crate::solve::search_graph::SearchGraph;
+use crate::solve::{
+    search_graph, CanonicalInput, CanonicalResponse, Certainty, Goal, GoalEvaluationKind,
+    GoalSource, MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData,
+    QueryResult, SolverMode, FIXPOINT_STEP_LIMIT,
+};
 
 pub(super) mod canonical;
 mod probe;
 
-pub struct EvalCtxt<'a, Infcx, I = <Infcx as IrSolverDelegate>::Interner>
+pub struct EvalCtxt<'a, Infcx, I = <Infcx as SolverDelegate>::Interner>
 where
-    Infcx: IrSolverDelegate<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     /// The inference context that backs (mostly) inference and placeholder terms
@@ -112,9 +100,9 @@ pub struct NestedGoals<I: Interner> {
     ///
     /// Forgetting to replace the RHS with a fresh inference variable when we evaluate
     /// this goal results in an ICE..
-    pub normalizes_to_goals: Vec<ir::solve::Goal<I, ir::NormalizesTo<I>>>,
+    pub normalizes_to_goals: Vec<Goal<I, ty::NormalizesTo<I>>>,
     /// The rest of the goals which have not yet processed or remain ambiguous.
-    pub goals: Vec<(GoalSource, ir::solve::Goal<I, I::Predicate>)>,
+    pub goals: Vec<(GoalSource, Goal<I, I::Predicate>)>,
 }
 
 impl<I: Interner> NestedGoals<I> {
@@ -127,14 +115,25 @@ impl<I: Interner> NestedGoals<I> {
     }
 }
 
-#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+#[derive(PartialEq, Eq, Debug, Hash, HashStable_NoContext, Clone, Copy)]
 pub enum GenerateProofTree {
     Yes,
     No,
 }
 
-#[extension(pub trait InferCtxtEvalExt<'tcx>)]
-impl<'tcx> InferCtxt<'tcx> {
+pub trait SolverDelegateEvalExt: SolverDelegate {
+    fn evaluate_root_goal(
+        &self,
+        goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
+        generate_proof_tree: GenerateProofTree,
+    ) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<Self::Interner>>);
+}
+
+impl<Infcx, I> SolverDelegateEvalExt for Infcx
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     /// Evaluates a goal from **outside** of the trait solver.
     ///
     /// Using this while inside of the solver is wrong as it uses a new
@@ -142,17 +141,20 @@ impl<'tcx> InferCtxt<'tcx> {
     #[instrument(level = "debug", skip(self))]
     fn evaluate_root_goal(
         &self,
-        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        goal: Goal<I, I::Predicate>,
         generate_proof_tree: GenerateProofTree,
-    ) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<TyCtxt<'tcx>>>)
-    {
+    ) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<I>>) {
         EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
             ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal)
         })
     }
 }
 
-impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
+impl<'a, Infcx, I> EvalCtxt<'a, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     pub(super) fn solver_mode(&self) -> SolverMode {
         self.search_graph.solver_mode()
     }
@@ -163,17 +165,16 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
 
     /// 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`]).
+    /// over using this manually (such as [`SolverDelegateEvalExt::evaluate_root_goal`]).
     pub(super) fn enter_root<R>(
-        infcx: &InferCtxt<'tcx>,
+        infcx: &Infcx,
         generate_proof_tree: GenerateProofTree,
-        f: impl FnOnce(&mut EvalCtxt<'_, SolverDelegate<'tcx>>) -> R,
-    ) -> (R, Option<inspect::GoalEvaluation<TyCtxt<'tcx>>>) {
-        let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
-        let mut search_graph = search_graph::SearchGraph::new(mode);
+        f: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> R,
+    ) -> (R, Option<inspect::GoalEvaluation<I>>) {
+        let mut search_graph = search_graph::SearchGraph::new(infcx.solver_mode());
 
         let mut ecx = EvalCtxt {
-            infcx: <&SolverDelegate<'tcx>>::from(infcx),
+            infcx,
             search_graph: &mut search_graph,
             nested_goals: NestedGoals::new(),
             inspect: ProofTreeBuilder::new_maybe_root(generate_proof_tree),
@@ -181,10 +182,10 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
             // Only relevant when canonicalizing the response,
             // which we don't do within this evaluation context.
             predefined_opaques_in_body: infcx
-                .tcx
+                .interner()
                 .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
             max_input_universe: ty::UniverseIndex::ROOT,
-            variables: ty::List::empty(),
+            variables: Default::default(),
             var_values: CanonicalVarValues::dummy(),
             is_normalizes_to_goal: false,
             tainted: Ok(()),
@@ -210,24 +211,17 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
     /// 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<TyCtxt<'tcx>>,
-        canonical_input: CanonicalInput<'tcx>,
-        canonical_goal_evaluation: &mut ProofTreeBuilder<SolverDelegate<'tcx>>,
-        f: impl FnOnce(&mut EvalCtxt<'_, SolverDelegate<'tcx>>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
+        tcx: I,
+        search_graph: &'a mut search_graph::SearchGraph<I>,
+        canonical_input: CanonicalInput<I>,
+        canonical_goal_evaluation: &mut ProofTreeBuilder<Infcx>,
+        f: impl FnOnce(&mut EvalCtxt<'_, Infcx>, Goal<I, I::Predicate>) -> 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)
-            .build_with_canonical(DUMMY_SP, &canonical_input);
+        let (ref infcx, input, var_values) =
+            SolverDelegate::build_with_canonical(tcx, search_graph.solver_mode(), &canonical_input);
 
         let mut ecx = EvalCtxt {
-            infcx: <&SolverDelegate<'tcx>>::from(infcx),
+            infcx,
             variables: canonical_input.variables,
             var_values,
             is_normalizes_to_goal: false,
@@ -240,8 +234,7 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
         };
 
         for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
-            let hidden_ty = ty::OpaqueHiddenType { ty, span: DUMMY_SP };
-            ecx.infcx.inject_new_hidden_type_unchecked(key, hidden_ty);
+            ecx.infcx.inject_new_hidden_type_unchecked(key, ty);
         }
 
         if !ecx.nested_goals.is_empty() {
@@ -256,7 +249,8 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
         // 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.
-        let _ = infcx.take_opaque_types();
+        // FIXME: Could we make `build_with_canonical` into `enter_with_canonical` and call this at the end?
+        infcx.reset_opaque_types();
 
         result
     }
@@ -268,15 +262,15 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
     /// 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
+    /// if you're inside of the solver or [SolverDelegateEvalExt::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<TyCtxt<'tcx>>,
-        canonical_input: CanonicalInput<'tcx>,
-        goal_evaluation: &mut ProofTreeBuilder<SolverDelegate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        tcx: I,
+        search_graph: &'a mut search_graph::SearchGraph<I>,
+        canonical_input: CanonicalInput<I>,
+        goal_evaluation: &mut ProofTreeBuilder<Infcx>,
+    ) -> QueryResult<I> {
         let mut canonical_goal_evaluation =
             goal_evaluation.new_canonical_goal_evaluation(canonical_input);
 
@@ -315,7 +309,7 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
         &mut self,
         goal_evaluation_kind: GoalEvaluationKind,
         source: GoalSource,
-        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        goal: Goal<I, I::Predicate>,
     ) -> Result<(bool, Certainty), NoSolution> {
         let (normalization_nested_goals, has_changed, certainty) =
             self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?;
@@ -336,8 +330,8 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
         &mut self,
         goal_evaluation_kind: GoalEvaluationKind,
         _source: GoalSource,
-        goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    ) -> Result<(NestedNormalizationGoals<TyCtxt<'tcx>>, bool, Certainty), NoSolution> {
+        goal: Goal<I, I::Predicate>,
+    ) -> Result<(NestedNormalizationGoals<I>, bool, Certainty), 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);
@@ -377,10 +371,10 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
 
     fn instantiate_response_discarding_overflow(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        original_values: Vec<ty::GenericArg<'tcx>>,
-        response: CanonicalResponse<'tcx>,
-    ) -> (NestedNormalizationGoals<TyCtxt<'tcx>>, Certainty, bool) {
+        param_env: I::ParamEnv,
+        original_values: Vec<I::GenericArg>,
+        response: CanonicalResponse<I>,
+    ) -> (NestedNormalizationGoals<I>, Certainty, bool) {
         if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty {
             return (NestedNormalizationGoals::empty(), response.value.certainty, false);
         }
@@ -393,7 +387,7 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
         (normalization_nested_goals, certainty, has_changed)
     }
 
-    fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
+    fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> {
         let Goal { param_env, predicate } = goal;
         let kind = predicate.kind();
         if let Some(kind) = kind.no_bound_vars() {
@@ -429,7 +423,7 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
                     self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct })
                 }
                 ty::PredicateKind::ConstEquate(_, _) => {
-                    bug!("ConstEquate should not be emitted when `-Znext-solver` is active")
+                    panic!("ConstEquate should not be emitted when `-Znext-solver` is active")
                 }
                 ty::PredicateKind::NormalizesTo(predicate) => {
                     self.compute_normalizes_to_goal(Goal { param_env, predicate })
@@ -565,21 +559,16 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
     }
 
     /// Record impl args in the proof tree for later access by `InspectCandidate`.
-    pub(crate) fn record_impl_args(&mut self, impl_args: ty::GenericArgsRef<'tcx>) {
+    pub(crate) fn record_impl_args(&mut self, impl_args: I::GenericArgs) {
         self.inspect.record_impl_args(self.infcx, self.max_input_universe, impl_args)
     }
-}
 
-impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     pub(super) fn interner(&self) -> I {
         self.infcx.interner()
     }
 
     #[instrument(level = "trace", skip(self))]
-    pub(super) fn add_normalizes_to_goal(
-        &mut self,
-        mut goal: ir::solve::Goal<I, ir::NormalizesTo<I>>,
-    ) {
+    pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal<I, ty::NormalizesTo<I>>) {
         goal.predicate = goal
             .predicate
             .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
@@ -588,11 +577,7 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     }
 
     #[instrument(level = "debug", skip(self))]
-    pub(super) fn add_goal(
-        &mut self,
-        source: GoalSource,
-        mut goal: ir::solve::Goal<I, I::Predicate>,
-    ) {
+    pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal<I, I::Predicate>) {
         goal.predicate = goal
             .predicate
             .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
@@ -604,7 +589,7 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     pub(super) fn add_goals(
         &mut self,
         source: GoalSource,
-        goals: impl IntoIterator<Item = ir::solve::Goal<I, I::Predicate>>,
+        goals: impl IntoIterator<Item = Goal<I, I::Predicate>>,
     ) {
         for goal in goals {
             self.add_goal(source, goal);
@@ -627,8 +612,8 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     /// If `kind` is an integer inference variable this will still return a ty infer var.
     pub(super) fn next_term_infer_of_kind(&mut self, kind: I::Term) -> I::Term {
         match kind.kind() {
-            ir::TermKind::Ty(_) => self.next_ty_infer().into(),
-            ir::TermKind::Const(_) => self.next_const_infer().into(),
+            ty::TermKind::Ty(_) => self.next_ty_infer().into(),
+            ty::TermKind::Const(_) => self.next_const_infer().into(),
         }
     }
 
@@ -637,20 +622,17 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     /// This is the case if the `term` does not occur in any other part of the predicate
     /// and is able to name all other placeholder and inference variables.
     #[instrument(level = "trace", skip(self), ret)]
-    pub(super) fn term_is_fully_unconstrained(
-        &self,
-        goal: ir::solve::Goal<I, ir::NormalizesTo<I>>,
-    ) -> bool {
+    pub(super) fn term_is_fully_unconstrained(&self, goal: Goal<I, ty::NormalizesTo<I>>) -> bool {
         let universe_of_term = match goal.predicate.term.kind() {
-            ir::TermKind::Ty(ty) => {
-                if let ir::Infer(ir::TyVar(vid)) = ty.kind() {
+            ty::TermKind::Ty(ty) => {
+                if let ty::Infer(ty::TyVar(vid)) = ty.kind() {
                     self.infcx.universe_of_ty(vid).unwrap()
                 } else {
                     return false;
                 }
             }
-            ir::TermKind::Const(ct) => {
-                if let ir::ConstKind::Infer(ir::InferConst::Var(vid)) = ct.kind() {
+            ty::TermKind::Const(ct) => {
+                if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() {
                     self.infcx.universe_of_ct(vid).unwrap()
                 } else {
                     return false;
@@ -658,14 +640,14 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
             }
         };
 
-        struct ContainsTermOrNotNameable<'a, Infcx: IrSolverDelegate<Interner = I>, I: Interner> {
+        struct ContainsTermOrNotNameable<'a, Infcx: SolverDelegate<Interner = I>, I: Interner> {
             term: I::Term,
-            universe_of_term: ir::UniverseIndex,
+            universe_of_term: ty::UniverseIndex,
             infcx: &'a Infcx,
         }
 
-        impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> ContainsTermOrNotNameable<'_, Infcx, I> {
-            fn check_nameable(&self, universe: ir::UniverseIndex) -> ControlFlow<()> {
+        impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ContainsTermOrNotNameable<'_, Infcx, I> {
+            fn check_nameable(&self, universe: ty::UniverseIndex) -> ControlFlow<()> {
                 if self.universe_of_term.can_name(universe) {
                     ControlFlow::Continue(())
                 } else {
@@ -674,15 +656,15 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
             }
         }
 
-        impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> TypeVisitor<I>
+        impl<Infcx: SolverDelegate<Interner = I>, I: Interner> TypeVisitor<I>
             for ContainsTermOrNotNameable<'_, Infcx, I>
         {
             type Result = ControlFlow<()>;
             fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
                 match t.kind() {
-                    ir::Infer(ir::TyVar(vid)) => {
-                        if let ir::TermKind::Ty(term) = self.term.kind()
-                            && let ir::Infer(ir::TyVar(term_vid)) = term.kind()
+                    ty::Infer(ty::TyVar(vid)) => {
+                        if let ty::TermKind::Ty(term) = self.term.kind()
+                            && let ty::Infer(ty::TyVar(term_vid)) = term.kind()
                             && self.infcx.root_ty_var(vid) == self.infcx.root_ty_var(term_vid)
                         {
                             ControlFlow::Break(())
@@ -690,7 +672,7 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
                             self.check_nameable(self.infcx.universe_of_ty(vid).unwrap())
                         }
                     }
-                    ir::Placeholder(p) => self.check_nameable(p.universe()),
+                    ty::Placeholder(p) => self.check_nameable(p.universe()),
                     _ => {
                         if t.has_non_region_infer() || t.has_placeholders() {
                             t.super_visit_with(self)
@@ -703,9 +685,9 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
 
             fn visit_const(&mut self, c: I::Const) -> Self::Result {
                 match c.kind() {
-                    ir::ConstKind::Infer(ir::InferConst::Var(vid)) => {
-                        if let ir::TermKind::Const(term) = self.term.kind()
-                            && let ir::ConstKind::Infer(ir::InferConst::Var(term_vid)) = term.kind()
+                    ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
+                        if let ty::TermKind::Const(term) = self.term.kind()
+                            && 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(())
@@ -713,7 +695,7 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
                             self.check_nameable(self.infcx.universe_of_ct(vid).unwrap())
                         }
                     }
-                    ir::ConstKind::Placeholder(p) => self.check_nameable(p.universe()),
+                    ty::ConstKind::Placeholder(p) => self.check_nameable(p.universe()),
                     _ => {
                         if c.has_non_region_infer() || c.has_placeholders() {
                             c.super_visit_with(self)
@@ -741,7 +723,7 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         lhs: T,
         rhs: T,
     ) -> Result<(), NoSolution> {
-        self.relate(param_env, lhs, ir::Variance::Invariant, rhs)
+        self.relate(param_env, lhs, ty::Variance::Invariant, rhs)
     }
 
     /// This should be used when relating a rigid alias with another type.
@@ -753,8 +735,8 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     pub(super) fn relate_rigid_alias_non_alias(
         &mut self,
         param_env: I::ParamEnv,
-        alias: ir::AliasTerm<I>,
-        variance: ir::Variance,
+        alias: ty::AliasTerm<I>,
+        variance: ty::Variance,
         term: I::Term,
     ) -> Result<(), NoSolution> {
         // NOTE: this check is purely an optimization, the structural eq would
@@ -770,7 +752,7 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
             // Alternatively we could modify `Equate` for this case by adding another
             // variant to `StructurallyRelateAliases`.
             let identity_args = self.fresh_args_for_item(alias.def_id);
-            let rigid_ctor = ir::AliasTerm::new(tcx, alias.def_id, identity_args);
+            let rigid_ctor = ty::AliasTerm::new(tcx, alias.def_id, identity_args);
             let ctor_term = rigid_ctor.to_term(tcx);
             let obligations =
                 self.infcx.eq_structurally_relating_aliases(param_env, term, ctor_term)?;
@@ -803,7 +785,7 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         sub: T,
         sup: T,
     ) -> Result<(), NoSolution> {
-        self.relate(param_env, sub, ir::Variance::Covariant, sup)
+        self.relate(param_env, sub, ty::Variance::Covariant, sup)
     }
 
     #[instrument(level = "trace", skip(self, param_env), ret)]
@@ -811,7 +793,7 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         &mut self,
         param_env: I::ParamEnv,
         lhs: T,
-        variance: ir::Variance,
+        variance: ty::Variance,
         rhs: T,
     ) -> Result<(), NoSolution> {
         let goals = self.infcx.relate(param_env, lhs, variance, rhs)?;
@@ -830,20 +812,20 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         param_env: I::ParamEnv,
         lhs: T,
         rhs: T,
-    ) -> Result<Vec<ir::solve::Goal<I, I::Predicate>>, NoSolution> {
-        self.infcx.relate(param_env, lhs, ir::Variance::Invariant, rhs)
+    ) -> Result<Vec<Goal<I, I::Predicate>>, NoSolution> {
+        self.infcx.relate(param_env, lhs, ty::Variance::Invariant, rhs)
     }
 
     pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<I> + Copy>(
         &self,
-        value: ir::Binder<I, T>,
+        value: ty::Binder<I, T>,
     ) -> T {
         self.infcx.instantiate_binder_with_infer(value)
     }
 
     pub(super) fn enter_forall<T: TypeFoldable<I> + Copy, U>(
         &self,
-        value: ir::Binder<I, T>,
+        value: ty::Binder<I, T>,
         f: impl FnOnce(T) -> U,
     ) -> U {
         self.infcx.enter_forall(value, f)
@@ -863,36 +845,29 @@ impl<Infcx: IrSolverDelegate<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         }
         args
     }
-}
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
-    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_ty_outlives(&self, ty: I::Ty, lt: I::Region) {
+        self.infcx.register_ty_outlives(ty, lt);
     }
 
-    pub(super) fn register_region_outlives(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) {
+    pub(super) fn register_region_outlives(&self, a: I::Region, b: I::Region) {
         // `b : a` ==> `a <= b`
-        // (inlined from `InferCtxt::region_outlives_predicate`)
-        self.infcx.sub_regions(
-            rustc_infer::infer::SubregionOrigin::RelateRegionParamBound(DUMMY_SP),
-            b,
-            a,
-        );
+        self.infcx.sub_regions(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()))
+        param_env: I::ParamEnv,
+        arg: I::GenericArg,
+    ) -> Option<Vec<Goal<I, I::Predicate>>> {
+        self.infcx.well_formed_goals(param_env, arg)
     }
 
+    /*
     pub(super) fn is_transmutable(
         &self,
-        src_and_dst: rustc_transmute::Types<'tcx>,
+        src_and_dst: rustc_transmute::Types<I>,
         assume: rustc_transmute::Assume,
     ) -> Result<Certainty, NoSolution> {
         use rustc_transmute::Answer;
@@ -906,46 +881,55 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             Answer::No(_) | Answer::If(_) => Err(NoSolution),
         }
     }
+    */
 
     pub(super) fn trait_ref_is_knowable(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        trait_ref: ty::TraitRef<'tcx>,
+        param_env: I::ParamEnv,
+        trait_ref: ty::TraitRef<I>,
     ) -> Result<bool, NoSolution> {
         let infcx = self.infcx;
         let lazily_normalize_ty = |ty| self.structurally_normalize_ty(param_env, ty);
-        coherence::trait_ref_is_knowable(infcx, trait_ref, lazily_normalize_ty)
-            .map(|is_knowable| is_knowable.is_ok())
+        infcx.trait_ref_is_knowable(trait_ref, lazily_normalize_ty)
+    }
+
+    pub(super) fn fetch_eligible_assoc_item(
+        &self,
+        param_env: I::ParamEnv,
+        goal_trait_ref: ty::TraitRef<I>,
+        trait_assoc_def_id: I::DefId,
+        impl_def_id: I::DefId,
+    ) -> Result<Option<I::DefId>, NoSolution> {
+        self.infcx.fetch_eligible_assoc_item(
+            param_env,
+            goal_trait_ref,
+            trait_assoc_def_id,
+            impl_def_id,
+        )
     }
 
-    pub(super) fn can_define_opaque_ty(&self, def_id: impl Into<DefId>) -> bool {
-        self.infcx.can_define_opaque_ty(def_id)
+    pub(super) fn can_define_opaque_ty(&self, def_id: I::LocalDefId) -> bool {
+        self.infcx.defining_opaque_types().contains(&def_id)
     }
 
     pub(super) fn insert_hidden_type(
         &mut self,
-        opaque_type_key: OpaqueTypeKey<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        hidden_ty: Ty<'tcx>,
+        opaque_type_key: ty::OpaqueTypeKey<I>,
+        param_env: I::ParamEnv,
+        hidden_ty: I::Ty,
     ) -> Result<(), NoSolution> {
         let mut goals = Vec::new();
-        self.infcx.insert_hidden_type(
-            opaque_type_key,
-            DUMMY_SP,
-            param_env,
-            hidden_ty,
-            &mut goals,
-        )?;
+        self.infcx.insert_hidden_type(opaque_type_key, param_env, hidden_ty, &mut goals)?;
         self.add_goals(GoalSource::Misc, goals);
         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>,
+        opaque_def_id: I::DefId,
+        opaque_args: I::GenericArgs,
+        param_env: I::ParamEnv,
+        hidden_ty: I::Ty,
     ) {
         let mut goals = Vec::new();
         self.infcx.add_item_bounds_for_hidden_type(
@@ -962,10 +946,10 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     // 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>> {
+        param_env: I::ParamEnv,
+        key: ty::OpaqueTypeKey<I>,
+        ty: I::Ty,
+    ) -> Vec<CanonicalResponse<I>> {
         // FIXME: Super inefficient to be cloning this...
         let opaques = self.infcx.clone_opaque_types_for_query_response();
 
@@ -984,7 +968,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                     }
                     ecx.eq(param_env, candidate_ty, ty)?;
                     ecx.add_item_bounds_for_hidden_type(
-                        candidate_key.def_id.to_def_id(),
+                        candidate_key.def_id.into(),
                         candidate_key.args,
                         param_env,
                         candidate_ty,
@@ -1001,23 +985,10 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     // as an ambiguity rather than no-solution.
     pub(super) fn try_const_eval_resolve(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
-        unevaluated: ty::UnevaluatedConst<'tcx>,
-    ) -> Option<ty::Const<'tcx>> {
-        use rustc_middle::mir::interpret::ErrorHandled;
-        match self.infcx.const_eval_resolve(param_env, unevaluated, DUMMY_SP) {
-            Ok(Some(val)) => Some(ty::Const::new_value(
-                self.interner(),
-                val,
-                self.interner()
-                    .type_of(unevaluated.def)
-                    .instantiate(self.interner(), unevaluated.args),
-            )),
-            Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None,
-            Err(ErrorHandled::Reported(e, _)) => {
-                Some(ty::Const::new_error(self.interner(), e.into()))
-            }
-        }
+        param_env: I::ParamEnv,
+        unevaluated: ty::UnevaluatedConst<I>,
+    ) -> Option<I::Const> {
+        self.infcx.try_const_eval_resolve(param_env, unevaluated)
     }
 }
 
@@ -1030,7 +1001,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
 /// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs.
 struct ReplaceAliasWithInfer<'me, 'a, Infcx, I>
 where
-    Infcx: IrSolverDelegate<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     ecx: &'me mut EvalCtxt<'a, Infcx>,
@@ -1039,7 +1010,7 @@ where
 
 impl<Infcx, I> TypeFolder<I> for ReplaceAliasWithInfer<'_, '_, Infcx, I>
 where
-    Infcx: IrSolverDelegate<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     fn interner(&self) -> I {
@@ -1048,16 +1019,16 @@ where
 
     fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
         match ty.kind() {
-            ir::Alias(..) if !ty.has_escaping_bound_vars() => {
+            ty::Alias(..) if !ty.has_escaping_bound_vars() => {
                 let infer_ty = self.ecx.next_ty_infer();
-                let normalizes_to = ir::PredicateKind::AliasRelate(
+                let normalizes_to = ty::PredicateKind::AliasRelate(
                     ty.into(),
                     infer_ty.into(),
-                    AliasRelationDirection::Equate,
+                    ty::AliasRelationDirection::Equate,
                 );
                 self.ecx.add_goal(
                     GoalSource::Misc,
-                    ir::solve::Goal::new(self.interner(), self.param_env, normalizes_to),
+                    Goal::new(self.interner(), self.param_env, normalizes_to),
                 );
                 infer_ty
             }
@@ -1067,16 +1038,16 @@ where
 
     fn fold_const(&mut self, ct: I::Const) -> I::Const {
         match ct.kind() {
-            ir::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => {
+            ty::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => {
                 let infer_ct = self.ecx.next_const_infer();
-                let normalizes_to = ir::PredicateKind::AliasRelate(
+                let normalizes_to = ty::PredicateKind::AliasRelate(
                     ct.into(),
                     infer_ct.into(),
-                    AliasRelationDirection::Equate,
+                    ty::AliasRelationDirection::Equate,
                 );
                 self.ecx.add_goal(
                     GoalSource::Misc,
-                    ir::solve::Goal::new(self.interner(), self.param_env, normalizes_to),
+                    Goal::new(self.interner(), self.param_env, normalizes_to),
                 );
                 infer_ct
             }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
index 00fe237735b..31edb635415 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
@@ -1,12 +1,11 @@
-use crate::solve::assembly::Candidate;
+use std::marker::PhantomData;
 
-use super::EvalCtxt;
-use rustc_next_trait_solver::infcx::SolverDelegate;
-use rustc_next_trait_solver::solve::{
-    inspect, BuiltinImplSource, CandidateSource, NoSolution, QueryResult,
-};
 use rustc_type_ir::Interner;
-use std::marker::PhantomData;
+
+use crate::infcx::SolverDelegate;
+use crate::solve::assembly::Candidate;
+use crate::solve::inspect;
+use crate::solve::{BuiltinImplSource, CandidateSource, EvalCtxt, NoSolution, QueryResult};
 
 pub(in crate::solve) struct ProbeCtxt<'me, 'a, Infcx, I, F, T>
 where
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
index d750cd963de..5fbec4b28d4 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
@@ -3,17 +3,19 @@
 //! This code is *a bit* of a mess and can hopefully be
 //! mostly ignored. For a general overview of how it works,
 //! see the comment on [ProofTreeBuilder].
+
 use std::marker::PhantomData;
 use std::mem;
 
+use rustc_type_ir::{self as ty, Interner};
+
+use crate::infcx::SolverDelegate;
 use crate::solve::eval_ctxt::canonical;
-use crate::solve::{self, inspect, GenerateProofTree};
-use rustc_middle::bug;
-use rustc_next_trait_solver::infcx::SolverDelegate;
-use rustc_next_trait_solver::solve::{
-    CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult,
+use crate::solve::inspect;
+use crate::solve::{
+    CanonicalInput, Certainty, GenerateProofTree, Goal, GoalEvaluationKind, GoalSource, QueryInput,
+    QueryResult,
 };
-use rustc_type_ir::{self as ty, Interner};
 
 /// The core data structure when building proof trees.
 ///
@@ -171,7 +173,7 @@ impl<I: Interner> WipCanonicalGoalEvaluationStep<I> {
         for _ in 0..self.probe_depth {
             match current.steps.last_mut() {
                 Some(WipProbeStep::NestedProbe(p)) => current = p,
-                _ => bug!(),
+                _ => panic!(),
             }
         }
         current
@@ -294,15 +296,15 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
         &mut self,
         goal: Goal<I, I::Predicate>,
         orig_values: &[I::GenericArg],
-        kind: solve::GoalEvaluationKind,
+        kind: GoalEvaluationKind,
     ) -> ProofTreeBuilder<Infcx> {
         self.opt_nested(|| match kind {
-            solve::GoalEvaluationKind::Root => Some(WipGoalEvaluation {
+            GoalEvaluationKind::Root => Some(WipGoalEvaluation {
                 uncanonicalized_goal: goal,
                 orig_values: orig_values.to_vec(),
                 evaluation: None,
             }),
-            solve::GoalEvaluationKind::Nested => None,
+            GoalEvaluationKind::Nested => None,
         })
     }
 
@@ -414,7 +416,7 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
             Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 state.var_values.push(arg.into());
             }
-            Some(s) => bug!("tried to add var values to {s:?}"),
+            Some(s) => panic!("tried to add var values to {s:?}"),
         }
     }
 
@@ -431,7 +433,7 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 }));
                 state.probe_depth += 1;
             }
-            Some(s) => bug!("tried to start probe to {s:?}"),
+            Some(s) => panic!("tried to start probe to {s:?}"),
         }
     }
 
@@ -442,7 +444,7 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 let prev = state.current_evaluation_scope().kind.replace(probe_kind);
                 assert_eq!(prev, None);
             }
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -459,7 +461,7 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 let prev = state.current_evaluation_scope().final_state.replace(final_state);
                 assert_eq!(prev, None);
             }
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -495,7 +497,7 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 );
                 state.current_evaluation_scope().steps.push(WipProbeStep::AddGoal(source, goal))
             }
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -519,7 +521,7 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                     .push(WipProbeStep::RecordImplArgs { impl_args });
             }
             None => {}
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -532,7 +534,7 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                     .push(WipProbeStep::MakeCanonicalResponse { shallow_certainty });
             }
             None => {}
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -545,7 +547,7 @@ impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 state.var_values.truncate(num_var_values);
                 state.probe_depth -= 1;
             }
-            _ => bug!(),
+            _ => panic!(),
         }
 
         self
diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs
new file mode 100644
index 00000000000..65f32f1947f
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs
@@ -0,0 +1,4 @@
+pub use rustc_type_ir::solve::inspect::*;
+
+mod build;
+pub(in crate::solve) use build::*;
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index 3b43c0e3e70..6c05394504f 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -14,40 +14,22 @@
 //! FIXME(@lcnr): Write that section. If you read this before then ask me
 //! about it on zulip.
 
-use self::infcx::SolverDelegate;
-use rustc_hir::def_id::DefId;
-use rustc_infer::infer::canonical::Canonical;
-use rustc_infer::traits::query::NoSolution;
-use rustc_macros::extension;
-use rustc_middle::bug;
-use rustc_middle::traits::solve::{
-    CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, QueryResult, Response,
-};
-use rustc_middle::ty::{
-    self, AliasRelationDirection, CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, Ty,
-    TyCtxt, TypeOutlivesPredicate, UniverseIndex,
-};
-use rustc_type_ir::solve::SolverMode;
-use rustc_type_ir::{self as ir, Interner};
-
 mod alias_relate;
 mod assembly;
 mod eval_ctxt;
-mod fulfill;
-mod infcx;
 pub mod inspect;
-mod normalize;
 mod normalizes_to;
 mod project_goals;
 mod search_graph;
-mod select;
 mod trait_goals;
 
-pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt};
-pub use fulfill::{FulfillmentCtxt, NextSolverError};
-pub(crate) use normalize::deeply_normalize_for_diagnostics;
-pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
-pub use select::InferCtxtSelectExt;
+pub use self::eval_ctxt::{EvalCtxt, GenerateProofTree, SolverDelegateEvalExt};
+pub use rustc_type_ir::solve::*;
+
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::{self as ty, Interner};
+
+use crate::infcx::SolverDelegate;
 
 /// How many fixpoint iterations we should attempt inside of the solver before bailing
 /// with overflow.
@@ -66,21 +48,24 @@ enum GoalEvaluationKind {
     Nested,
 }
 
-#[extension(trait CanonicalResponseExt)]
-impl<'tcx> Canonical<'tcx, Response<TyCtxt<'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()
-    }
+fn has_no_inference_or_external_constraints<I: Interner>(
+    response: ty::Canonical<I, Response<I>>,
+) -> bool {
+    response.value.external_constraints.region_constraints.is_empty()
+        && response.value.var_values.is_identity()
+        && response.value.external_constraints.opaque_types.is_empty()
 }
 
-impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
+impl<'a, Infcx, I> EvalCtxt<'a, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", skip(self))]
     fn compute_type_outlives_goal(
         &mut self,
-        goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::OutlivesPredicate<I, I::Ty>>,
+    ) -> QueryResult<I> {
         let ty::OutlivesPredicate(ty, lt) = goal.predicate;
         self.register_ty_outlives(ty, lt);
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
@@ -89,21 +74,18 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
     #[instrument(level = "trace", skip(self))]
     fn compute_region_outlives_goal(
         &mut self,
-        goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::OutlivesPredicate<I, I::Region>>,
+    ) -> QueryResult<I> {
         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 = "trace", skip(self))]
-    fn compute_coerce_goal(
-        &mut self,
-        goal: Goal<'tcx, CoercePredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+    fn compute_coerce_goal(&mut self, goal: Goal<I, ty::CoercePredicate<I>>) -> QueryResult<I> {
         self.compute_subtype_goal(Goal {
             param_env: goal.param_env,
-            predicate: SubtypePredicate {
+            predicate: ty::SubtypePredicate {
                 a_is_expected: false,
                 a: goal.predicate.a,
                 b: goal.predicate.b,
@@ -112,10 +94,7 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn compute_subtype_goal(
-        &mut self,
-        goal: Goal<'tcx, SubtypePredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+    fn compute_subtype_goal(&mut self, goal: Goal<I, ty::SubtypePredicate<I>>) -> QueryResult<I> {
         if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
             self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
         } else {
@@ -124,8 +103,8 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
         }
     }
 
-    fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
-        if self.interner().is_object_safe(trait_def_id) {
+    fn compute_object_safe_goal(&mut self, trait_def_id: I::DefId) -> QueryResult<I> {
+        if self.interner().trait_is_object_safe(trait_def_id) {
             self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         } else {
             Err(NoSolution)
@@ -133,10 +112,7 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn compute_well_formed_goal(
-        &mut self,
-        goal: Goal<'tcx, ty::GenericArg<'tcx>>,
-    ) -> QueryResult<'tcx> {
+    fn compute_well_formed_goal(&mut self, goal: Goal<I, I::GenericArg>) -> QueryResult<I> {
         match self.well_formed_goals(goal.param_env, goal.predicate) {
             Some(goals) => {
                 self.add_goals(GoalSource::Misc, goals);
@@ -149,8 +125,8 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
     #[instrument(level = "trace", skip(self))]
     fn compute_const_evaluatable_goal(
         &mut self,
-        Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        Goal { param_env, predicate: ct }: Goal<I, I::Const>,
+    ) -> QueryResult<I> {
         match ct.kind() {
             ty::ConstKind::Unevaluated(uv) => {
                 // We never return `NoSolution` here as `try_const_eval_resolve` emits an
@@ -180,7 +156,7 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
             // - `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)
+                panic!("unexpect const kind: {:?}", ct)
             }
         }
     }
@@ -188,8 +164,8 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
     #[instrument(level = "trace", skip(self), ret)]
     fn compute_const_arg_has_type_goal(
         &mut self,
-        goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, (I::Const, I::Ty)>,
+    ) -> QueryResult<I> {
         let (ct, ty) = goal.predicate;
 
         let ct_ty = match ct.kind() {
@@ -206,7 +182,7 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
                 return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
             }
             ty::ConstKind::Unevaluated(uv) => {
-                self.interner().type_of(uv.def).instantiate(self.interner(), uv.args)
+                self.interner().type_of(uv.def).instantiate(self.interner(), &uv.args)
             }
             ty::ConstKind::Expr(_) => unimplemented!(
                 "`feature(generic_const_exprs)` is not supported in the new trait solver"
@@ -214,10 +190,10 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
             ty::ConstKind::Param(_) => {
                 unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`")
             }
-            ty::ConstKind::Bound(_, _) => bug!("escaping bound vars in {:?}", ct),
+            ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct),
             ty::ConstKind::Value(ty, _) => ty,
             ty::ConstKind::Placeholder(placeholder) => {
-                placeholder.find_const_ty_from_env(goal.param_env)
+                self.interner().find_const_ty_from_env(goal.param_env, placeholder)
             }
         };
 
@@ -226,15 +202,19 @@ impl<'a, 'tcx> EvalCtxt<'a, SolverDelegate<'tcx>> {
     }
 }
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     /// 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 = "trace", skip(self), ret)]
     fn try_merge_responses(
         &mut self,
-        responses: &[CanonicalResponse<'tcx>],
-    ) -> Option<CanonicalResponse<'tcx>> {
+        responses: &[CanonicalResponse<I>],
+    ) -> Option<CanonicalResponse<I>> {
         if responses.is_empty() {
             return None;
         }
@@ -250,14 +230,14 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             .iter()
             .find(|response| {
                 response.value.certainty == Certainty::Yes
-                    && response.has_no_inference_or_external_constraints()
+                    && has_no_inference_or_external_constraints(**response)
             })
             .copied()
     }
 
     /// If we fail to merge responses we flounder and return overflow or ambiguity.
     #[instrument(level = "trace", skip(self), ret)]
-    fn flounder(&mut self, responses: &[CanonicalResponse<'tcx>]) -> QueryResult<'tcx> {
+    fn flounder(&mut self, responses: &[CanonicalResponse<I>]) -> QueryResult<I> {
         if responses.is_empty() {
             return Err(NoSolution);
         }
@@ -267,7 +247,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                 certainty.unify_with(response.value.certainty)
             })
         else {
-            bug!("expected flounder response to be ambiguous")
+            panic!("expected flounder response to be ambiguous")
         };
 
         Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
@@ -281,9 +261,9 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     #[instrument(level = "trace", skip(self, param_env), ret)]
     fn structurally_normalize_ty(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Result<Ty<'tcx>, NoSolution> {
+        param_env: I::ParamEnv,
+        ty: I::Ty,
+    ) -> Result<I::Ty, NoSolution> {
         if let ty::Alias(..) = ty.kind() {
             let normalized_ty = self.next_ty_infer();
             let alias_relate_goal = Goal::new(
@@ -292,7 +272,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                 ty::PredicateKind::AliasRelate(
                     ty.into(),
                     normalized_ty.into(),
-                    AliasRelationDirection::Equate,
+                    ty::AliasRelationDirection::Equate,
                 ),
             );
             self.add_goal(GoalSource::Misc, alias_relate_goal);
@@ -306,15 +286,15 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
 
 fn response_no_constraints_raw<I: Interner>(
     tcx: I,
-    max_universe: UniverseIndex,
+    max_universe: ty::UniverseIndex,
     variables: I::CanonicalVars,
     certainty: Certainty,
-) -> ir::solve::CanonicalResponse<I> {
-    ir::Canonical {
+) -> CanonicalResponse<I> {
+    ty::Canonical {
         max_universe,
         variables,
         value: Response {
-            var_values: ir::CanonicalVarValues::make_identity(tcx, variables),
+            var_values: ty::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()),
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs
index 064018e89b8..9f1917fde84 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs
@@ -1,14 +1,18 @@
-use crate::solve::infcx::SolverDelegate;
-use crate::solve::EvalCtxt;
-use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
-use rustc_middle::ty;
+use rustc_type_ir::{self as ty, Interner};
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
+
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", skip(self), ret)]
     pub(super) fn normalize_anon_const(
         &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         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),
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
index 48d45fe510d..8436f3ad484 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
@@ -5,17 +5,20 @@
 //! 2. equate the self type, and
 //! 3. instantiate and register where clauses.
 
-use crate::solve::infcx::SolverDelegate;
-use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult};
-use rustc_middle::ty;
+use rustc_type_ir::{self as ty, Interner};
 
-use crate::solve::EvalCtxt;
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult};
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     pub(super) fn normalize_inherent_associated_type(
         &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let inherent = goal.predicate.alias.expect_ty(tcx);
 
@@ -26,7 +29,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
         self.eq(
             goal.param_env,
             inherent.self_ty(),
-            tcx.type_of(impl_def_id).instantiate(tcx, impl_args),
+            tcx.type_of(impl_def_id).instantiate(tcx, &impl_args),
         )?;
 
         // Equate IAT with the RHS of the project goal
@@ -41,12 +44,11 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
         self.add_goals(
             GoalSource::Misc,
             tcx.predicates_of(inherent.def_id)
-                .instantiate(tcx, inherent_args)
-                .into_iter()
-                .map(|(pred, _)| goal.with(tcx, pred)),
+                .iter_instantiated(tcx, &inherent_args)
+                .map(|pred| goal.with(tcx, pred)),
         );
 
-        let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args);
+        let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, &inherent_args);
         self.instantiate_normalizes_to_term(goal, normalized.into());
         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_next_trait_solver/src/solve/normalizes_to/mod.rs
index 0aa10afbee7..cbc18449f0a 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -1,35 +1,32 @@
-use crate::traits::specialization_graph::{self, LeafDef, Node};
-
-use super::assembly::structural_traits::AsyncCallableRelevantTypes;
-use super::assembly::{self, structural_traits, Candidate};
-use super::infcx::SolverDelegate;
-use super::{EvalCtxt, GoalSource};
-use rustc_hir::def_id::DefId;
-use rustc_hir::LangItem;
-use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::inspect::ProbeKind;
-use rustc_infer::traits::solve::MaybeCause;
-use rustc_infer::traits::Reveal;
-use rustc_middle::bug;
-use rustc_middle::traits::solve::{CandidateSource, 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::{TypeVisitableExt, Upcast};
-use rustc_span::{ErrorGuaranteed, DUMMY_SP};
-
 mod anon_const;
 mod inherent;
 mod opaque_types;
 mod weak_types;
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::Upcast as _;
+use rustc_type_ir::{self as ty, Interner, NormalizesTo};
+
+use crate::infcx::SolverDelegate;
+use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
+use crate::solve::assembly::{self, Candidate};
+use crate::solve::inspect::ProbeKind;
+use crate::solve::{
+    BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
+    NoSolution, QueryResult,
+};
+
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", skip(self), ret)]
     pub(super) fn compute_normalizes_to_goal(
         &mut self,
-        goal: Goal<'tcx, NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         self.set_is_normalizes_to_goal();
         debug_assert!(self.term_is_fully_unconstrained(goal));
         let normalize_result = self
@@ -49,10 +46,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// Normalize the given alias by at least one step. If the alias is rigid, this
     /// returns `NoSolution`.
     #[instrument(level = "trace", skip(self), ret)]
-    fn normalize_at_least_one_step(
-        &mut self,
-        goal: Goal<'tcx, NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+    fn normalize_at_least_one_step(&mut self, goal: Goal<I, NormalizesTo<I>>) -> QueryResult<I> {
         match goal.predicate.alias.kind(self.interner()) {
             ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
                 let candidates = self.assemble_and_evaluate_candidates(goal);
@@ -72,38 +66,42 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// emit nested `AliasRelate` goals to structurally normalize the alias.
     pub fn instantiate_normalizes_to_term(
         &mut self,
-        goal: Goal<'tcx, NormalizesTo<'tcx>>,
-        term: ty::Term<'tcx>,
+        goal: Goal<I, NormalizesTo<I>>,
+        term: I::Term,
     ) {
         self.eq(goal.param_env, goal.predicate.term, term)
             .expect("expected goal term to be fully unconstrained");
     }
 }
 
-impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
-    fn self_ty(self) -> Ty<'tcx> {
+impl<Infcx, I> assembly::GoalKind<Infcx> for NormalizesTo<I>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
+    fn self_ty(self) -> I::Ty {
         self.self_ty()
     }
 
-    fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
+    fn trait_ref(self, tcx: I) -> ty::TraitRef<I> {
         self.alias.trait_ref(tcx)
     }
 
-    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self {
         self.with_self_ty(tcx, self_ty)
     }
 
-    fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
+    fn trait_def_id(self, tcx: I) -> I::DefId {
         self.trait_def_id(tcx)
     }
 
     fn probe_and_match_goal_against_assumption(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        source: CandidateSource<'tcx>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-        then: impl FnOnce(&mut EvalCtxt<'_, SolverDelegate<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+        then: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult<I>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if let Some(projection_pred) = assumption.as_projection_clause() {
             if projection_pred.projection_def_id() == goal.predicate.def_id() {
                 let tcx = ecx.interner();
@@ -121,9 +119,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                     // 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)),
+                        tcx.own_predicates_of(goal.predicate.def_id())
+                            .iter_instantiated(tcx, &goal.predicate.alias.args)
+                            .map(|pred| goal.with(tcx, pred)),
                     );
 
                     then(ecx)
@@ -137,24 +135,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_impl_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, NormalizesTo<'tcx>>,
-        impl_def_id: DefId,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, NormalizesTo<I>>,
+        impl_def_id: I::DefId,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
 
         let goal_trait_ref = goal.predicate.alias.trait_ref(tcx);
-        let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
-        let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
-        if !drcx.args_may_unify(
-            goal.predicate.trait_ref(tcx).args,
-            impl_trait_header.trait_ref.skip_binder().args,
+        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
+        if !ecx.interner().args_may_unify_deep(
+            goal.predicate.alias.trait_ref(tcx).args,
+            impl_trait_ref.skip_binder().args,
         ) {
             return Err(NoSolution);
         }
 
         // We have to ignore negative impls when projecting.
-        let impl_polarity = impl_trait_header.polarity;
+        let impl_polarity = tcx.impl_polarity(impl_def_id);
         match impl_polarity {
             ty::ImplPolarity::Negative => return Err(NoSolution),
             ty::ImplPolarity::Reservation => {
@@ -165,30 +162,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
         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_header.trait_ref.instantiate(tcx, impl_args);
+            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()
+                .iter_instantiated(tcx, &impl_args)
                 .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)),
+                tcx.own_predicates_of(goal.predicate.def_id())
+                    .iter_instantiated(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) = ecx.fetch_eligible_assoc_item_def(
+            let Some(target_item_def_id) = ecx.fetch_eligible_assoc_item(
                 goal.param_env,
                 goal_trait_ref,
                 goal.predicate.def_id(),
@@ -198,21 +193,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                 return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
             };
 
-            let error_response = |ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>, reason| {
-                let guar = tcx.dcx().span_delayed_bug(tcx.def_span(assoc_def.item.def_id), reason);
+            let error_response = |ecx: &mut EvalCtxt<'_, Infcx>, msg: &str| {
+                let guar = tcx.delay_bug(msg);
                 let error_term = match goal.predicate.alias.kind(tcx) {
                     ty::AliasTermKind::ProjectionTy => Ty::new_error(tcx, guar).into(),
-                    ty::AliasTermKind::ProjectionConst => ty::Const::new_error(tcx, guar).into(),
-                    kind => bug!("expected projection, found {kind:?}"),
+                    ty::AliasTermKind::ProjectionConst => Const::new_error(tcx, guar).into(),
+                    kind => panic!("expected projection, found {kind:?}"),
                 };
                 ecx.instantiate_normalizes_to_term(goal, error_term);
                 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");
+            if !tcx.has_item_definition(target_item_def_id) {
+                return error_response(ecx, "missing item");
             }
 
+            let target_container_def_id = tcx.parent(target_item_def_id);
+
             // 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>`
@@ -223,39 +220,40 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             //
             // And then map these args to the args of the defining impl of `Assoc`, going
             // from `[u32, u64]` to `[u32, i32, u64]`.
-            let associated_item_args =
-                ecx.translate_args(&assoc_def, goal, impl_def_id, impl_args, impl_trait_ref)?;
-
-            if !tcx.check_args_compatible(assoc_def.item.def_id, associated_item_args) {
-                return error_response(
-                    ecx,
-                    "associated item has mismatched generic item arguments",
-                );
+            let target_args = ecx.translate_args(
+                goal,
+                impl_def_id,
+                impl_args,
+                impl_trait_ref,
+                target_container_def_id,
+            )?;
+
+            if !tcx.check_args_compatible(target_item_def_id, target_args) {
+                return error_response(ecx, "associated item has mismatched arguments");
             }
 
             // Finally we construct the actual value of the associated type.
             let term = match goal.predicate.alias.kind(tcx) {
                 ty::AliasTermKind::ProjectionTy => {
-                    tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into())
+                    tcx.type_of(target_item_def_id).map_bound(|ty| ty.into())
                 }
                 ty::AliasTermKind::ProjectionConst => {
-                    if tcx.features().associated_const_equality {
-                        bug!("associated const projection is not supported yet")
+                    if tcx.features().associated_const_equality() {
+                        panic!("associated const projection is not supported yet")
                     } else {
                         ty::EarlyBinder::bind(
-                            ty::Const::new_error_with_message(
+                            Const::new_error_with_message(
                                 tcx,
-                                DUMMY_SP,
                                 "associated const projection is not supported yet",
                             )
                             .into(),
                         )
                     }
                 }
-                kind => bug!("expected projection, found {kind:?}"),
+                kind => panic!("expected projection, found {kind:?}"),
             };
 
-            ecx.instantiate_normalizes_to_term(goal, term.instantiate(tcx, associated_item_args));
+            ecx.instantiate_normalizes_to_term(goal, term.instantiate(tcx, &target_args));
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         })
     }
@@ -263,63 +261,60 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     /// 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<'_, SolverDelegate<'tcx>>,
-        _guar: ErrorGuaranteed,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        _guar: I::ErrorGuaranteed,
+    ) -> Result<Candidate<I>, NoSolution> {
         Err(NoSolution)
     }
 
     fn consider_auto_trait_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        ecx.interner().dcx().span_delayed_bug(
-            ecx.interner().def_span(goal.predicate.def_id()),
-            "associated types not allowed on auto traits",
-        );
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        _goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        ecx.interner().delay_bug("associated types not allowed on auto traits");
         Err(NoSolution)
     }
 
     fn consider_trait_alias_candidate(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("trait aliases do not have associated types: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("trait aliases do not have associated types: {:?}", goal);
     }
 
     fn consider_builtin_sized_candidate(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`Sized` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`Sized` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_copy_clone_candidate(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_pointer_like_candidate(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`PointerLike` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`PointerLike` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_fn_ptr_trait_candidate(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`FnPtr` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`FnPtr` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
         let tupled_inputs_and_output =
             match structural_traits::extract_tupled_inputs_and_output_from_callable(
@@ -333,7 +328,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                 }
             };
         let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
-            ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output])
+            ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output])
         });
 
         let pred = tupled_inputs_and_output
@@ -359,16 +354,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_async_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
 
         let env_region = match goal_kind {
             ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2),
             // Doesn't matter what this region is
-            ty::ClosureKind::FnOnce => tcx.lifetimes.re_static,
+            ty::ClosureKind::FnOnce => Region::new_static(tcx),
         };
         let (tupled_inputs_and_output_and_coroutine, nested_preds) =
             structural_traits::extract_tupled_inputs_and_output_from_async_callable(
@@ -379,7 +374,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             )?;
         let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
             |AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| {
-                ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output_ty])
+                ty::TraitRef::new(
+                    tcx,
+                    tcx.require_lang_item(TraitSolverLangItem::Sized),
+                    [output_ty],
+                )
             },
         );
 
@@ -391,7 +390,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                      coroutine_return_ty,
                  }| {
                     let (projection_term, term) = if tcx
-                        .is_lang_item(goal.predicate.def_id(), LangItem::CallOnceFuture)
+                        .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallOnceFuture)
                     {
                         (
                             ty::AliasTerm::new(
@@ -401,34 +400,41 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                             ),
                             output_coroutine_ty.into(),
                         )
-                    } else if tcx.is_lang_item(goal.predicate.def_id(), LangItem::CallRefFuture) {
+                    } else if tcx
+                        .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallRefFuture)
+                    {
                         (
                             ty::AliasTerm::new(
                                 tcx,
                                 goal.predicate.def_id(),
                                 [
-                                    ty::GenericArg::from(goal.predicate.self_ty()),
+                                    I::GenericArg::from(goal.predicate.self_ty()),
                                     tupled_inputs_ty.into(),
                                     env_region.into(),
                                 ],
                             ),
                             output_coroutine_ty.into(),
                         )
-                    } else if tcx.is_lang_item(goal.predicate.def_id(), LangItem::AsyncFnOnceOutput)
-                    {
+                    } else if tcx.is_lang_item(
+                        goal.predicate.def_id(),
+                        TraitSolverLangItem::AsyncFnOnceOutput,
+                    ) {
                         (
                             ty::AliasTerm::new(
                                 tcx,
                                 goal.predicate.def_id(),
                                 [
-                                    ty::GenericArg::from(goal.predicate.self_ty()),
+                                    I::GenericArg::from(goal.predicate.self_ty()),
                                     tupled_inputs_ty.into(),
                                 ],
                             ),
                             coroutine_return_ty.into(),
                         )
                     } else {
-                        bug!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id())
+                        panic!(
+                            "no such associated type in `AsyncFn*`: {:?}",
+                            goal.predicate.def_id()
+                        )
                     };
                     ty::ProjectionPredicate { projection_term, term }
                 },
@@ -450,9 +456,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_async_fn_kind_helper_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let [
             closure_fn_kind_ty,
             goal_kind_ty,
@@ -462,7 +468,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             coroutine_captures_by_ref_ty,
         ] = **goal.predicate.alias.args
         else {
-            bug!();
+            panic!();
         };
 
         // Bail if the upvars haven't been constrained.
@@ -497,18 +503,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_tuple_candidate(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`Tuple` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`Tuple` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_pointee_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
-        let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
+        let metadata_def_id = tcx.require_lang_item(TraitSolverLangItem::Metadata);
         assert_eq!(metadata_def_id, goal.predicate.def_id());
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             let metadata_ty = match goal.predicate.self_ty().kind() {
@@ -530,16 +536,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                 | ty::CoroutineWitness(..)
                 | ty::Never
                 | ty::Foreign(..)
-                | ty::Dynamic(_, _, ty::DynStar) => tcx.types.unit,
+                | ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(tcx),
 
-                ty::Error(e) => Ty::new_error(tcx, *e),
+                ty::Error(e) => Ty::new_error(tcx, e),
 
-                ty::Str | ty::Slice(_) => tcx.types.usize,
+                ty::Str | ty::Slice(_) => Ty::new_usize(tcx),
 
                 ty::Dynamic(_, _, ty::Dyn) => {
-                    let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
+                    let dyn_metadata = tcx.require_lang_item(TraitSolverLangItem::DynMetadata);
                     tcx.type_of(dyn_metadata)
-                        .instantiate(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
+                        .instantiate(tcx, &[I::GenericArg::from(goal.predicate.self_ty())])
                 }
 
                 ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
@@ -549,32 +555,31 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                     // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
                     let sized_predicate = ty::TraitRef::new(
                         tcx,
-                        tcx.require_lang_item(LangItem::Sized, None),
-                        [ty::GenericArg::from(goal.predicate.self_ty())],
+                        tcx.require_lang_item(TraitSolverLangItem::Sized),
+                        [I::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::new_unit(tcx)
                 }
 
-                ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
-                    None => tcx.types.unit,
-                    Some(tail_def) => {
-                        let tail_ty = tail_def.ty(tcx, args);
-                        Ty::new_projection(tcx, metadata_def_id, [tail_ty])
+                ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(tcx) {
+                    None => Ty::new_unit(tcx),
+                    Some(tail_ty) => {
+                        Ty::new_projection(tcx, metadata_def_id, [tail_ty.instantiate(tcx, &args)])
                     }
                 },
-                ty::Adt(_, _) => tcx.types.unit,
+                ty::Adt(_, _) => Ty::new_unit(tcx),
 
                 ty::Tuple(elements) => match elements.last() {
-                    None => tcx.types.unit,
+                    None => Ty::new_unit(tcx),
                     Some(&tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]),
                 },
 
                 ty::Infer(
                     ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
                 )
-                | ty::Bound(..) => bug!(
+                | ty::Bound(..) => panic!(
                     "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
                     goal.predicate.self_ty()
                 ),
@@ -586,11 +591,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_future_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -622,11 +627,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -658,18 +663,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_fused_iterator_candidate(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`FusedIterator` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`FusedIterator` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_async_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -685,10 +690,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             // coroutine yield ty `Poll<Option<I>>`.
             let wrapped_expected_ty = Ty::new_adt(
                 tcx,
-                tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)),
+                tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Poll)),
                 tcx.mk_args(&[Ty::new_adt(
                     tcx,
-                    tcx.adt_def(tcx.require_lang_item(LangItem::Option, None)),
+                    tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Option)),
                     tcx.mk_args(&[expected_ty.into()]),
                 )
                 .into()]),
@@ -701,11 +706,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_coroutine_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -717,15 +722,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
         let coroutine = args.as_coroutine();
 
-        let term = if tcx.is_lang_item(goal.predicate.def_id(), LangItem::CoroutineReturn) {
+        let term = if tcx
+            .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineReturn)
+        {
             coroutine.return_ty().into()
-        } else if tcx.is_lang_item(goal.predicate.def_id(), LangItem::CoroutineYield) {
+        } else if tcx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineYield) {
             coroutine.yield_ty().into()
         } else {
-            bug!(
-                "unexpected associated item `<{self_ty} as Coroutine>::{}`",
-                tcx.item_name(goal.predicate.def_id())
-            )
+            panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id())
         };
 
         Self::probe_and_consider_implied_clause(
@@ -748,18 +752,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_structural_builtin_unsize_candidates(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
-        bug!("`Unsize` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Vec<Candidate<I>> {
+        panic!("`Unsize` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_discriminant_kind_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let discriminant_ty = match *self_ty.kind() {
+        let discriminant_ty = match self_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(..)
@@ -794,7 +798,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             }
 
             ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
-            | ty::Bound(..) => bug!(
+            | ty::Bound(..) => panic!(
                 "unexpected self ty `{:?}` when normalizing `<T as DiscriminantKind>::Discriminant`",
                 goal.predicate.self_ty()
             ),
@@ -807,11 +811,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_async_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let async_destructor_ty = match *self_ty.kind() {
+        let async_destructor_ty = match self_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(..)
@@ -842,12 +846,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
             ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
             | ty::Foreign(..)
-            | ty::Bound(..) => bug!(
+            | ty::Bound(..) => panic!(
                 "unexpected self ty `{:?}` when normalizing `<T as AsyncDestruct>::AsyncDestructor`",
                 goal.predicate.self_ty()
             ),
 
-            ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => bug!(
+            ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => panic!(
                 "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}"
             ),
         };
@@ -860,93 +864,56 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_destruct_candidate(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`Destruct` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`Destruct` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_transmute_candidate(
-        _ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal)
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal)
     }
 }
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     fn translate_args(
         &mut self,
-        assoc_def: &LeafDef,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-        impl_def_id: DefId,
-        impl_args: ty::GenericArgsRef<'tcx>,
-        impl_trait_ref: rustc_type_ir::TraitRef<TyCtxt<'tcx>>,
-    ) -> Result<ty::GenericArgsRef<'tcx>, NoSolution> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+        impl_def_id: I::DefId,
+        impl_args: I::GenericArgs,
+        impl_trait_ref: rustc_type_ir::TraitRef<I>,
+        target_container_def_id: I::DefId,
+    ) -> Result<I::GenericArgs, NoSolution> {
         let tcx = self.interner();
-        Ok(match assoc_def.defining_node {
-            Node::Trait(_) => goal.predicate.alias.args,
-            Node::Impl(target_impl_def_id) => {
-                if target_impl_def_id == impl_def_id {
-                    // Same impl, no need to fully translate, just a rebase from
-                    // the trait is sufficient.
-                    goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, impl_args)
-                } else {
-                    let target_args = self.fresh_args_for_item(target_impl_def_id);
-                    let target_trait_ref = tcx
-                        .impl_trait_ref(target_impl_def_id)
-                        .unwrap()
-                        .instantiate(tcx, target_args);
-                    // Relate source impl to target impl by equating trait refs.
-                    self.eq(goal.param_env, impl_trait_ref, target_trait_ref)?;
-                    // Also add predicates since they may be needed to constrain the
-                    // target impl's params.
-                    self.add_goals(
-                        GoalSource::Misc,
-                        tcx.predicates_of(target_impl_def_id)
-                            .instantiate(tcx, target_args)
-                            .into_iter()
-                            .map(|(pred, _)| goal.with(tcx, pred)),
-                    );
-                    goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, target_args)
-                }
-            }
-        })
-    }
-
-    /// 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 = "trace", skip(self, param_env), ret)]
-    fn fetch_eligible_assoc_item_def(
-        &self,
-        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(self.interner(), impl_def_id, trait_assoc_def_id)
-                .map_err(|ErrorGuaranteed { .. }| NoSolution)?;
-
-        let eligible = if node_item.is_final() {
-            // Non-specializable items are always projectable.
-            true
+        Ok(if target_container_def_id == impl_trait_ref.def_id {
+            // Default value from the trait definition. No need to rebase.
+            goal.predicate.alias.args
+        } else if target_container_def_id == impl_def_id {
+            // Same impl, no need to fully translate, just a rebase from
+            // the trait is sufficient.
+            goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, impl_args)
         } 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 = self.resolve_vars_if_possible(goal_trait_ref);
-                !poly_trait_ref.still_further_specializable()
-            } else {
-                trace!(?node_item.item.def_id, "not eligible due to default");
-                false
-            }
-        };
-
-        if eligible { Ok(Some(node_item)) } else { Ok(None) }
+            let target_args = self.fresh_args_for_item(target_container_def_id);
+            let target_trait_ref =
+                tcx.impl_trait_ref(target_container_def_id).instantiate(tcx, &target_args);
+            // Relate source impl to target impl by equating trait refs.
+            self.eq(goal.param_env, impl_trait_ref, target_trait_ref)?;
+            // Also add predicates since they may be needed to constrain the
+            // target impl's params.
+            self.add_goals(
+                GoalSource::Misc,
+                tcx.predicates_of(target_container_def_id)
+                    .iter_instantiated(tcx, &target_args)
+                    .map(|pred| goal.with(tcx, pred)),
+            );
+            goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, target_args)
+        })
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
index f7423c367b5..710671b45d0 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
@@ -2,20 +2,22 @@
 //! behaves differently depending on the param-env's reveal mode and whether
 //! the opaque is in a defining scope.
 
-use crate::solve::infcx::SolverDelegate;
-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 rustc_index::bit_set::GrowableBitSet;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::{self as ty, Interner};
 
-use crate::solve::{EvalCtxt, SolverMode};
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, Reveal, SolverMode};
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     pub(super) fn normalize_opaque_type(
         &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let opaque_ty = goal.predicate.alias;
         let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const");
@@ -32,7 +34,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                     return Err(NoSolution);
                 }
                 // FIXME: This may have issues when the args contain aliases...
-                match self.interner().uses_unique_placeholders_ignoring_regions(opaque_ty.args) {
+                match uses_unique_placeholders_ignoring_regions(self.interner(), opaque_ty.args) {
                     Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
                         return self.evaluate_added_goals_and_make_canonical_response(
                             Certainty::AMBIGUOUS,
@@ -61,6 +63,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                 }
 
                 // Otherwise, define a new opaque type
+                // FIXME: should we use `inject_hidden_type_unchecked` here?
                 self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
                 self.add_item_bounds_for_hidden_type(
                     opaque_ty.def_id,
@@ -83,10 +86,51 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             }
             (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);
+                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)
             }
         }
     }
 }
+
+/// Checks whether each generic argument is simply a unique generic placeholder.
+///
+/// FIXME: Interner argument is needed to constrain the `I` parameter.
+pub fn uses_unique_placeholders_ignoring_regions<I: Interner>(
+    _interner: I,
+    args: I::GenericArgs,
+) -> Result<(), NotUniqueParam<I>> {
+    let mut seen = GrowableBitSet::default();
+    for arg in args {
+        match arg.kind() {
+            // Ignore regions, since we can't resolve those in a canonicalized
+            // query in the trait solver.
+            ty::GenericArgKind::Lifetime(_) => {}
+            ty::GenericArgKind::Type(t) => match t.kind() {
+                ty::Placeholder(p) => {
+                    if !seen.insert(p.var()) {
+                        return Err(NotUniqueParam::DuplicateParam(t.into()));
+                    }
+                }
+                _ => return Err(NotUniqueParam::NotParam(t.into())),
+            },
+            ty::GenericArgKind::Const(c) => match c.kind() {
+                ty::ConstKind::Placeholder(p) => {
+                    if !seen.insert(p.var()) {
+                        return Err(NotUniqueParam::DuplicateParam(c.into()));
+                    }
+                }
+                _ => return Err(NotUniqueParam::NotParam(c.into())),
+            },
+        }
+    }
+
+    Ok(())
+}
+
+// FIXME: This should check for dupes and non-params first, then infer vars.
+pub enum NotUniqueParam<I: Interner> {
+    DuplicateParam(I::GenericArg),
+    NotParam(I::GenericArg),
+}
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs
index 26d60ffb321..45341917bb2 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs
@@ -4,17 +4,20 @@
 //! Since a weak alias is never ambiguous, this just computes the `type_of` of
 //! the alias and registers the where-clauses of the type alias.
 
-use crate::solve::infcx::SolverDelegate;
-use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult};
-use rustc_middle::ty;
+use rustc_type_ir::{self as ty, Interner};
 
-use crate::solve::EvalCtxt;
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult};
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     pub(super) fn normalize_weak_type(
         &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let weak_ty = goal.predicate.alias;
 
@@ -22,13 +25,11 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
         self.add_goals(
             GoalSource::Misc,
             tcx.predicates_of(weak_ty.def_id)
-                .instantiate(tcx, weak_ty.args)
-                .predicates
-                .into_iter()
+                .iter_instantiated(tcx, &weak_ty.args)
                 .map(|pred| goal.with(tcx, pred)),
         );
 
-        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
+        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, &weak_ty.args);
         self.instantiate_normalizes_to_term(goal, actual.into());
 
         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_next_trait_solver/src/solve/project_goals.rs
index 839db73a8b3..b20c274b62c 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs
@@ -1,16 +1,18 @@
-use crate::solve::GoalSource;
+use rustc_type_ir::{self as ty, Interner, ProjectionPredicate};
 
-use super::infcx::SolverDelegate;
-use super::EvalCtxt;
-use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
-use rustc_middle::ty::{self, ProjectionPredicate};
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult};
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", skip(self), ret)]
     pub(super) fn compute_projection_goal(
         &mut self,
-        goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ProjectionPredicate<I>>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let projection_term = goal.predicate.projection_term.to_term(tcx);
         let goal = goal.with(
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
index 055540d122f..d50ff2f8deb 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
@@ -1,19 +1,18 @@
 use std::mem;
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_index::Idx;
-use rustc_index::IndexVec;
-use rustc_next_trait_solver::infcx::SolverDelegate;
-use rustc_next_trait_solver::solve::CacheData;
-use rustc_next_trait_solver::solve::{CanonicalInput, Certainty, QueryResult};
-use rustc_session::Limit;
+use rustc_index::{Idx, IndexVec};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::Interner;
 
-use super::inspect;
-use super::inspect::ProofTreeBuilder;
-use super::SolverMode;
-use crate::solve::FIXPOINT_STEP_LIMIT;
+use crate::infcx::SolverDelegate;
+use crate::solve::inspect::{self, ProofTreeBuilder};
+use crate::solve::{
+    CacheData, CanonicalInput, Certainty, QueryResult, SolverMode, FIXPOINT_STEP_LIMIT,
+};
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Limit(usize);
 
 rustc_index::newtype_index! {
     #[orderable]
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 0aace43f333..19eee82edc0 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -1,60 +1,59 @@
 //! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
 
-use super::assembly::structural_traits::AsyncCallableRelevantTypes;
-use super::assembly::{self, structural_traits, Candidate};
-use super::infcx::SolverDelegate;
-use super::{EvalCtxt, GoalSource, SolverMode};
+use rustc_ast_ir::Movability;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_hir::def_id::DefId;
-use rustc_hir::{LangItem, Movability};
-use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::MaybeCause;
-use rustc_infer::traits::util::supertraits;
-use rustc_middle::bug;
-use rustc_middle::traits::solve::inspect::ProbeKind;
-use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult};
-use rustc_middle::traits::{BuiltinImplSource, Reveal};
-use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
-use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
-use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
-use rustc_span::ErrorGuaranteed;
-
-impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
-    fn self_ty(self) -> Ty<'tcx> {
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::visit::TypeVisitableExt as _;
+use rustc_type_ir::{self as ty, Interner, TraitPredicate, Upcast as _};
+
+use crate::infcx::SolverDelegate;
+use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
+use crate::solve::assembly::{self, Candidate};
+use crate::solve::inspect::ProbeKind;
+use crate::solve::{
+    BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
+    NoSolution, QueryResult, Reveal, SolverMode,
+};
+
+impl<Infcx, I> assembly::GoalKind<Infcx> for TraitPredicate<I>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
+    fn self_ty(self) -> I::Ty {
         self.self_ty()
     }
 
-    fn trait_ref(self, _: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
+    fn trait_ref(self, _: I) -> ty::TraitRef<I> {
         self.trait_ref
     }
 
-    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self {
         self.with_self_ty(tcx, self_ty)
     }
 
-    fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId {
+    fn trait_def_id(self, _: I) -> I::DefId {
         self.def_id()
     }
 
     fn consider_impl_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, TraitPredicate<'tcx>>,
-        impl_def_id: DefId,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, TraitPredicate<I>>,
+        impl_def_id: I::DefId,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
 
-        let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
-        let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
-        if !drcx.args_may_unify(
-            goal.predicate.trait_ref.args,
-            impl_trait_header.trait_ref.skip_binder().args,
-        ) {
+        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
+        if !tcx
+            .args_may_unify_deep(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
+        {
             return Err(NoSolution);
         }
 
         // An upper bound of the certainty of this goal, used to lower the certainty
         // of reservation impl to ambiguous during coherence.
-        let impl_polarity = impl_trait_header.polarity;
+        let impl_polarity = tcx.impl_polarity(impl_def_id);
         let maximal_certainty = match (impl_polarity, goal.predicate.polarity) {
             // In intercrate mode, this is ambiguous. But outside of intercrate,
             // it's not a real impl.
@@ -77,14 +76,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
             let impl_args = ecx.fresh_args_for_item(impl_def_id);
             ecx.record_impl_args(impl_args);
-            let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args);
+            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()
+                .iter_instantiated(tcx, &impl_args)
                 .map(|pred| goal.with(tcx, pred));
             ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
 
@@ -93,21 +90,21 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_error_guaranteed_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        _guar: ErrorGuaranteed,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        _guar: I::ErrorGuaranteed,
+    ) -> Result<Candidate<I>, NoSolution> {
         // FIXME: don't need to enter a probe here.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
 
     fn probe_and_match_goal_against_assumption(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        source: CandidateSource<'tcx>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-        then: impl FnOnce(&mut EvalCtxt<'_, SolverDelegate<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+        then: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult<I>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if let Some(trait_clause) = assumption.as_trait_clause() {
             if trait_clause.def_id() == goal.predicate.def_id()
                 && trait_clause.polarity() == goal.predicate.polarity
@@ -130,9 +127,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_auto_trait_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -159,7 +156,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         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)
-                || ecx.can_define_opaque_ty(opaque_ty.def_id)
+                || opaque_ty
+                    .def_id
+                    .as_local()
+                    .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id))
             {
                 return Err(NoSolution);
             }
@@ -173,9 +173,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_trait_alias_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -185,20 +185,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             let nested_obligations = tcx
                 .predicates_of(goal.predicate.def_id())
-                .instantiate(tcx, goal.predicate.trait_ref.args);
+                .iter_instantiated(tcx, &goal.predicate.trait_ref.args)
+                .map(|p| goal.with(tcx, p));
             // 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.add_goals(GoalSource::Misc, nested_obligations);
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         })
     }
 
     fn consider_builtin_sized_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -211,9 +209,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_copy_clone_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -226,28 +224,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_pointer_like_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        // The regions of a type don't affect the size of the type
         let tcx = ecx.interner();
-        // We should erase regions from both the param-env and type, since both
-        // may have infer regions. Specifically, after canonicalizing and instantiating,
-        // early bound regions turn into region vars in both the new and old solver.
-        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() {
+        if (goal.param_env, goal.predicate.self_ty()).has_non_region_infer() {
             return ecx.forced_ambiguity(MaybeCause::Ambiguity);
         }
 
-        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
+        if tcx.layout_is_pointer_like(goal.param_env, goal.predicate.self_ty()) {
             ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
                 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
         } else {
@@ -256,9 +246,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_fn_ptr_trait_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
         match goal.predicate.polarity {
             // impl FnPtr for FnPtr {}
@@ -287,10 +277,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -308,7 +298,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                 }
             };
         let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
-            ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output])
+            ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output])
         });
 
         let pred = tupled_inputs_and_output
@@ -328,10 +318,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_async_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -343,13 +333,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                 goal.predicate.self_ty(),
                 goal_kind,
                 // This region doesn't matter because we're throwing away the coroutine type
-                tcx.lifetimes.re_static,
+                Region::new_static(tcx),
             )?;
         let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
             |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
                 ty::TraitRef::new(
                     tcx,
-                    tcx.require_lang_item(LangItem::Sized, None),
+                    tcx.require_lang_item(TraitSolverLangItem::Sized),
                     [output_coroutine_ty],
                 )
             },
@@ -379,11 +369,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_async_fn_kind_helper_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let [closure_fn_kind_ty, goal_kind_ty] = **goal.predicate.trait_ref.args else {
-            bug!();
+            panic!();
         };
 
         let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else {
@@ -406,9 +396,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     /// impl Tuple for (T1, .., Tn) {}
     /// ```
     fn consider_builtin_tuple_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -422,9 +412,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_pointee_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -434,14 +424,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_future_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+        let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
             return Err(NoSolution);
         };
 
@@ -460,14 +450,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+        let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
             return Err(NoSolution);
         };
 
@@ -486,14 +476,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_fused_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+        let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
             return Err(NoSolution);
         };
 
@@ -510,14 +500,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_async_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+        let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
             return Err(NoSolution);
         };
 
@@ -536,15 +526,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_coroutine_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -568,9 +558,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_discriminant_kind_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -581,9 +571,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_async_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -594,9 +584,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -610,10 +600,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_transmute_candidate(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        if goal.predicate.polarity != ty::PredicatePolarity::Positive {
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        _goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        // TODO:
+        todo!()
+        /* if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
@@ -641,6 +633,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             )?;
             ecx.evaluate_added_goals_and_make_canonical_response(certainty)
         })
+        */
     }
 
     /// ```ignore (builtin impl example)
@@ -651,9 +644,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     /// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
     /// ```
     fn consider_structural_builtin_unsize_candidates(
-        ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Vec<Candidate<I>> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return vec![];
         }
@@ -676,7 +669,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
             let goal = goal.with(ecx.interner(), (a_ty, b_ty));
             match (a_ty.kind(), b_ty.kind()) {
-                (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"),
+                (ty::Infer(ty::TyVar(..)), ..) => panic!("unexpected infer {a_ty:?} {b_ty:?}"),
 
                 (_, ty::Infer(ty::TyVar(..))) => {
                     result_to_single(ecx.forced_ambiguity(MaybeCause::Ambiguity))
@@ -684,24 +677,24 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
                 // 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),
+                    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.
-                (_, &ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single(
+                (_, ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single(
                     ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data),
                 ),
 
                 // `[T; N]` -> `[T]` unsizing
-                (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
+                (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))
                 }
 
                 // `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
-                (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
+                (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args))
                     if a_def.is_struct() && a_def == b_def =>
                 {
                     result_to_single(
@@ -710,7 +703,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                 }
 
                 //  `(A, B, T)` -> `(A, B, U)` where `T: Unsize<U>`
-                (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
+                (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))
@@ -722,7 +715,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 }
 
-impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     /// Trait upcasting allows for coercions between trait objects:
     /// ```ignore (builtin impl example)
     /// trait Super {}
@@ -734,12 +731,12 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// ```
     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<Candidate<TyCtxt<'tcx>>> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        a_data: I::BoundExistentialPredicates,
+        a_region: I::Region,
+        b_data: I::BoundExistentialPredicates,
+        b_region: I::Region,
+    ) -> Vec<Candidate<I>> {
         let tcx = self.interner();
         let Goal { predicate: (a_ty, _b_ty), .. } = goal;
 
@@ -757,7 +754,10 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                 a_data.principal(),
             ));
         } else if let Some(a_principal) = a_data.principal() {
-            for new_a_principal in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)).skip(1) {
+            for new_a_principal in
+                Infcx::elaborate_supertraits(self.interner(), a_principal.with_self_ty(tcx, a_ty))
+                    .skip(1)
+            {
                 responses.extend(self.consider_builtin_upcast_to_principal(
                     goal,
                     CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
@@ -777,15 +777,15 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
 
     fn consider_builtin_unsize_to_dyn_candidate(
         &mut self,
-        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
-        b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-        b_region: ty::Region<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        b_data: I::BoundExistentialPredicates,
+        b_region: I::Region,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = self.interner();
         let Goal { predicate: (a_ty, _), .. } = goal;
 
         // Can only unsize to an object-safe trait.
-        if b_data.principal_def_id().is_some_and(|def_id| !tcx.is_object_safe(def_id)) {
+        if b_data.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) {
             return Err(NoSolution);
         }
 
@@ -794,7 +794,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             // (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))),
+                b_data.into_iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
             );
 
             // The type must be `Sized` to be unsized.
@@ -802,7 +802,11 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                 GoalSource::ImplWhereBound,
                 goal.with(
                     tcx,
-                    ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [a_ty]),
+                    ty::TraitRef::new(
+                        tcx,
+                        tcx.require_lang_item(TraitSolverLangItem::Sized),
+                        [a_ty],
+                    ),
                 ),
             );
 
@@ -814,24 +818,26 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
 
     fn consider_builtin_upcast_to_principal(
         &mut self,
-        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
-        source: CandidateSource<'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>>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        source: CandidateSource<I>,
+        a_data: I::BoundExistentialPredicates,
+        a_region: I::Region,
+        b_data: I::BoundExistentialPredicates,
+        b_region: I::Region,
+        upcast_principal: Option<ty::Binder<I, ty::ExistentialTraitRef<I>>>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let param_env = goal.param_env;
 
         // We may upcast to auto traits that are either explicitly listed in
         // the object type's bounds, or implied by the principal trait ref's
         // supertraits.
-        let a_auto_traits: FxIndexSet<DefId> = a_data
+        let a_auto_traits: FxIndexSet<I::DefId> = a_data
             .auto_traits()
+            .into_iter()
             .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
                 self.interner()
                     .supertrait_def_ids(principal_def_id)
+                    .into_iter()
                     .filter(|def_id| self.interner().trait_is_auto(*def_id))
             }))
             .collect();
@@ -841,9 +847,9 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
         // having any inference side-effects. We process obligations because
         // unification may initially succeed due to deferred projection equality.
         let projection_may_match =
-            |ecx: &mut EvalCtxt<'_, SolverDelegate<'tcx>>,
-             source_projection: ty::PolyExistentialProjection<'tcx>,
-             target_projection: ty::PolyExistentialProjection<'tcx>| {
+            |ecx: &mut EvalCtxt<'_, Infcx>,
+             source_projection: ty::Binder<I, ty::ExistentialProjection<I>>,
+             target_projection: ty::Binder<I, ty::ExistentialProjection<I>>| {
                 source_projection.item_def_id() == target_projection.item_def_id()
                     && ecx
                         .probe(|_| ProbeKind::UpcastProjectionCompatibility)
@@ -875,7 +881,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                     ty::ExistentialPredicate::Projection(target_projection) => {
                         let target_projection = bound.rebind(target_projection);
                         let mut matching_projections =
-                            a_data.projection_bounds().filter(|source_projection| {
+                            a_data.projection_bounds().into_iter().filter(|source_projection| {
                                 projection_may_match(ecx, *source_projection, target_projection)
                             });
                         let Some(source_projection) = matching_projections.next() else {
@@ -900,11 +906,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             // Also require that a_ty's lifetime outlives b_ty's lifetime.
             ecx.add_goal(
                 GoalSource::ImplWhereBound,
-                Goal::new(
-                    ecx.interner(),
-                    param_env,
-                    ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
-                ),
+                Goal::new(ecx.interner(), param_env, ty::OutlivesPredicate(a_region, b_region)),
             );
 
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
@@ -921,10 +923,10 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// `#[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>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        a_elem_ty: I::Ty,
+        b_elem_ty: I::Ty,
+    ) -> Result<Candidate<I>, NoSolution> {
         self.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
         self.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
@@ -945,26 +947,25 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// ```
     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>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        def: I::AdtDef,
+        a_args: I::GenericArgs,
+        b_args: I::GenericArgs,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = self.interner();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
-        let unsizing_params = tcx.unsizing_params_for_adt(def.did());
+        let unsizing_params = tcx.unsizing_params_for_adt(def.def_id());
         // 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 tail_field_ty = def.struct_tail_ty(tcx).unwrap();
 
-        let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
-        let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
+        let a_tail_ty = tail_field_ty.instantiate(tcx, &a_args);
+        let b_tail_ty = tail_field_ty.instantiate(tcx, &b_args);
 
         // Instantiate just the unsizing params from B into A. The type after
         // this instantiation must be equal to B. This is so we don't unsize
@@ -973,7 +974,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             a_args
                 .iter()
                 .enumerate()
-                .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }),
+                .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);
 
@@ -986,7 +987,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                 tcx,
                 ty::TraitRef::new(
                     tcx,
-                    tcx.require_lang_item(LangItem::Unsize, None),
+                    tcx.require_lang_item(TraitSolverLangItem::Unsize),
                     [a_tail_ty, b_tail_ty],
                 ),
             ),
@@ -1007,10 +1008,10 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// ```
     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>>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        a_tys: I::Tys,
+        b_tys: I::Tys,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = self.interner();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
@@ -1029,7 +1030,7 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
                 tcx,
                 ty::TraitRef::new(
                     tcx,
-                    tcx.require_lang_item(LangItem::Unsize, None),
+                    tcx.require_lang_item(TraitSolverLangItem::Unsize),
                     [a_last_ty, b_last_ty],
                 ),
             ),
@@ -1044,10 +1045,10 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     // the type's constituent types.
     fn disqualify_auto_trait_candidate_due_to_possible_impl(
         &mut self,
-        goal: Goal<'tcx, TraitPredicate<'tcx>>,
-    ) -> Option<Result<Candidate<TyCtxt<'tcx>>, NoSolution>> {
+        goal: Goal<I, TraitPredicate<I>>,
+    ) -> Option<Result<Candidate<I>, NoSolution>> {
         let self_ty = goal.predicate.self_ty();
-        match *self_ty.kind() {
+        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,
@@ -1065,13 +1066,15 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
             | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..)
             | ty::Placeholder(..) => Some(Err(NoSolution)),
 
-            ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
+            ty::Infer(_) | ty::Bound(_, _) => panic!("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 self.interner().is_lang_item(goal.predicate.def_id(), LangItem::Unpin) =>
+                if self
+                    .interner()
+                    .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin) =>
             {
                 match self.interner().coroutine_movability(def_id) {
                     Movability::Static => Some(Err(NoSolution)),
@@ -1144,13 +1147,13 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     /// wrapped in one.
     fn probe_and_evaluate_goal_for_constituent_tys(
         &mut self,
-        source: CandidateSource<'tcx>,
-        goal: Goal<'tcx, TraitPredicate<'tcx>>,
+        source: CandidateSource<I>,
+        goal: Goal<I, TraitPredicate<I>>,
         constituent_tys: impl Fn(
-            &EvalCtxt<'_, SolverDelegate<'tcx>>,
-            Ty<'tcx>,
-        ) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+            &EvalCtxt<'_, Infcx>,
+            I::Ty,
+        ) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>,
+    ) -> Result<Candidate<I>, NoSolution> {
         self.probe_trait_candidate(source).enter(|ecx| {
             ecx.add_goals(
                 GoalSource::ImplWhereBound,
@@ -1173,8 +1176,8 @@ impl<'tcx> EvalCtxt<'_, SolverDelegate<'tcx>> {
     #[instrument(level = "trace", skip(self))]
     pub(super) fn compute_trait_goal(
         &mut self,
-        goal: Goal<'tcx, TraitPredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, TraitPredicate<I>>,
+    ) -> QueryResult<I> {
         let candidates = self.assemble_and_evaluate_candidates(goal);
         self.merge_candidates(candidates)
     }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs
deleted file mode 100644
index 60d52305a6b..00000000000
--- a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-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_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 4afb9a2339b..c790c737fc9 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxHashSet;
 
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
 use crate::relate::Relate;
-use crate::solve::{CacheData, CanonicalInput, QueryResult};
+use crate::solve::{CacheData, CanonicalInput, QueryResult, Reveal};
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty, CollectAndApply, Interner, UpcastFrom};
 
@@ -29,10 +29,14 @@ pub trait Ty<I: Interner<Ty = Self>>:
     + Relate<I>
     + Flags
 {
+    fn new_unit(interner: I) -> Self;
+
     fn new_bool(interner: I) -> Self;
 
     fn new_u8(interner: I) -> Self;
 
+    fn new_usize(interner: I) -> Self;
+
     fn new_infer(interner: I, var: ty::InferTy) -> Self;
 
     fn new_var(interner: I, var: ty::TyVid) -> Self;
@@ -109,6 +113,10 @@ pub trait Ty<I: Interner<Ty = Self>>:
         matches!(self.kind(), ty::Infer(ty::TyVar(_)))
     }
 
+    fn is_fn_ptr(self) -> bool {
+        matches!(self.kind(), ty::FnPtr(_))
+    }
+
     fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> {
         match self.kind() {
             ty::FnPtr(sig) => sig,
@@ -128,6 +136,49 @@ pub trait Ty<I: Interner<Ty = Self>>:
             _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self),
         }
     }
+
+    fn discriminant_ty(self, interner: I) -> I::Ty;
+
+    fn async_destructor_ty(self, interner: I) -> I::Ty;
+
+    /// Returns `true` when the outermost type cannot be further normalized,
+    /// resolved, or instantiated. This includes all primitive types, but also
+    /// things like ADTs and trait objects, sice even if their arguments or
+    /// nested types may be further simplified, the outermost [`ty::TyKind`] or
+    /// type constructor remains the same.
+    fn is_known_rigid(self) -> bool {
+        match self.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Pat(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_, _)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Closure(_, _)
+            | ty::CoroutineClosure(_, _)
+            | ty::Coroutine(_, _)
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Tuple(_) => true,
+
+            ty::Error(_)
+            | ty::Infer(_)
+            | ty::Alias(_, _)
+            | ty::Param(_)
+            | ty::Bound(_, _)
+            | ty::Placeholder(_) => false,
+        }
+    }
 }
 
 pub trait Tys<I: Interner<Tys = Self>>:
@@ -202,6 +253,12 @@ pub trait Const<I: Interner<Const = Self>>:
 
     fn new_expr(interner: I, expr: I::ExprConst) -> Self;
 
+    fn new_error(interner: I, guar: I::ErrorGuaranteed) -> Self;
+
+    fn new_error_with_message(interner: I, msg: impl ToString) -> Self {
+        Self::new_error(interner, interner.delay_bug(msg))
+    }
+
     fn is_ct_var(self) -> bool {
         matches!(self.kind(), ty::ConstKind::Infer(ty::InferConst::Var(_)))
     }
@@ -223,6 +280,37 @@ pub trait GenericArg<I: Interner<GenericArg = Self>>:
     + From<I::Region>
     + From<I::Const>
 {
+    fn as_type(&self) -> Option<I::Ty> {
+        if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None }
+    }
+
+    fn expect_ty(&self) -> I::Ty {
+        self.as_type().expect("expected a type")
+    }
+
+    fn as_const(&self) -> Option<I::Const> {
+        if let ty::GenericArgKind::Const(c) = self.kind() { Some(c) } else { None }
+    }
+
+    fn expect_const(&self) -> I::Const {
+        self.as_const().expect("expected a const")
+    }
+
+    fn as_region(&self) -> Option<I::Region> {
+        if let ty::GenericArgKind::Lifetime(c) = self.kind() { Some(c) } else { None }
+    }
+
+    fn expect_region(&self) -> I::Region {
+        self.as_region().expect("expected a const")
+    }
+
+    fn is_non_region_infer(self) -> bool {
+        match self.kind() {
+            ty::GenericArgKind::Lifetime(_) => false,
+            ty::GenericArgKind::Type(ty) => ty.is_ty_var(),
+            ty::GenericArgKind::Const(ct) => ct.is_ct_var(),
+        }
+    }
 }
 
 pub trait Term<I: Interner<Term = Self>>:
@@ -232,7 +320,7 @@ pub trait Term<I: Interner<Term = Self>>:
         if let ty::TermKind::Ty(ty) = self.kind() { Some(ty) } else { None }
     }
 
-    fn expect_type(&self) -> I::Ty {
+    fn expect_ty(&self) -> I::Ty {
         self.as_type().expect("expected a type, but found a const")
     }
 
@@ -250,6 +338,19 @@ pub trait Term<I: Interner<Term = Self>>:
             ty::TermKind::Const(ct) => ct.is_ct_var(),
         }
     }
+
+    fn to_alias_term(self) -> Option<ty::AliasTerm<I>> {
+        match self.kind() {
+            ty::TermKind::Ty(ty) => match ty.kind() {
+                ty::Alias(_kind, alias_ty) => Some(alias_ty.into()),
+                _ => None,
+            },
+            ty::TermKind::Const(ct) => match ct.kind() {
+                ty::ConstKind::Unevaluated(uv) => Some(uv.into()),
+                _ => None,
+            },
+        }
+    }
 }
 
 pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
@@ -262,8 +363,17 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
     + Default
     + Relate<I>
 {
+    fn rebase_onto(
+        self,
+        interner: I,
+        source_def_id: I::DefId,
+        target: I::GenericArgs,
+    ) -> I::GenericArgs;
+
     fn type_at(self, i: usize) -> I::Ty;
 
+    fn region_at(self, i: usize) -> I::Region;
+
     fn identity_for_item(interner: I, def_id: I::DefId) -> I::GenericArgs;
 
     fn extend_with_error(
@@ -303,6 +413,9 @@ pub trait Predicate<I: Interner<Predicate = Self>>:
     + UpcastFrom<I, ty::NormalizesTo<I>>
     + UpcastFrom<I, ty::TraitRef<I>>
     + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>>
+    + UpcastFrom<I, ty::TraitPredicate<I>>
+    + UpcastFrom<I, ty::OutlivesPredicate<I, I::Ty>>
+    + UpcastFrom<I, ty::OutlivesPredicate<I, I::Region>>
     + IntoKind<Kind = ty::Binder<I, ty::PredicateKind<I>>>
 {
     fn is_coinductive(self, interner: I) -> bool;
@@ -318,9 +431,34 @@ pub trait Clause<I: Interner<Clause = Self>>:
     + Eq
     + TypeFoldable<I>
     // FIXME: Remove these, uplift the `Upcast` impls.
+    + UpcastFrom<I, ty::TraitRef<I>>
     + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>>
+    + UpcastFrom<I, ty::ProjectionPredicate<I>>
     + UpcastFrom<I, ty::Binder<I, ty::ProjectionPredicate<I>>>
+    + IntoKind<Kind = ty::Binder<I, ty::ClauseKind<I>>>
 {
+    fn as_trait_clause(self) -> Option<ty::Binder<I, ty::TraitPredicate<I>>> {
+        self.kind()
+            .map_bound(|clause| {
+                if let ty::ClauseKind::Trait(t) = clause {
+                    Some(t)
+                } else {
+                    None
+                }
+            })
+            .transpose()
+    }
+    fn as_projection_clause(self) -> Option<ty::Binder<I, ty::ProjectionPredicate<I>>> {
+        self.kind()
+            .map_bound(|clause| {
+                if let ty::ClauseKind::Projection(p) = clause {
+                    Some(p)
+                } else {
+                    None
+                }
+            })
+            .transpose()
+    }
 }
 
 /// Common capabilities of placeholder kinds
@@ -352,18 +490,33 @@ pub trait ParamLike {
 pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
     fn def_id(self) -> I::DefId;
 
+    fn is_struct(self) -> bool;
+
+    /// Returns the type of the struct tail.
+    ///
+    /// Expects the `AdtDef` to be a struct. If it is not, then this will panic.
+    fn struct_tail_ty(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>;
+
     fn is_phantom_data(self) -> bool;
 
     // FIXME: perhaps use `all_fields` and expose `FieldDef`.
-    fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl Iterator<Item = I::Ty>>;
+    fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl IntoIterator<Item = I::Ty>>;
 
     fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>;
 }
 
+pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
+    fn reveal(self) -> Reveal;
+
+    fn caller_bounds(self) -> impl IntoIterator<Item = I::Clause>;
+}
+
 pub trait Features<I: Interner>: Copy {
     fn generic_const_exprs(self) -> bool;
 
     fn coroutine_clone(self) -> bool;
+
+    fn associated_const_equality(self) -> bool;
 }
 
 pub trait EvaluationCache<I: Interner> {
@@ -392,3 +545,26 @@ pub trait EvaluationCache<I: Interner> {
         available_depth: usize,
     ) -> Option<CacheData<I>>;
 }
+
+pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
+    fn as_local(self) -> Option<I::LocalDefId>;
+}
+
+pub trait BoundExistentialPredicates<I: Interner>:
+    Copy
+    + Debug
+    + Hash
+    + Eq
+    + Relate<I>
+    + IntoIterator<Item = ty::Binder<I, ty::ExistentialPredicate<I>>>
+{
+    fn principal_def_id(self) -> Option<I::DefId>;
+
+    fn principal(self) -> Option<ty::Binder<I, ty::ExistentialTraitRef<I>>>;
+
+    fn auto_traits(self) -> impl IntoIterator<Item = I::DefId>;
+
+    fn projection_bounds(
+        self,
+    ) -> impl IntoIterator<Item = ty::Binder<I, ty::ExistentialProjection<I>>>;
+}
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index b099f63d382..59ca95c09cd 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -1,4 +1,5 @@
 use rustc_ast_ir::Movability;
+use rustc_index::bit_set::BitSet;
 use smallvec::SmallVec;
 use std::fmt::Debug;
 use std::hash::Hash;
@@ -10,7 +11,7 @@ use crate::ir_print::IrPrint;
 use crate::lang_items::TraitSolverLangItem;
 use crate::relate::Relate;
 use crate::solve::inspect::CanonicalGoalEvaluationStep;
-use crate::solve::{ExternalConstraintsData, SolverMode};
+use crate::solve::{ExternalConstraintsData, PredefinedOpaquesData, SolverMode};
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty};
 
@@ -29,9 +30,8 @@ pub trait Interner:
     + IrPrint<ty::CoercePredicate<Self>>
     + IrPrint<ty::FnSig<Self>>
 {
-    type DefId: Copy + Debug + Hash + Eq + TypeFoldable<Self>;
+    type DefId: DefId<Self>;
     type LocalDefId: Copy + Debug + Hash + Eq + Into<Self::DefId> + TypeFoldable<Self>;
-    type AdtDef: AdtDef<Self>;
 
     type GenericArgs: GenericArgs<Self>;
     type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref<Target = [Self::GenericArg]>;
@@ -46,18 +46,45 @@ pub trait Interner:
         + Default;
     type BoundVarKind: Copy + Debug + Hash + Eq;
 
-    type PredefinedOpaques: Copy + Debug + Hash + Eq;
-    type DefiningOpaqueTypes: Copy + Debug + Hash + Default + Eq + TypeVisitable<Self>;
+    type PredefinedOpaques: Copy
+        + Debug
+        + Hash
+        + Eq
+        + TypeFoldable<Self>
+        + Deref<Target = PredefinedOpaquesData<Self>>;
+    fn mk_predefined_opaques_in_body(
+        self,
+        data: PredefinedOpaquesData<Self>,
+    ) -> Self::PredefinedOpaques;
+
+    type DefiningOpaqueTypes: Copy
+        + Debug
+        + Hash
+        + Default
+        + Eq
+        + TypeVisitable<Self>
+        + Deref<Target: Deref<Target = [Self::LocalDefId]>>;
     type CanonicalGoalEvaluationStepRef: Copy
         + Debug
         + Hash
         + Eq
         + Deref<Target = CanonicalGoalEvaluationStep<Self>>;
 
-    type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator<Item = ty::CanonicalVarInfo<Self>>;
+    type CanonicalVars: Copy
+        + Debug
+        + Hash
+        + Eq
+        + IntoIterator<Item = ty::CanonicalVarInfo<Self>>
+        + Deref<Target: Deref<Target = [ty::CanonicalVarInfo<Self>]>>
+        + Default;
     fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
 
-    type ExternalConstraints: Copy + Debug + Hash + Eq;
+    type ExternalConstraints: Copy
+        + Debug
+        + Hash
+        + Eq
+        + TypeFoldable<Self>
+        + Deref<Target = ExternalConstraintsData<Self>>;
     fn mk_external_constraints(
         self,
         data: ExternalConstraintsData<Self>,
@@ -76,12 +103,7 @@ pub trait Interner:
 
     // Things stored inside of tys
     type ErrorGuaranteed: Copy + Debug + Hash + Eq;
-    type BoundExistentialPredicates: Copy
-        + Debug
-        + Hash
-        + Eq
-        + Relate<Self>
-        + IntoIterator<Item = ty::Binder<Self, ty::ExistentialPredicate<Self>>>;
+    type BoundExistentialPredicates: BoundExistentialPredicates<Self>;
     type AllocId: Copy + Debug + Hash + Eq;
     type Pat: Copy + Debug + Hash + Eq + Debug + Relate<Self>;
     type Safety: Safety<Self>;
@@ -103,7 +125,7 @@ pub trait Interner:
     type PlaceholderRegion: PlaceholderLike;
 
     // Predicates
-    type ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable<Self>;
+    type ParamEnv: ParamEnv<Self>;
     type Predicate: Predicate<Self>;
     type Clause: Clause<Self>;
     type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags;
@@ -123,9 +145,11 @@ pub trait Interner:
         + IntoIterator<Item: Deref<Target = ty::Variance>>;
     fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf;
 
-    // FIXME: Remove after uplifting `EarlyBinder`
     fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
 
+    type AdtDef: AdtDef<Self>;
+    fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef;
+
     fn alias_ty_kind(self, alias: ty::AliasTy<Self>) -> ty::AliasTyKind;
 
     fn alias_term_kind(self, alias: ty::AliasTerm<Self>) -> ty::AliasTermKind;
@@ -143,6 +167,8 @@ pub trait Interner:
         I: Iterator<Item = T>,
         T: CollectAndApply<Self::GenericArg, Self::GenericArgs>;
 
+    fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool;
+
     fn check_and_mk_args(
         self,
         def_id: Self::DefId,
@@ -187,6 +213,17 @@ pub trait Interner:
         def_id: Self::DefId,
     ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
 
+    fn predicates_of(
+        self,
+        def_id: Self::DefId,
+    ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
+
+    fn own_predicates_of(
+        self,
+        def_id: Self::DefId,
+    ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
+
+    // FIXME: Rename this so it's obvious it's only *immediate* super predicates.
     fn super_predicates_of(
         self,
         def_id: Self::DefId,
@@ -196,7 +233,64 @@ pub trait Interner:
 
     fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId;
 
+    fn is_lang_item(self, def_id: Self::DefId, lang_item: TraitSolverLangItem) -> bool;
+
     fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>;
+
+    // FIXME: move `fast_reject` into `rustc_type_ir`.
+    fn args_may_unify_deep(
+        self,
+        obligation_args: Self::GenericArgs,
+        impl_args: Self::GenericArgs,
+    ) -> bool;
+
+    fn for_each_relevant_impl(
+        self,
+        trait_def_id: Self::DefId,
+        self_ty: Self::Ty,
+        f: impl FnMut(Self::DefId),
+    );
+
+    fn has_item_definition(self, def_id: Self::DefId) -> bool;
+
+    fn impl_is_default(self, impl_def_id: Self::DefId) -> bool;
+
+    fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder<Self, ty::TraitRef<Self>>;
+
+    fn impl_polarity(self, impl_def_id: Self::DefId) -> ty::ImplPolarity;
+
+    fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool;
+
+    fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool;
+
+    fn trait_is_object_safe(self, trait_def_id: Self::DefId) -> bool;
+
+    fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool;
+
+    fn fn_trait_kind_from_def_id(self, trait_def_id: Self::DefId) -> Option<ty::ClosureKind>;
+
+    fn async_fn_trait_kind_from_def_id(self, trait_def_id: Self::DefId) -> Option<ty::ClosureKind>;
+
+    fn supertrait_def_ids(self, trait_def_id: Self::DefId)
+    -> impl IntoIterator<Item = Self::DefId>;
+
+    fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed;
+
+    fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool;
+    fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool;
+    fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool;
+    fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool;
+
+    fn layout_is_pointer_like(self, param_env: Self::ParamEnv, ty: Self::Ty) -> bool;
+
+    type UnsizingParams: Deref<Target = BitSet<u32>>;
+    fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams;
+
+    fn find_const_ty_from_env(
+        self,
+        param_env: Self::ParamEnv,
+        placeholder: Self::PlaceholderConst,
+    ) -> Self::Ty;
 }
 
 /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs
index 9a3b324fcd7..cf5ec1ab3fe 100644
--- a/compiler/rustc_type_ir/src/lang_items.rs
+++ b/compiler/rustc_type_ir/src/lang_items.rs
@@ -1,8 +1,36 @@
 /// Lang items used by the new trait solver. This can be mapped to whatever internal
 /// representation of `LangItem`s used in the underlying compiler implementation.
 pub enum TraitSolverLangItem {
-    Future,
-    FutureOutput,
+    // tidy-alphabetical-start
+    AsyncDestruct,
     AsyncFnKindHelper,
     AsyncFnKindUpvars,
+    AsyncFnOnceOutput,
+    AsyncIterator,
+    CallOnceFuture,
+    CallRefFuture,
+    Clone,
+    Copy,
+    Coroutine,
+    CoroutineReturn,
+    CoroutineYield,
+    Destruct,
+    DiscriminantKind,
+    DynMetadata,
+    FnPtrTrait,
+    FusedIterator,
+    Future,
+    FutureOutput,
+    Iterator,
+    Metadata,
+    Option,
+    PointeeTrait,
+    PointerLike,
+    Poll,
+    Sized,
+    TransmuteTrait,
+    Tuple,
+    Unpin,
+    Unsize,
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index c0713dc50d2..bf39f920276 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -7,7 +7,7 @@ use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Gen
 
 use crate::inherent::*;
 use crate::lift::Lift;
-use crate::upcast::Upcast;
+use crate::upcast::{Upcast, UpcastFrom};
 use crate::visit::TypeVisitableExt as _;
 use crate::{self as ty, Interner};
 
@@ -166,6 +166,12 @@ impl<I: Interner> ty::Binder<I, TraitPredicate<I>> {
     }
 }
 
+impl<I: Interner> UpcastFrom<I, TraitRef<I>> for TraitPredicate<I> {
+    fn upcast_from(from: TraitRef<I>, _tcx: I) -> Self {
+        TraitPredicate { trait_ref: from, polarity: PredicatePolarity::Positive }
+    }
+}
+
 impl<I: Interner> fmt::Debug for TraitPredicate<I> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // FIXME(effects) printing?