about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-10-10 14:59:40 +0000
committerbors <bors@rust-lang.org>2024-10-10 14:59:40 +0000
commit8d94e06ec9758b5c03ea77bb5dab22a1a76bc261 (patch)
tree700a7ba3864f80b3de54d742050aa6e0dbf039ae
parent4cc494bbfe9911d24f3ee521f98d5c6bb7e3ffe8 (diff)
parent8715bfbf0ef398780451e9f45d036cf8353b1e60 (diff)
downloadrust-8d94e06ec9758b5c03ea77bb5dab22a1a76bc261.tar.gz
rust-8d94e06ec9758b5c03ea77bb5dab22a1a76bc261.zip
Auto merge of #131263 - compiler-errors:solver-relating, r=lcnr
Introduce SolverRelating type relation to the new solver

Redux of #128744.

Splits out relate for the new solver so that implementors don't need to implement it themselves.

r? lcnr
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs7
-rw-r--r--compiler/rustc_infer/src/infer/at.rs146
-rw-r--r--compiler/rustc_infer/src/infer/context.rs114
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs240
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lattice.rs7
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs7
-rw-r--r--compiler/rustc_infer/src/infer/relate/type_relating.rs22
-rw-r--r--compiler/rustc_middle/src/ty/context.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/lib.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/relate.rs15
-rw-r--r--compiler/rustc_next_trait_solver/src/relate/combine.rs34
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs6
-rw-r--r--compiler/rustc_type_ir/src/infer_ctxt.rs63
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs4
-rw-r--r--compiler/rustc_type_ir/src/interner.rs2
-rw-r--r--compiler/rustc_type_ir/src/lib.rs4
-rw-r--r--compiler/rustc_type_ir/src/relate.rs15
-rw-r--r--compiler/rustc_type_ir/src/relate/combine.rs246
-rw-r--r--compiler/rustc_type_ir/src/relate/solver_relating.rs394
-rw-r--r--tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs1
-rw-r--r--tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.stderr16
-rw-r--r--tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr28
-rw-r--r--tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs3
-rw-r--r--tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.rs1
-rw-r--r--tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr14
-rw-r--r--tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs1
-rw-r--r--tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.stderr19
31 files changed, 911 insertions, 517 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index bf079e813f1..cfc14d146bd 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -11,6 +11,7 @@ use rustc_middle::span_bug;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::fold::FnMutDelegate;
+use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
@@ -362,7 +363,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
             ) if a_def_id == b_def_id || infcx.next_trait_solver() => {
-                infcx.super_combine_tys(self, a, b).map(|_| ()).or_else(|err| {
+                super_combine_tys(&infcx.infcx, self, a, b).map(|_| ()).or_else(|err| {
                     // This behavior is only there for the old solver, the new solver
                     // shouldn't ever fail. Instead, it unconditionally emits an
                     // alias-relate goal.
@@ -385,7 +386,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
                 debug!(?a, ?b, ?self.ambient_variance);
 
                 // Will also handle unification of `IntVar` and `FloatVar`.
-                self.type_checker.infcx.super_combine_tys(self, a, b)?;
+                super_combine_tys(&self.type_checker.infcx.infcx, self, a, b)?;
             }
         }
 
@@ -422,7 +423,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
         assert!(!a.has_non_region_infer(), "unexpected inference var {:?}", a);
         assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b);
 
-        self.type_checker.infcx.super_combine_consts(self, a, b)
+        super_combine_consts(&self.type_checker.infcx.infcx, self, a, b)
     }
 
     #[instrument(skip(self), level = "trace")]
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 5422761a6cf..8c943a961e7 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -27,11 +27,14 @@
 
 use relate::lattice::{LatticeOp, LatticeOpKind};
 use rustc_middle::bug;
+use rustc_middle::ty::relate::solver_relating::RelateExt as NextSolverRelate;
 use rustc_middle::ty::{Const, ImplSubject};
 
 use super::*;
 use crate::infer::relate::type_relating::TypeRelating;
-use crate::infer::relate::{Relate, StructurallyRelateAliases, TypeRelation};
+use crate::infer::relate::{Relate, TypeRelation};
+use crate::traits::Obligation;
+use crate::traits::solve::Goal;
 
 /// Whether we should define opaque types or just treat them opaquely.
 ///
@@ -109,16 +112,26 @@ impl<'a, 'tcx> At<'a, 'tcx> {
     where
         T: ToTrace<'tcx>,
     {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            ToTrace::to_trace(self.cause, expected, actual),
-            self.param_env,
-            define_opaque_types,
-            StructurallyRelateAliases::No,
-            ty::Contravariant,
-        );
-        op.relate(expected, actual)?;
-        Ok(InferOk { value: (), obligations: op.into_obligations() })
+        if self.infcx.next_trait_solver {
+            NextSolverRelate::relate(
+                self.infcx,
+                self.param_env,
+                expected,
+                ty::Contravariant,
+                actual,
+            )
+            .map(|goals| self.goals_to_obligations(goals))
+        } else {
+            let mut op = TypeRelating::new(
+                self.infcx,
+                ToTrace::to_trace(self.cause, expected, actual),
+                self.param_env,
+                define_opaque_types,
+                ty::Contravariant,
+            );
+            op.relate(expected, actual)?;
+            Ok(InferOk { value: (), obligations: op.into_obligations() })
+        }
     }
 
     /// Makes `expected <: actual`.
@@ -131,16 +144,20 @@ impl<'a, 'tcx> At<'a, 'tcx> {
     where
         T: ToTrace<'tcx>,
     {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            ToTrace::to_trace(self.cause, expected, actual),
-            self.param_env,
-            define_opaque_types,
-            StructurallyRelateAliases::No,
-            ty::Covariant,
-        );
-        op.relate(expected, actual)?;
-        Ok(InferOk { value: (), obligations: op.into_obligations() })
+        if self.infcx.next_trait_solver {
+            NextSolverRelate::relate(self.infcx, self.param_env, expected, ty::Covariant, actual)
+                .map(|goals| self.goals_to_obligations(goals))
+        } else {
+            let mut op = TypeRelating::new(
+                self.infcx,
+                ToTrace::to_trace(self.cause, expected, actual),
+                self.param_env,
+                define_opaque_types,
+                ty::Covariant,
+            );
+            op.relate(expected, actual)?;
+            Ok(InferOk { value: (), obligations: op.into_obligations() })
+        }
     }
 
     /// Makes `expected == actual`.
@@ -172,16 +189,20 @@ impl<'a, 'tcx> At<'a, 'tcx> {
     where
         T: Relate<TyCtxt<'tcx>>,
     {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            trace,
-            self.param_env,
-            define_opaque_types,
-            StructurallyRelateAliases::No,
-            ty::Invariant,
-        );
-        op.relate(expected, actual)?;
-        Ok(InferOk { value: (), obligations: op.into_obligations() })
+        if self.infcx.next_trait_solver {
+            NextSolverRelate::relate(self.infcx, self.param_env, expected, ty::Invariant, actual)
+                .map(|goals| self.goals_to_obligations(goals))
+        } else {
+            let mut op = TypeRelating::new(
+                self.infcx,
+                trace,
+                self.param_env,
+                define_opaque_types,
+                ty::Invariant,
+            );
+            op.relate(expected, actual)?;
+            Ok(InferOk { value: (), obligations: op.into_obligations() })
+        }
     }
 
     pub fn relate<T>(
@@ -208,49 +229,6 @@ impl<'a, 'tcx> At<'a, 'tcx> {
         }
     }
 
-    /// Used in the new solver since we don't care about tracking an `ObligationCause`.
-    pub fn relate_no_trace<T>(
-        self,
-        expected: T,
-        variance: ty::Variance,
-        actual: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution>
-    where
-        T: Relate<TyCtxt<'tcx>>,
-    {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            TypeTrace::dummy(self.cause),
-            self.param_env,
-            DefineOpaqueTypes::Yes,
-            StructurallyRelateAliases::No,
-            variance,
-        );
-        op.relate(expected, actual)?;
-        Ok(op.into_obligations().into_iter().map(|o| o.into()).collect())
-    }
-
-    /// Used in the new solver since we don't care about tracking an `ObligationCause`.
-    pub fn eq_structurally_relating_aliases_no_trace<T>(
-        self,
-        expected: T,
-        actual: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution>
-    where
-        T: Relate<TyCtxt<'tcx>>,
-    {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            TypeTrace::dummy(self.cause),
-            self.param_env,
-            DefineOpaqueTypes::Yes,
-            StructurallyRelateAliases::Yes,
-            ty::Invariant,
-        );
-        op.relate(expected, actual)?;
-        Ok(op.into_obligations().into_iter().map(|o| o.into()).collect())
-    }
-
     /// Computes the least-upper-bound, or mutual supertype, of two
     /// values. The order of the arguments doesn't matter, but since
     /// this can result in an error (e.g., if asked to compute LUB of
@@ -269,6 +247,26 @@ impl<'a, 'tcx> At<'a, 'tcx> {
         let value = op.relate(expected, actual)?;
         Ok(InferOk { value, obligations: op.into_obligations() })
     }
+
+    fn goals_to_obligations(
+        &self,
+        goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+    ) -> InferOk<'tcx, ()> {
+        InferOk {
+            value: (),
+            obligations: goals
+                .into_iter()
+                .map(|goal| {
+                    Obligation::new(
+                        self.infcx.tcx,
+                        self.cause.clone(),
+                        goal.param_env,
+                        goal.predicate,
+                    )
+                })
+                .collect(),
+        }
+    }
 }
 
 impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> {
diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs
index c06836edcfb..57007752cad 100644
--- a/compiler/rustc_infer/src/infer/context.rs
+++ b/compiler/rustc_infer/src/infer/context.rs
@@ -1,22 +1,27 @@
 ///! Definition of `InferCtxtLike` from the librarified type layer.
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_middle::infer::unify_key::EffectVarValue;
 use rustc_middle::traits::ObligationCause;
-use rustc_middle::traits::solve::{Goal, NoSolution, SolverMode};
+use rustc_middle::traits::solve::SolverMode;
 use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::relate::RelateResult;
+use rustc_middle::ty::relate::combine::PredicateEmittingRelation;
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::DUMMY_SP;
-use rustc_type_ir::InferCtxtLike;
-use rustc_type_ir::relate::Relate;
+use rustc_span::{DUMMY_SP, ErrorGuaranteed};
 
 use super::{BoundRegionConversionTime, InferCtxt, SubregionOrigin};
 
-impl<'tcx> InferCtxtLike for InferCtxt<'tcx> {
+impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
     type Interner = TyCtxt<'tcx>;
 
     fn cx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 
+    fn next_trait_solver(&self) -> bool {
+        self.next_trait_solver
+    }
+
     fn solver_mode(&self) -> ty::solve::SolverMode {
         match self.intercrate {
             true => SolverMode::Coherence,
@@ -131,29 +136,86 @@ impl<'tcx> InferCtxtLike for InferCtxt<'tcx> {
         self.enter_forall(value, f)
     }
 
-    fn relate<T: Relate<TyCtxt<'tcx>>>(
+    fn equate_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) {
+        self.inner.borrow_mut().type_variables().equate(a, b);
+    }
+
+    fn equate_int_vids_raw(&self, a: rustc_type_ir::IntVid, b: rustc_type_ir::IntVid) {
+        self.inner.borrow_mut().int_unification_table().union(a, b);
+    }
+
+    fn equate_float_vids_raw(&self, a: rustc_type_ir::FloatVid, b: rustc_type_ir::FloatVid) {
+        self.inner.borrow_mut().float_unification_table().union(a, b);
+    }
+
+    fn equate_const_vids_raw(&self, a: rustc_type_ir::ConstVid, b: rustc_type_ir::ConstVid) {
+        self.inner.borrow_mut().const_unification_table().union(a, b);
+    }
+
+    fn equate_effect_vids_raw(&self, a: rustc_type_ir::EffectVid, b: rustc_type_ir::EffectVid) {
+        self.inner.borrow_mut().effect_unification_table().union(a, b);
+    }
+
+    fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
-        lhs: T,
-        variance: ty::Variance,
-        rhs: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
-        self.at(&ObligationCause::dummy(), param_env).relate_no_trace(lhs, variance, rhs)
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: rustc_type_ir::TyVid,
+        instantiation_variance: rustc_type_ir::Variance,
+        source_ty: Ty<'tcx>,
+    ) -> RelateResult<'tcx, ()> {
+        self.instantiate_ty_var(
+            relation,
+            target_is_expected,
+            target_vid,
+            instantiation_variance,
+            source_ty,
+        )
+    }
+
+    fn instantiate_int_var_raw(
+        &self,
+        vid: rustc_type_ir::IntVid,
+        value: rustc_type_ir::IntVarValue,
+    ) {
+        self.inner.borrow_mut().int_unification_table().union_value(vid, value);
+    }
+
+    fn instantiate_float_var_raw(
+        &self,
+        vid: rustc_type_ir::FloatVid,
+        value: rustc_type_ir::FloatVarValue,
+    ) {
+        self.inner.borrow_mut().float_unification_table().union_value(vid, value);
+    }
+
+    fn instantiate_effect_var_raw(&self, vid: rustc_type_ir::EffectVid, value: ty::Const<'tcx>) {
+        self.inner
+            .borrow_mut()
+            .effect_unification_table()
+            .union_value(vid, EffectVarValue::Known(value));
     }
 
-    fn eq_structurally_relating_aliases<T: Relate<TyCtxt<'tcx>>>(
+    fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
-        lhs: T,
-        rhs: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
-        self.at(&ObligationCause::dummy(), param_env)
-            .eq_structurally_relating_aliases_no_trace(lhs, rhs)
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: rustc_type_ir::ConstVid,
+        source_ct: ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, ()> {
+        self.instantiate_const_var(relation, target_is_expected, target_vid, source_ct)
+    }
+
+    fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
+        self.set_tainted_by_errors(e)
     }
 
     fn shallow_resolve(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
         self.shallow_resolve(ty)
     }
+    fn shallow_resolve_const(&self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        self.shallow_resolve_const(ct)
+    }
 
     fn resolve_vars_if_possible<T>(&self, value: T) -> T
     where
@@ -167,7 +229,19 @@ impl<'tcx> InferCtxtLike for InferCtxt<'tcx> {
     }
 
     fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) {
-        self.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), sub, sup)
+        self.inner.borrow_mut().unwrap_region_constraints().make_subregion(
+            SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None),
+            sub,
+            sup,
+        );
+    }
+
+    fn equate_regions(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) {
+        self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(
+            SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None),
+            a,
+            b,
+        );
     }
 
     fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 21f9bf028d5..bc813305ba4 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -32,7 +32,6 @@ use rustc_middle::infer::unify_key::{
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
 use rustc_middle::traits::select;
-use rustc_middle::traits::solve::{Goal, NoSolution};
 pub use rustc_middle::ty::IntVarValue;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{
@@ -340,7 +339,6 @@ pub enum ValuePairs<'tcx> {
     PolySigs(ExpectedFound<ty::PolyFnSig<'tcx>>),
     ExistentialTraitRef(ExpectedFound<ty::PolyExistentialTraitRef<'tcx>>),
     ExistentialProjection(ExpectedFound<ty::PolyExistentialProjection<'tcx>>),
-    Dummy,
 }
 
 impl<'tcx> ValuePairs<'tcx> {
@@ -1638,10 +1636,6 @@ impl<'tcx> TypeTrace<'tcx> {
             values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
         }
     }
-
-    fn dummy(cause: &ObligationCause<'tcx>) -> TypeTrace<'tcx> {
-        TypeTrace { cause: cause.clone(), values: ValuePairs::Dummy }
-    }
 }
 
 impl<'tcx> SubregionOrigin<'tcx> {
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
deleted file mode 100644
index c4f5a85a8be..00000000000
--- a/compiler/rustc_infer/src/infer/relate/combine.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-//! There are four type combiners: `TypeRelating`, `Lub`, and `Glb`,
-//! and `NllTypeRelating` in rustc_borrowck, which is only used for NLL.
-//!
-//! Each implements the trait [TypeRelation] and contains methods for
-//! combining two instances of various things and yielding a new instance.
-//! These combiner methods always yield a `Result<T>`. To relate two
-//! types, you can use `infcx.at(cause, param_env)` which then allows
-//! you to use the relevant methods of [At](crate::infer::at::At).
-//!
-//! Combiners mostly do their specific behavior and then hand off the
-//! bulk of the work to [InferCtxt::super_combine_tys] and
-//! [InferCtxt::super_combine_consts].
-//!
-//! Combining two types may have side-effects on the inference contexts
-//! which can be undone by using snapshots. You probably want to use
-//! either [InferCtxt::commit_if_ok] or [InferCtxt::probe].
-//!
-//! On success, the  LUB/GLB operations return the appropriate bound. The
-//! return value of `Equate` or `Sub` shouldn't really be used.
-
-use rustc_middle::bug;
-use rustc_middle::infer::unify_key::EffectVarValue;
-use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::{self, InferConst, IntType, Ty, TypeVisitableExt, UintType};
-pub use rustc_next_trait_solver::relate::combine::*;
-use tracing::debug;
-
-use super::{RelateResult, StructurallyRelateAliases};
-use crate::infer::{InferCtxt, relate};
-
-impl<'tcx> InferCtxt<'tcx> {
-    pub fn super_combine_tys<R>(
-        &self,
-        relation: &mut R,
-        a: Ty<'tcx>,
-        b: Ty<'tcx>,
-    ) -> RelateResult<'tcx, Ty<'tcx>>
-    where
-        R: PredicateEmittingRelation<InferCtxt<'tcx>>,
-    {
-        debug!("super_combine_tys::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
-        debug_assert!(!a.has_escaping_bound_vars());
-        debug_assert!(!b.has_escaping_bound_vars());
-
-        match (a.kind(), b.kind()) {
-            // Relate integral variables to other types
-            (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => {
-                self.inner.borrow_mut().int_unification_table().union(a_id, b_id);
-                Ok(a)
-            }
-            (&ty::Infer(ty::IntVar(v_id)), &ty::Int(v)) => {
-                self.unify_integral_variable(v_id, IntType(v));
-                Ok(b)
-            }
-            (&ty::Int(v), &ty::Infer(ty::IntVar(v_id))) => {
-                self.unify_integral_variable(v_id, IntType(v));
-                Ok(a)
-            }
-            (&ty::Infer(ty::IntVar(v_id)), &ty::Uint(v)) => {
-                self.unify_integral_variable(v_id, UintType(v));
-                Ok(b)
-            }
-            (&ty::Uint(v), &ty::Infer(ty::IntVar(v_id))) => {
-                self.unify_integral_variable(v_id, UintType(v));
-                Ok(a)
-            }
-
-            // Relate floating-point variables to other types
-            (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => {
-                self.inner.borrow_mut().float_unification_table().union(a_id, b_id);
-                Ok(a)
-            }
-            (&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => {
-                self.unify_float_variable(v_id, ty::FloatVarValue::Known(v));
-                Ok(b)
-            }
-            (&ty::Float(v), &ty::Infer(ty::FloatVar(v_id))) => {
-                self.unify_float_variable(v_id, ty::FloatVarValue::Known(v));
-                Ok(a)
-            }
-
-            // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm.
-            (ty::Alias(..), ty::Infer(ty::TyVar(_))) | (ty::Infer(ty::TyVar(_)), ty::Alias(..))
-                if self.next_trait_solver() =>
-            {
-                bug!(
-                    "We do not expect to encounter `TyVar` this late in combine \
-                    -- they should have been handled earlier"
-                )
-            }
-            (_, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)))
-            | (ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), _)
-                if self.next_trait_solver() =>
-            {
-                bug!("We do not expect to encounter `Fresh` variables in the new solver")
-            }
-
-            (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => {
-                match relation.structurally_relate_aliases() {
-                    StructurallyRelateAliases::Yes => {
-                        relate::structurally_relate_tys(relation, a, b)
-                    }
-                    StructurallyRelateAliases::No => {
-                        relation.register_alias_relate_predicate(a, b);
-                        Ok(a)
-                    }
-                }
-            }
-
-            // All other cases of inference are errors
-            (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
-                Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
-            }
-
-            // During coherence, opaque types should be treated as *possibly*
-            // equal to any other type (except for possibly itself). This is an
-            // extremely heavy hammer, but can be relaxed in a forwards-compatible
-            // way later.
-            (&ty::Alias(ty::Opaque, _), _) | (_, &ty::Alias(ty::Opaque, _)) if self.intercrate => {
-                relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]);
-                Ok(a)
-            }
-
-            _ => relate::structurally_relate_tys(relation, a, b),
-        }
-    }
-
-    pub fn super_combine_consts<R>(
-        &self,
-        relation: &mut R,
-        a: ty::Const<'tcx>,
-        b: ty::Const<'tcx>,
-    ) -> RelateResult<'tcx, ty::Const<'tcx>>
-    where
-        R: PredicateEmittingRelation<InferCtxt<'tcx>>,
-    {
-        debug!("super_combine_consts::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
-        debug_assert!(!a.has_escaping_bound_vars());
-        debug_assert!(!b.has_escaping_bound_vars());
-
-        if a == b {
-            return Ok(a);
-        }
-
-        let a = self.shallow_resolve_const(a);
-        let b = self.shallow_resolve_const(b);
-
-        match (a.kind(), b.kind()) {
-            (
-                ty::ConstKind::Infer(InferConst::Var(a_vid)),
-                ty::ConstKind::Infer(InferConst::Var(b_vid)),
-            ) => {
-                self.inner.borrow_mut().const_unification_table().union(a_vid, b_vid);
-                Ok(a)
-            }
-
-            (
-                ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
-                ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
-            ) => {
-                self.inner.borrow_mut().effect_unification_table().union(a_vid, b_vid);
-                Ok(a)
-            }
-
-            // All other cases of inference with other variables are errors.
-            (
-                ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
-                ty::ConstKind::Infer(_),
-            )
-            | (
-                ty::ConstKind::Infer(_),
-                ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
-            ) => {
-                bug!(
-                    "tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
-                )
-            }
-
-            (ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
-                self.instantiate_const_var(relation, true, vid, b)?;
-                Ok(b)
-            }
-
-            (_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
-                self.instantiate_const_var(relation, false, vid, a)?;
-                Ok(a)
-            }
-
-            (ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
-                Ok(self.unify_effect_variable(vid, b))
-            }
-
-            (_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
-                Ok(self.unify_effect_variable(vid, a))
-            }
-
-            (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
-                if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
-            {
-                match relation.structurally_relate_aliases() {
-                    StructurallyRelateAliases::No => {
-                        relation.register_predicates([if self.next_trait_solver() {
-                            ty::PredicateKind::AliasRelate(
-                                a.into(),
-                                b.into(),
-                                ty::AliasRelationDirection::Equate,
-                            )
-                        } else {
-                            ty::PredicateKind::ConstEquate(a, b)
-                        }]);
-
-                        Ok(b)
-                    }
-                    StructurallyRelateAliases::Yes => {
-                        relate::structurally_relate_consts(relation, a, b)
-                    }
-                }
-            }
-            _ => relate::structurally_relate_consts(relation, a, b),
-        }
-    }
-
-    #[inline(always)]
-    fn unify_integral_variable(&self, vid: ty::IntVid, val: ty::IntVarValue) {
-        self.inner.borrow_mut().int_unification_table().union_value(vid, val);
-    }
-
-    #[inline(always)]
-    fn unify_float_variable(&self, vid: ty::FloatVid, val: ty::FloatVarValue) {
-        self.inner.borrow_mut().float_unification_table().union_value(vid, val);
-    }
-
-    fn unify_effect_variable(&self, vid: ty::EffectVid, val: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        self.inner
-            .borrow_mut()
-            .effect_unification_table()
-            .union_value(vid, EffectVarValue::Known(val));
-        val
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index 726a2296d11..7049444db9b 100644
--- a/compiler/rustc_infer/src/infer/relate/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -183,7 +183,7 @@ impl<'tcx> InferCtxt<'tcx> {
     ///
     /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant.
     #[instrument(level = "debug", skip(self, relation))]
-    pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
+    pub(crate) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
         &self,
         relation: &mut R,
         target_is_expected: bool,
diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs
index 8cec312abf7..7eb61abfbc1 100644
--- a/compiler/rustc_infer/src/infer/relate/lattice.rs
+++ b/compiler/rustc_infer/src/infer/relate/lattice.rs
@@ -18,6 +18,7 @@
 //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order)
 
 use rustc_middle::traits::solve::Goal;
+use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TyVar, TypeVisitableExt};
 use rustc_span::Span;
@@ -148,7 +149,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for LatticeOp<'_, 'tcx> {
             (
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
-            ) if a_def_id == b_def_id => infcx.super_combine_tys(self, a, b),
+            ) if a_def_id == b_def_id => super_combine_tys(infcx, self, a, b),
 
             (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
             | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
@@ -163,7 +164,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for LatticeOp<'_, 'tcx> {
                 Ok(a)
             }
 
-            _ => infcx.super_combine_tys(self, a, b),
+            _ => super_combine_tys(infcx, self, a, b),
         }
     }
 
@@ -191,7 +192,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for LatticeOp<'_, 'tcx> {
         a: ty::Const<'tcx>,
         b: ty::Const<'tcx>,
     ) -> RelateResult<'tcx, ty::Const<'tcx>> {
-        self.infcx.super_combine_consts(self, a, b)
+        super_combine_consts(self.infcx, self, a, b)
     }
 
     fn binders<T>(
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
index baab5bb274a..e6d1003cab6 100644
--- a/compiler/rustc_infer/src/infer/relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -3,12 +3,9 @@
 //! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc).
 
 pub use rustc_middle::ty::relate::RelateResult;
-pub use rustc_next_trait_solver::relate::*;
+pub use rustc_type_ir::relate::combine::PredicateEmittingRelation;
+pub use rustc_type_ir::relate::*;
 
-pub use self::combine::PredicateEmittingRelation;
-
-#[allow(hidden_glob_reexports)]
-pub(super) mod combine;
 mod generalize;
 mod higher_ranked;
 pub(super) mod lattice;
diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs
index 4b4b13e919c..35103c070c2 100644
--- a/compiler/rustc_infer/src/infer/relate/type_relating.rs
+++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs
@@ -1,4 +1,5 @@
 use rustc_middle::traits::solve::Goal;
+use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
 use rustc_middle::ty::relate::{
     Relate, RelateResult, TypeRelation, relate_args_invariantly, relate_args_with_variances,
 };
@@ -20,7 +21,6 @@ pub(crate) struct TypeRelating<'infcx, 'tcx> {
     trace: TypeTrace<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     define_opaque_types: DefineOpaqueTypes,
-    structurally_relate_aliases: StructurallyRelateAliases,
 
     // Mutable fields.
     ambient_variance: ty::Variance,
@@ -56,15 +56,14 @@ impl<'infcx, 'tcx> TypeRelating<'infcx, 'tcx> {
         trace: TypeTrace<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         define_opaque_types: DefineOpaqueTypes,
-        structurally_relate_aliases: StructurallyRelateAliases,
         ambient_variance: ty::Variance,
     ) -> TypeRelating<'infcx, 'tcx> {
+        assert!(!infcx.next_trait_solver);
         TypeRelating {
             infcx,
             trace,
             param_env,
             define_opaque_types,
-            structurally_relate_aliases,
             ambient_variance,
             obligations: vec![],
             cache: Default::default(),
@@ -183,23 +182,16 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, 'tcx> {
                 )?;
             }
 
-            (&ty::Error(e), _) | (_, &ty::Error(e)) => {
-                infcx.set_tainted_by_errors(e);
-                return Ok(Ty::new_error(self.cx(), e));
-            }
-
             (
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
             ) if a_def_id == b_def_id => {
-                infcx.super_combine_tys(self, a, b)?;
+                super_combine_tys(infcx, self, a, b)?;
             }
 
             (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
             | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
-                if self.define_opaque_types == DefineOpaqueTypes::Yes
-                    && def_id.is_local()
-                    && !infcx.next_trait_solver() =>
+                if self.define_opaque_types == DefineOpaqueTypes::Yes && def_id.is_local() =>
             {
                 self.register_goals(infcx.handle_opaque_type(
                     a,
@@ -210,7 +202,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, 'tcx> {
             }
 
             _ => {
-                infcx.super_combine_tys(self, a, b)?;
+                super_combine_tys(infcx, self, a, b)?;
             }
         }
 
@@ -265,7 +257,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, 'tcx> {
         a: ty::Const<'tcx>,
         b: ty::Const<'tcx>,
     ) -> RelateResult<'tcx, ty::Const<'tcx>> {
-        self.infcx.super_combine_consts(self, a, b)
+        super_combine_consts(self.infcx, self, a, b)
     }
 
     fn binders<T>(
@@ -357,7 +349,7 @@ impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for TypeRelating<'_, 'tcx>
     }
 
     fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
-        self.structurally_relate_aliases
+        StructurallyRelateAliases::No
     }
 
     fn register_predicates(
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 4a02fce5e7d..90265f67bc1 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -698,6 +698,12 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
     }
 }
 
+impl<'tcx> rustc_type_ir::inherent::Span<TyCtxt<'tcx>> for Span {
+    fn dummy() -> Self {
+        DUMMY_SP
+    }
+}
+
 type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
 
 pub struct CtxtInterners<'tcx> {
diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs
index ca140500e2c..de74ac32804 100644
--- a/compiler/rustc_next_trait_solver/src/lib.rs
+++ b/compiler/rustc_next_trait_solver/src/lib.rs
@@ -12,6 +12,5 @@
 pub mod canonicalizer;
 pub mod coherence;
 pub mod delegate;
-pub mod relate;
 pub mod resolve;
 pub mod solve;
diff --git a/compiler/rustc_next_trait_solver/src/relate.rs b/compiler/rustc_next_trait_solver/src/relate.rs
deleted file mode 100644
index db819961bbd..00000000000
--- a/compiler/rustc_next_trait_solver/src/relate.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-pub use rustc_type_ir::relate::*;
-
-pub mod combine;
-
-/// Whether aliases should be related structurally or not. Used
-/// to adjust the behavior of generalization and combine.
-///
-/// This should always be `No` unless in a few special-cases when
-/// instantiating canonical responses and in the new solver. Each
-/// such case should have a comment explaining why it is used.
-#[derive(Debug, Copy, Clone)]
-pub enum StructurallyRelateAliases {
-    Yes,
-    No,
-}
diff --git a/compiler/rustc_next_trait_solver/src/relate/combine.rs b/compiler/rustc_next_trait_solver/src/relate/combine.rs
deleted file mode 100644
index 96968327d8e..00000000000
--- a/compiler/rustc_next_trait_solver/src/relate/combine.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-pub use rustc_type_ir::relate::*;
-use rustc_type_ir::solve::Goal;
-use rustc_type_ir::{InferCtxtLike, Interner, Upcast};
-
-use super::StructurallyRelateAliases;
-
-pub trait PredicateEmittingRelation<Infcx, I = <Infcx as InferCtxtLike>::Interner>:
-    TypeRelation<I>
-where
-    Infcx: InferCtxtLike<Interner = I>,
-    I: Interner,
-{
-    fn span(&self) -> I::Span;
-
-    fn param_env(&self) -> I::ParamEnv;
-
-    /// Whether aliases should be related structurally. This is pretty much
-    /// always `No` unless you're equating in some specific locations of the
-    /// new solver. See the comments in these use-cases for more details.
-    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
-
-    /// Register obligations that must hold in order for this relation to hold
-    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>);
-
-    /// Register predicates that must hold in order for this relation to hold.
-    /// This uses the default `param_env` of the obligation.
-    fn register_predicates(
-        &mut self,
-        obligations: impl IntoIterator<Item: Upcast<I, I::Predicate>>,
-    );
-
-    /// Register `AliasRelate` obligation(s) that both types must be related to each other.
-    fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty);
-}
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index 252a9ed1a2e..3b6197c6759 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -14,6 +14,7 @@ use std::iter;
 use rustc_index::IndexVec;
 use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::inherent::*;
+use rustc_type_ir::relate::solver_relating::RelateExt;
 use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner};
 use tracing::{instrument, trace};
 
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index bc324dcbf51..8391cdb2ca1 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -7,6 +7,7 @@ use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack};
 use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::relate::Relate;
+use rustc_type_ir::relate::solver_relating::RelateExt;
 use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use rustc_type_ir::{self as ty, CanonicalVarValues, InferCtxtLike, Interner};
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
@@ -871,7 +872,7 @@ where
         lhs: T,
         rhs: T,
     ) -> Result<Vec<Goal<I, I::Predicate>>, NoSolution> {
-        self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs)
+        Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs)?)
     }
 
     pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<I> + Copy>(
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index c7b3f704330..bd78a6ee3af 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -1285,9 +1285,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     ValuePairs::ExistentialProjection(_) => {
                         (false, Mismatch::Fixed("existential projection"))
                     }
-                    ValuePairs::Dummy => {
-                        bug!("do not expect to report a type error from a ValuePairs::Dummy")
-                    }
                 };
                 let Some(vals) = self.values_str(values) else {
                     // Derived error. Cancel the emitter.
@@ -1853,9 +1850,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found);
                 Some((exp, fnd, None))
             }
-            ValuePairs::Dummy => {
-                bug!("do not expect to report a type error from a ValuePairs::Dummy")
-            }
         }
     }
 
diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs
index 1d0b2345b80..b9f5cde653e 100644
--- a/compiler/rustc_type_ir/src/infer_ctxt.rs
+++ b/compiler/rustc_type_ir/src/infer_ctxt.rs
@@ -1,12 +1,21 @@
 use crate::fold::TypeFoldable;
-use crate::relate::Relate;
-use crate::solve::{Goal, NoSolution, SolverMode};
+use crate::relate::RelateResult;
+use crate::relate::combine::PredicateEmittingRelation;
+use crate::solve::SolverMode;
 use crate::{self as ty, Interner};
 
 pub trait InferCtxtLike: Sized {
     type Interner: Interner;
     fn cx(&self) -> Self::Interner;
 
+    /// Whether the new trait solver is enabled. This only exists because rustc
+    /// shares code between the new and old trait solvers; for all other users,
+    /// this should always be true. If this is unknowingly false and you try to
+    /// use the new trait solver, things will break badly.
+    fn next_trait_solver(&self) -> bool {
+        true
+    }
+
     fn solver_mode(&self) -> SolverMode;
 
     fn universe(&self) -> ty::UniverseIndex;
@@ -58,25 +67,45 @@ pub trait InferCtxtLike: Sized {
         f: impl FnOnce(T) -> U,
     ) -> U;
 
-    fn relate<T: Relate<Self::Interner>>(
-        &self,
-        param_env: <Self::Interner as Interner>::ParamEnv,
-        lhs: T,
-        variance: ty::Variance,
-        rhs: T,
-    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
+    fn equate_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid);
+    fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid);
+    fn equate_float_vids_raw(&self, a: ty::FloatVid, b: ty::FloatVid);
+    fn equate_const_vids_raw(&self, a: ty::ConstVid, b: ty::ConstVid);
+    fn equate_effect_vids_raw(&self, a: ty::EffectVid, b: ty::EffectVid);
 
-    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
+    fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>(
+        &self,
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: ty::TyVid,
+        instantiation_variance: ty::Variance,
+        source_ty: <Self::Interner as Interner>::Ty,
+    ) -> RelateResult<Self::Interner, ()>;
+    fn instantiate_int_var_raw(&self, vid: ty::IntVid, value: ty::IntVarValue);
+    fn instantiate_float_var_raw(&self, vid: ty::FloatVid, value: ty::FloatVarValue);
+    fn instantiate_effect_var_raw(
+        &self,
+        vid: ty::EffectVid,
+        value: <Self::Interner as Interner>::Const,
+    );
+    fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>(
         &self,
-        param_env: <Self::Interner as Interner>::ParamEnv,
-        lhs: T,
-        rhs: T,
-    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: ty::ConstVid,
+        source_ct: <Self::Interner as Interner>::Const,
+    ) -> RelateResult<Self::Interner, ()>;
+
+    fn set_tainted_by_errors(&self, e: <Self::Interner as Interner>::ErrorGuaranteed);
 
     fn shallow_resolve(
         &self,
         ty: <Self::Interner as Interner>::Ty,
     ) -> <Self::Interner as Interner>::Ty;
+    fn shallow_resolve_const(
+        &self,
+        ty: <Self::Interner as Interner>::Const,
+    ) -> <Self::Interner as Interner>::Const;
 
     fn resolve_vars_if_possible<T>(&self, value: T) -> T
     where
@@ -90,6 +119,12 @@ pub trait InferCtxtLike: Sized {
         sup: <Self::Interner as Interner>::Region,
     );
 
+    fn equate_regions(
+        &self,
+        a: <Self::Interner as Interner>::Region,
+        b: <Self::Interner as Interner>::Region,
+    );
+
     fn register_ty_outlives(
         &self,
         ty: <Self::Interner as Interner>::Ty,
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 69665df4bfc..f7875bb5152 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -565,6 +565,10 @@ pub trait BoundExistentialPredicates<I: Interner>:
     ) -> impl IntoIterator<Item = ty::Binder<I, ty::ExistentialProjection<I>>>;
 }
 
+pub trait Span<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
+    fn dummy() -> Self;
+}
+
 pub trait SliceLike: Sized + Copy {
     type Item: Copy;
     type IntoIter: Iterator<Item = Self::Item>;
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index b2ac67efef6..a72e7b482a6 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -36,7 +36,7 @@ pub trait Interner:
 {
     type DefId: DefId<Self>;
     type LocalDefId: Copy + Debug + Hash + Eq + Into<Self::DefId> + TypeFoldable<Self>;
-    type Span: Copy + Debug + Hash + Eq + TypeFoldable<Self>;
+    type Span: Span<Self>;
 
     type GenericArgs: GenericArgs<Self>;
     type GenericArgsSlice: Copy + Debug + Hash + Eq + SliceLike<Item = Self::GenericArg>;
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 02a9ad1e35f..51c887fc4da 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -206,8 +206,8 @@ pub fn debug_bound_var<T: std::fmt::Write>(
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq)]
-#[cfg_attr(feature = "nightly", derive(Decodable, Encodable, Hash, HashStable_NoContext))]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "nightly", derive(Decodable, Encodable, HashStable_NoContext))]
 #[cfg_attr(feature = "nightly", rustc_pass_by_value)]
 pub enum Variance {
     Covariant,     // T<A> <: T<B> iff A <: B -- e.g., function return type
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
index 9c725f34d8e..1302906adab 100644
--- a/compiler/rustc_type_ir/src/relate.rs
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -9,8 +9,23 @@ use crate::fold::TypeFoldable;
 use crate::inherent::*;
 use crate::{self as ty, Interner};
 
+pub mod combine;
+pub mod solver_relating;
+
 pub type RelateResult<I, T> = Result<T, TypeError<I>>;
 
+/// Whether aliases should be related structurally or not. Used
+/// to adjust the behavior of generalization and combine.
+///
+/// This should always be `No` unless in a few special-cases when
+/// instantiating canonical responses and in the new solver. Each
+/// such case should have a comment explaining why it is used.
+#[derive(Debug, Copy, Clone)]
+pub enum StructurallyRelateAliases {
+    Yes,
+    No,
+}
+
 /// Extra information about why we ended up with a particular variance.
 /// This is only used to add more information to error messages, and
 /// has no effect on soundness. While choosing the 'wrong' `VarianceDiagInfo`
diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs
new file mode 100644
index 00000000000..60a953801a4
--- /dev/null
+++ b/compiler/rustc_type_ir/src/relate/combine.rs
@@ -0,0 +1,246 @@
+use tracing::debug;
+
+use super::{
+    ExpectedFound, RelateResult, StructurallyRelateAliases, TypeRelation,
+    structurally_relate_consts, structurally_relate_tys,
+};
+use crate::error::TypeError;
+use crate::inherent::*;
+use crate::solve::{Goal, SolverMode};
+use crate::visit::TypeVisitableExt as _;
+use crate::{self as ty, InferCtxtLike, Interner, Upcast};
+
+pub trait PredicateEmittingRelation<Infcx, I = <Infcx as InferCtxtLike>::Interner>:
+    TypeRelation<I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn span(&self) -> I::Span;
+
+    fn param_env(&self) -> I::ParamEnv;
+
+    /// Whether aliases should be related structurally. This is pretty much
+    /// always `No` unless you're equating in some specific locations of the
+    /// new solver. See the comments in these use-cases for more details.
+    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
+
+    /// Register obligations that must hold in order for this relation to hold
+    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>);
+
+    /// Register predicates that must hold in order for this relation to hold.
+    /// This uses the default `param_env` of the obligation.
+    fn register_predicates(
+        &mut self,
+        obligations: impl IntoIterator<Item: Upcast<I, I::Predicate>>,
+    );
+
+    /// Register `AliasRelate` obligation(s) that both types must be related to each other.
+    fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty);
+}
+
+pub fn super_combine_tys<Infcx, I, R>(
+    infcx: &Infcx,
+    relation: &mut R,
+    a: I::Ty,
+    b: I::Ty,
+) -> RelateResult<I, I::Ty>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    R: PredicateEmittingRelation<Infcx>,
+{
+    debug!("super_combine_tys::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
+    debug_assert!(!a.has_escaping_bound_vars());
+    debug_assert!(!b.has_escaping_bound_vars());
+
+    match (a.kind(), b.kind()) {
+        (ty::Error(e), _) | (_, ty::Error(e)) => {
+            infcx.set_tainted_by_errors(e);
+            return Ok(Ty::new_error(infcx.cx(), e));
+        }
+
+        // Relate integral variables to other types
+        (ty::Infer(ty::IntVar(a_id)), ty::Infer(ty::IntVar(b_id))) => {
+            infcx.equate_int_vids_raw(a_id, b_id);
+            Ok(a)
+        }
+        (ty::Infer(ty::IntVar(v_id)), ty::Int(v)) => {
+            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::IntType(v));
+            Ok(b)
+        }
+        (ty::Int(v), ty::Infer(ty::IntVar(v_id))) => {
+            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::IntType(v));
+            Ok(a)
+        }
+        (ty::Infer(ty::IntVar(v_id)), ty::Uint(v)) => {
+            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::UintType(v));
+            Ok(b)
+        }
+        (ty::Uint(v), ty::Infer(ty::IntVar(v_id))) => {
+            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::UintType(v));
+            Ok(a)
+        }
+
+        // Relate floating-point variables to other types
+        (ty::Infer(ty::FloatVar(a_id)), ty::Infer(ty::FloatVar(b_id))) => {
+            infcx.equate_float_vids_raw(a_id, b_id);
+            Ok(a)
+        }
+        (ty::Infer(ty::FloatVar(v_id)), ty::Float(v)) => {
+            infcx.instantiate_float_var_raw(v_id, ty::FloatVarValue::Known(v));
+            Ok(b)
+        }
+        (ty::Float(v), ty::Infer(ty::FloatVar(v_id))) => {
+            infcx.instantiate_float_var_raw(v_id, ty::FloatVarValue::Known(v));
+            Ok(a)
+        }
+
+        // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm.
+        (ty::Alias(..), ty::Infer(ty::TyVar(_))) | (ty::Infer(ty::TyVar(_)), ty::Alias(..))
+            if infcx.next_trait_solver() =>
+        {
+            panic!(
+                "We do not expect to encounter `TyVar` this late in combine \
+                    -- they should have been handled earlier"
+            )
+        }
+        (_, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)))
+        | (ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), _)
+            if infcx.next_trait_solver() =>
+        {
+            panic!("We do not expect to encounter `Fresh` variables in the new solver")
+        }
+
+        (_, ty::Alias(..)) | (ty::Alias(..), _) if infcx.next_trait_solver() => {
+            match relation.structurally_relate_aliases() {
+                StructurallyRelateAliases::Yes => structurally_relate_tys(relation, a, b),
+                StructurallyRelateAliases::No => {
+                    relation.register_alias_relate_predicate(a, b);
+                    Ok(a)
+                }
+            }
+        }
+
+        // All other cases of inference are errors
+        (ty::Infer(_), _) | (_, ty::Infer(_)) => {
+            Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
+        }
+
+        (ty::Alias(ty::Opaque, _), _) | (_, ty::Alias(ty::Opaque, _)) => {
+            match infcx.solver_mode() {
+                SolverMode::Normal => {
+                    assert!(!infcx.next_trait_solver());
+                    structurally_relate_tys(relation, a, b)
+                }
+                // During coherence, opaque types should be treated as *possibly*
+                // equal to any other type (except for possibly itinfcx). This is an
+                // extremely heavy hammer, but can be relaxed in a forwards-compatible
+                // way later.
+                SolverMode::Coherence => {
+                    relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]);
+                    Ok(a)
+                }
+            }
+        }
+
+        _ => structurally_relate_tys(relation, a, b),
+    }
+}
+
+pub fn super_combine_consts<Infcx, I, R>(
+    infcx: &Infcx,
+    relation: &mut R,
+    a: I::Const,
+    b: I::Const,
+) -> RelateResult<I, I::Const>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    R: PredicateEmittingRelation<Infcx>,
+{
+    debug!("super_combine_consts::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
+    debug_assert!(!a.has_escaping_bound_vars());
+    debug_assert!(!b.has_escaping_bound_vars());
+
+    if a == b {
+        return Ok(a);
+    }
+
+    let a = infcx.shallow_resolve_const(a);
+    let b = infcx.shallow_resolve_const(b);
+
+    match (a.kind(), b.kind()) {
+        (
+            ty::ConstKind::Infer(ty::InferConst::Var(a_vid)),
+            ty::ConstKind::Infer(ty::InferConst::Var(b_vid)),
+        ) => {
+            infcx.equate_const_vids_raw(a_vid, b_vid);
+            Ok(a)
+        }
+
+        (
+            ty::ConstKind::Infer(ty::InferConst::EffectVar(a_vid)),
+            ty::ConstKind::Infer(ty::InferConst::EffectVar(b_vid)),
+        ) => {
+            infcx.equate_effect_vids_raw(a_vid, b_vid);
+            Ok(a)
+        }
+
+        // All other cases of inference with other variables are errors.
+        (
+            ty::ConstKind::Infer(ty::InferConst::Var(_) | ty::InferConst::EffectVar(_)),
+            ty::ConstKind::Infer(_),
+        )
+        | (
+            ty::ConstKind::Infer(_),
+            ty::ConstKind::Infer(ty::InferConst::Var(_) | ty::InferConst::EffectVar(_)),
+        ) => {
+            panic!(
+                "tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
+            )
+        }
+
+        (ty::ConstKind::Infer(ty::InferConst::Var(vid)), _) => {
+            infcx.instantiate_const_var_raw(relation, true, vid, b)?;
+            Ok(b)
+        }
+
+        (_, ty::ConstKind::Infer(ty::InferConst::Var(vid))) => {
+            infcx.instantiate_const_var_raw(relation, false, vid, a)?;
+            Ok(a)
+        }
+
+        (ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)), _) => {
+            infcx.instantiate_effect_var_raw(vid, b);
+            Ok(b)
+        }
+
+        (_, ty::ConstKind::Infer(ty::InferConst::EffectVar(vid))) => {
+            infcx.instantiate_effect_var_raw(vid, a);
+            Ok(a)
+        }
+
+        (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
+            if infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver() =>
+        {
+            match relation.structurally_relate_aliases() {
+                StructurallyRelateAliases::No => {
+                    relation.register_predicates([if infcx.next_trait_solver() {
+                        ty::PredicateKind::AliasRelate(
+                            a.into(),
+                            b.into(),
+                            ty::AliasRelationDirection::Equate,
+                        )
+                    } else {
+                        ty::PredicateKind::ConstEquate(a, b)
+                    }]);
+
+                    Ok(b)
+                }
+                StructurallyRelateAliases::Yes => structurally_relate_consts(relation, a, b),
+            }
+        }
+        _ => structurally_relate_consts(relation, a, b),
+    }
+}
diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs
new file mode 100644
index 00000000000..ad10ad5af9c
--- /dev/null
+++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs
@@ -0,0 +1,394 @@
+pub use rustc_type_ir::relate::*;
+use rustc_type_ir::solve::Goal;
+use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
+use tracing::{debug, instrument};
+
+use self::combine::{PredicateEmittingRelation, super_combine_consts, super_combine_tys};
+use crate::data_structures::DelayedSet;
+
+pub trait RelateExt: InferCtxtLike {
+    fn relate<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        variance: ty::Variance,
+        rhs: T,
+    ) -> Result<
+        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+        TypeError<Self::Interner>,
+    >;
+
+    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        rhs: T,
+    ) -> Result<
+        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+        TypeError<Self::Interner>,
+    >;
+}
+
+impl<Infcx: InferCtxtLike> RelateExt for Infcx {
+    fn relate<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        variance: ty::Variance,
+        rhs: T,
+    ) -> Result<
+        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+        TypeError<Self::Interner>,
+    > {
+        let mut relate =
+            SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env);
+        relate.relate(lhs, rhs)?;
+        Ok(relate.goals)
+    }
+
+    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        rhs: T,
+    ) -> Result<
+        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+        TypeError<Self::Interner>,
+    > {
+        let mut relate =
+            SolverRelating::new(self, StructurallyRelateAliases::Yes, ty::Invariant, param_env);
+        relate.relate(lhs, rhs)?;
+        Ok(relate.goals)
+    }
+}
+
+/// Enforce that `a` is equal to or a subtype of `b`.
+pub struct SolverRelating<'infcx, Infcx, I: Interner> {
+    infcx: &'infcx Infcx,
+    // Immutable fields.
+    structurally_relate_aliases: StructurallyRelateAliases,
+    param_env: I::ParamEnv,
+    // Mutable fields.
+    ambient_variance: ty::Variance,
+    goals: Vec<Goal<I, I::Predicate>>,
+    /// The cache only tracks the `ambient_variance` as it's the
+    /// only field which is mutable and which meaningfully changes
+    /// the result when relating types.
+    ///
+    /// The cache does not track whether the state of the
+    /// `Infcx` has been changed or whether we've added any
+    /// goals to `self.goals`. Whether a goal is added once or multiple
+    /// times is not really meaningful.
+    ///
+    /// Changes in the inference state may delay some type inference to
+    /// the next fulfillment loop. Given that this loop is already
+    /// necessary, this is also not a meaningful change. Consider
+    /// the following three relations:
+    /// ```text
+    /// Vec<?0> sub Vec<?1>
+    /// ?0 eq u32
+    /// Vec<?0> sub Vec<?1>
+    /// ```
+    /// Without a cache, the second `Vec<?0> sub Vec<?1>` would eagerly
+    /// constrain `?1` to `u32`. When using the cache entry from the
+    /// first time we've related these types, this only happens when
+    /// later proving the `Subtype(?0, ?1)` goal from the first relation.
+    cache: DelayedSet<(ty::Variance, I::Ty, I::Ty)>,
+}
+
+impl<'infcx, Infcx, I> SolverRelating<'infcx, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    pub fn new(
+        infcx: &'infcx Infcx,
+        structurally_relate_aliases: StructurallyRelateAliases,
+        ambient_variance: ty::Variance,
+        param_env: I::ParamEnv,
+    ) -> Self {
+        SolverRelating {
+            infcx,
+            structurally_relate_aliases,
+            ambient_variance,
+            param_env,
+            goals: vec![],
+            cache: Default::default(),
+        }
+    }
+}
+
+impl<Infcx, I> TypeRelation<I> for SolverRelating<'_, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn cx(&self) -> I {
+        self.infcx.cx()
+    }
+
+    fn relate_item_args(
+        &mut self,
+        item_def_id: I::DefId,
+        a_arg: I::GenericArgs,
+        b_arg: I::GenericArgs,
+    ) -> RelateResult<I, I::GenericArgs> {
+        if self.ambient_variance == ty::Invariant {
+            // Avoid fetching the variance if we are in an invariant
+            // context; no need, and it can induce dependency cycles
+            // (e.g., #41849).
+            relate_args_invariantly(self, a_arg, b_arg)
+        } else {
+            let tcx = self.cx();
+            let opt_variances = tcx.variances_of(item_def_id);
+            relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, false)
+        }
+    }
+
+    fn relate_with_variance<T: Relate<I>>(
+        &mut self,
+        variance: ty::Variance,
+        _info: VarianceDiagInfo<I>,
+        a: T,
+        b: T,
+    ) -> RelateResult<I, T> {
+        let old_ambient_variance = self.ambient_variance;
+        self.ambient_variance = self.ambient_variance.xform(variance);
+        debug!(?self.ambient_variance, "new ambient variance");
+
+        let r = if self.ambient_variance == ty::Bivariant { Ok(a) } else { self.relate(a, b) };
+
+        self.ambient_variance = old_ambient_variance;
+        r
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult<I, I::Ty> {
+        if a == b {
+            return Ok(a);
+        }
+
+        let infcx = self.infcx;
+        let a = infcx.shallow_resolve(a);
+        let b = infcx.shallow_resolve(b);
+
+        if self.cache.contains(&(self.ambient_variance, a, b)) {
+            return Ok(a);
+        }
+
+        match (a.kind(), b.kind()) {
+            (ty::Infer(ty::TyVar(a_id)), ty::Infer(ty::TyVar(b_id))) => {
+                match self.ambient_variance {
+                    ty::Covariant => {
+                        // can't make progress on `A <: B` if both A and B are
+                        // type variables, so record an obligation.
+                        self.goals.push(Goal::new(
+                            self.cx(),
+                            self.param_env,
+                            ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
+                                a_is_expected: true,
+                                a,
+                                b,
+                            })),
+                        ));
+                    }
+                    ty::Contravariant => {
+                        // can't make progress on `B <: A` if both A and B are
+                        // type variables, so record an obligation.
+                        self.goals.push(Goal::new(
+                            self.cx(),
+                            self.param_env,
+                            ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
+                                a_is_expected: false,
+                                a: b,
+                                b: a,
+                            })),
+                        ));
+                    }
+                    ty::Invariant => {
+                        infcx.equate_ty_vids_raw(a_id, b_id);
+                    }
+                    ty::Bivariant => {
+                        unreachable!("Expected bivariance to be handled in relate_with_variance")
+                    }
+                }
+            }
+
+            (ty::Infer(ty::TyVar(a_vid)), _) => {
+                infcx.instantiate_ty_var_raw(self, true, a_vid, self.ambient_variance, b)?;
+            }
+            (_, ty::Infer(ty::TyVar(b_vid))) => {
+                infcx.instantiate_ty_var_raw(
+                    self,
+                    false,
+                    b_vid,
+                    self.ambient_variance.xform(ty::Contravariant),
+                    a,
+                )?;
+            }
+
+            _ => {
+                super_combine_tys(self.infcx, self, a, b)?;
+            }
+        }
+
+        assert!(self.cache.insert((self.ambient_variance, a, b)));
+
+        Ok(a)
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult<I, I::Region> {
+        match self.ambient_variance {
+            // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a)
+            ty::Covariant => self.infcx.sub_regions(b, a),
+            // Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b)
+            ty::Contravariant => self.infcx.sub_regions(a, b),
+            ty::Invariant => self.infcx.equate_regions(a, b),
+            ty::Bivariant => {
+                unreachable!("Expected bivariance to be handled in relate_with_variance")
+            }
+        }
+
+        Ok(a)
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult<I, I::Const> {
+        super_combine_consts(self.infcx, self, a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<I, T>,
+        b: ty::Binder<I, T>,
+    ) -> RelateResult<I, ty::Binder<I, T>>
+    where
+        T: Relate<I>,
+    {
+        // If they're equal, then short-circuit.
+        if a == b {
+            return Ok(a);
+        }
+
+        // If they have no bound vars, relate normally.
+        if let Some(a_inner) = a.no_bound_vars() {
+            if let Some(b_inner) = b.no_bound_vars() {
+                self.relate(a_inner, b_inner)?;
+                return Ok(a);
+            }
+        };
+
+        match self.ambient_variance {
+            // Checks whether `for<..> sub <: for<..> sup` holds.
+            //
+            // For this to hold, **all** instantiations of the super type
+            // have to be a super type of **at least one** instantiation of
+            // the subtype.
+            //
+            // This is implemented by first entering a new universe.
+            // We then replace all bound variables in `sup` with placeholders,
+            // and all bound variables in `sub` with inference vars.
+            // We can then just relate the two resulting types as normal.
+            //
+            // Note: this is a subtle algorithm. For a full explanation, please see
+            // the [rustc dev guide][rd]
+            //
+            // [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html
+            ty::Covariant => {
+                self.infcx.enter_forall(b, |b| {
+                    let a = self.infcx.instantiate_binder_with_infer(a);
+                    self.relate(a, b)
+                })?;
+            }
+            ty::Contravariant => {
+                self.infcx.enter_forall(a, |a| {
+                    let b = self.infcx.instantiate_binder_with_infer(b);
+                    self.relate(a, b)
+                })?;
+            }
+
+            // When **equating** binders, we check that there is a 1-to-1
+            // correspondence between the bound vars in both types.
+            //
+            // We do so by separately instantiating one of the binders with
+            // placeholders and the other with inference variables and then
+            // equating the instantiated types.
+            //
+            // We want `for<..> A == for<..> B` -- therefore we want
+            // `exists<..> A == for<..> B` and `exists<..> B == for<..> A`.
+            // Check if `exists<..> A == for<..> B`
+            ty::Invariant => {
+                self.infcx.enter_forall(b, |b| {
+                    let a = self.infcx.instantiate_binder_with_infer(a);
+                    self.relate(a, b)
+                })?;
+
+                // Check if `exists<..> B == for<..> A`.
+                self.infcx.enter_forall(a, |a| {
+                    let b = self.infcx.instantiate_binder_with_infer(b);
+                    self.relate(a, b)
+                })?;
+            }
+            ty::Bivariant => {
+                unreachable!("Expected bivariance to be handled in relate_with_variance")
+            }
+        }
+        Ok(a)
+    }
+}
+
+impl<Infcx, I> PredicateEmittingRelation<Infcx> for SolverRelating<'_, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn span(&self) -> I::Span {
+        Span::dummy()
+    }
+
+    fn param_env(&self) -> I::ParamEnv {
+        self.param_env
+    }
+
+    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
+        self.structurally_relate_aliases
+    }
+
+    fn register_predicates(
+        &mut self,
+        obligations: impl IntoIterator<Item: ty::Upcast<I, I::Predicate>>,
+    ) {
+        self.goals.extend(
+            obligations.into_iter().map(|pred| Goal::new(self.infcx.cx(), self.param_env, pred)),
+        );
+    }
+
+    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>) {
+        self.goals.extend(obligations);
+    }
+
+    fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty) {
+        self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
+            ty::Covariant => ty::PredicateKind::AliasRelate(
+                a.into(),
+                b.into(),
+                ty::AliasRelationDirection::Subtype,
+            ),
+            // a :> b is b <: a
+            ty::Contravariant => ty::PredicateKind::AliasRelate(
+                b.into(),
+                a.into(),
+                ty::AliasRelationDirection::Subtype,
+            ),
+            ty::Invariant => ty::PredicateKind::AliasRelate(
+                a.into(),
+                b.into(),
+                ty::AliasRelationDirection::Equate,
+            ),
+            ty::Bivariant => {
+                unreachable!("Expected bivariance to be handled in relate_with_variance")
+            }
+        })]);
+    }
+}
diff --git a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs
index 6083cc7d96d..b4df58b3c25 100644
--- a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs
+++ b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs
@@ -14,7 +14,6 @@ impl Foo for Baz {
     //~^ ERROR `F` cannot be sent between threads safely
     where
         F: FnMut() + Send,
-        //~^ ERROR impl has stricter requirements than trait
     {
         ()
     }
diff --git a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.stderr b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.stderr
index 7f3cd2a5900..e6379954776 100644
--- a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.stderr
+++ b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.stderr
@@ -16,18 +16,6 @@ LL |     async fn bar<F>(&mut self, _func: F) -> ()
 LL |         F: FnMut() + Send,
    |                      ^^^^ required by this bound in `<Baz as Foo>::bar`
 
-error[E0276]: impl has stricter requirements than trait
-  --> $DIR/remove-invalid-type-bound-suggest-issue-127555.rs:16:22
-   |
-LL | /     fn bar<F>(&mut self, func: F) -> impl std::future::Future<Output = ()> + Send
-LL | |     where
-LL | |         F: FnMut();
-   | |___________________- definition of `bar` from trait
-...
-LL |           F: FnMut() + Send,
-   |                        ^^^^ impl has extra requirement `F: Send`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
-Some errors have detailed explanations: E0276, E0277.
-For more information about an error, try `rustc --explain E0276`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr
index d71c1768a6a..058517f0014 100644
--- a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr
+++ b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr
@@ -73,32 +73,6 @@ help: consider further restricting this bound
 LL |         F: Callback<Self::CallbackArg> + MyFn<i32>,
    |                                        +++++++++++
 
-error[E0277]: the trait bound `F: Callback<i32>` is not satisfied
-  --> $DIR/false-positive-predicate-entailment-error.rs:43:12
-   |
-LL |         F: Callback<Self::CallbackArg>,
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>`
-   |
-note: required for `F` to implement `Callback<i32>`
-  --> $DIR/false-positive-predicate-entailment-error.rs:14:21
-   |
-LL | impl<A, F: MyFn<A>> Callback<A> for F {
-   |            -------  ^^^^^^^^^^^     ^
-   |            |
-   |            unsatisfied trait bound introduced here
-note: the requirement `F: Callback<i32>` appears on the `impl`'s method `autobatch` but not on the corresponding trait's method
-  --> $DIR/false-positive-predicate-entailment-error.rs:25:8
-   |
-LL | trait ChannelSender {
-   |       ------------- in this trait
-...
-LL |     fn autobatch<F>(self) -> impl Trait
-   |        ^^^^^^^^^ this trait's method doesn't have the requirement `F: Callback<i32>`
-help: consider further restricting this bound
-   |
-LL |         F: Callback<Self::CallbackArg> + MyFn<i32>,
-   |                                        +++++++++++
-
 error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
   --> $DIR/false-positive-predicate-entailment-error.rs:36:30
    |
@@ -168,6 +142,6 @@ help: consider further restricting this bound
 LL |         F: Callback<Self::CallbackArg> + MyFn<i32>,
    |                                        +++++++++++
 
-error: aborting due to 8 previous errors
+error: aborting due to 7 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs
index 59fdeab9e0a..2987d183e04 100644
--- a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs
+++ b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs
@@ -41,8 +41,7 @@ impl ChannelSender for Sender {
     //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied
     where
         F: Callback<Self::CallbackArg>,
-        //[current]~^ ERROR the trait bound `F: Callback<i32>` is not satisfied
-        //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied
+        //[current]~^ ERROR the trait bound `F: MyFn<i32>` is not satisfied
     {
         Thing
     }
diff --git a/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.rs b/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.rs
index 84bc39d9263..ee47de2c732 100644
--- a/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.rs
+++ b/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.rs
@@ -8,7 +8,6 @@ impl Foo<char> for Bar {
     fn foo<F2: Foo<u8>>(self) -> impl Foo<u8> {
         //~^ ERROR: the trait bound `impl Foo<u8>: Foo<char>` is not satisfied [E0277]
         //~| ERROR: the trait bound `Bar: Foo<u8>` is not satisfied [E0277]
-        //~| ERROR: impl has stricter requirements than trait
         //~| ERROR: the trait bound `F2: Foo<u8>` is not satisfied
         self
     }
diff --git a/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr b/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr
index ae449099987..768224e4c51 100644
--- a/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr
+++ b/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr
@@ -23,15 +23,6 @@ note: required by a bound in `<Bar as Foo<char>>::foo`
 LL |     fn foo<F2: Foo<u8>>(self) -> impl Foo<u8> {
    |                ^^^^^^^ required by this bound in `<Bar as Foo<char>>::foo`
 
-error[E0276]: impl has stricter requirements than trait
-  --> $DIR/return-dont-satisfy-bounds.rs:8:16
-   |
-LL |     fn foo<F2>(self) -> impl Foo<T>;
-   |     -------------------------------- definition of `foo` from trait
-...
-LL |     fn foo<F2: Foo<u8>>(self) -> impl Foo<u8> {
-   |                ^^^^^^^ impl has extra requirement `F2: Foo<u8>`
-
 error[E0277]: the trait bound `Bar: Foo<u8>` is not satisfied
   --> $DIR/return-dont-satisfy-bounds.rs:8:34
    |
@@ -44,7 +35,6 @@ LL |         self
    = help: the trait `Foo<char>` is implemented for `Bar`
    = help: for that trait implementation, expected `char`, found `u8`
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0276, E0277.
-For more information about an error, try `rustc --explain E0276`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs b/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs
index 71a91fe319e..062351afecb 100644
--- a/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs
+++ b/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs
@@ -10,7 +10,6 @@ pub trait Foo {
 impl Foo for () {
     fn bar<'im: 'im>(&'im mut self) -> impl Sized + 'im {}
     //~^ ERROR return type captures more lifetimes than trait definition
-    //~| WARN impl trait in impl method signature does not match trait method signature
 }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.stderr b/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.stderr
index 339e2e6335e..b390a23eef4 100644
--- a/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.stderr
+++ b/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.stderr
@@ -21,22 +21,5 @@ LL |     fn bar<'tr: 'tr>(&'tr mut self) -> impl Sized + use<Self>;
    |                                        ^^^^^^^^^^^^^^^^^^^^^^
    = note: hidden type inferred to be `impl Sized + 'im`
 
-warning: impl trait in impl method signature does not match trait method signature
-  --> $DIR/rpitit-captures-more-method-lifetimes.rs:11:40
-   |
-LL |     fn bar<'tr: 'tr>(&'tr mut self) -> impl Sized + use<Self>;
-   |                                        ---------------------- return type from trait method defined here
-...
-LL |     fn bar<'im: 'im>(&'im mut self) -> impl Sized + 'im {}
-   |                                        ^^^^^^^^^^^^^^^^
-   |
-   = note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
-   = note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
-   = note: `#[warn(refining_impl_trait_reachable)]` on by default
-help: replace the return type so that it matches the trait
-   |
-LL |     fn bar<'im: 'im>(&'im mut self) -> impl Sized {}
-   |                                        ~~~~~~~~~~
-
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors