about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-02-22 17:18:35 +0000
committerMichael Goulet <michael@errs.io>2024-03-01 01:20:49 +0000
commitc87b727a23cd1a04379770edbdc758538a8bc3d0 (patch)
tree16e0bab508cf7d92c1a380134f4883c231e07da8
parent3cb36317cdea5c4de0224f64a0ec6db5cd50a8fd (diff)
downloadrust-c87b727a23cd1a04379770edbdc758538a8bc3d0.tar.gz
rust-c87b727a23cd1a04379770edbdc758538a8bc3d0.zip
Combine sub and eq
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs13
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs30
-rw-r--r--compiler/rustc_infer/src/infer/relate/equate.rs228
-rw-r--r--compiler/rustc_infer/src/infer/relate/glb.rs10
-rw-r--r--compiler/rustc_infer/src/infer/relate/lub.rs10
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs3
-rw-r--r--compiler/rustc_infer/src/infer/relate/sub.rs229
-rw-r--r--compiler/rustc_infer/src/infer/relate/type_relating.rs325
8 files changed, 356 insertions, 492 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index 85f63371659..fd6c29379a3 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -349,12 +349,15 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
 
         debug!(?self.ambient_variance);
         // In a bivariant context this always succeeds.
-        let r =
-            if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? };
+        let r = if self.ambient_variance == ty::Variance::Bivariant {
+            Ok(a)
+        } else {
+            self.relate(a, b)
+        };
 
         self.ambient_variance = old_ambient_variance;
 
-        Ok(r)
+        r
     }
 
     #[instrument(skip(self), level = "debug")]
@@ -579,10 +582,6 @@ impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx
         );
     }
 
-    fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
-        unreachable!("manually overridden to handle ty::Variance::Contravariant ambient variance")
-    }
-
     fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
         self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
             ty::Variance::Covariant => ty::PredicateKind::AliasRelate(
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
index 0852bb4f993..0a550660f94 100644
--- a/compiler/rustc_infer/src/infer/relate/combine.rs
+++ b/compiler/rustc_infer/src/infer/relate/combine.rs
@@ -1,4 +1,6 @@
-//! There are four type combiners: [Equate], [Sub], [Lub], and [Glb].
+//! 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
@@ -22,10 +24,9 @@
 //! [TypeRelation::a_is_expected], so when dealing with contravariance
 //! this should be correctly updated.
 
-use super::equate::Equate;
 use super::glb::Glb;
 use super::lub::Lub;
-use super::sub::Sub;
+use super::type_relating::TypeRelating;
 use super::StructurallyRelateAliases;
 use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
 use crate::traits::{Obligation, PredicateObligations};
@@ -322,12 +323,12 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         &'a mut self,
         structurally_relate_aliases: StructurallyRelateAliases,
         a_is_expected: bool,
-    ) -> Equate<'a, 'infcx, 'tcx> {
-        Equate::new(self, structurally_relate_aliases, a_is_expected)
+    ) -> TypeRelating<'a, 'infcx, 'tcx> {
+        TypeRelating::new(self, a_is_expected, structurally_relate_aliases, ty::Invariant)
     }
 
-    pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'tcx> {
-        Sub::new(self, a_is_expected)
+    pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> TypeRelating<'a, 'infcx, 'tcx> {
+        TypeRelating::new(self, a_is_expected, StructurallyRelateAliases::No, ty::Covariant)
     }
 
     pub fn lub<'a>(&'a mut self, a_is_expected: bool) -> Lub<'a, 'infcx, 'tcx> {
@@ -367,19 +368,8 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
     /// be used if control over the obligation causes is required.
     fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ToPredicate<'tcx>>);
 
-    /// Register an obligation that both types must be related to each other according to
-    /// the [`ty::AliasRelationDirection`] given by [`ObligationEmittingRelation::alias_relate_direction`]
-    fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
-        self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate(
-            a.into(),
-            b.into(),
-            self.alias_relate_direction(),
-        ))]);
-    }
-
-    /// Relation direction emitted for `AliasRelate` predicates, corresponding to the direction
-    /// of the relation.
-    fn alias_relate_direction(&self) -> ty::AliasRelationDirection;
+    /// Register `AliasRelate` obligation(s) that both types must be related to each other.
+    fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>);
 }
 
 fn int_unification_error<'tcx>(
diff --git a/compiler/rustc_infer/src/infer/relate/equate.rs b/compiler/rustc_infer/src/infer/relate/equate.rs
deleted file mode 100644
index 1617a062ea0..00000000000
--- a/compiler/rustc_infer/src/infer/relate/equate.rs
+++ /dev/null
@@ -1,228 +0,0 @@
-use super::combine::{CombineFields, ObligationEmittingRelation};
-use super::StructurallyRelateAliases;
-use crate::infer::BoundRegionConversionTime::HigherRankedType;
-use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
-use crate::traits::PredicateObligations;
-
-use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
-use rustc_middle::ty::GenericArgsRef;
-use rustc_middle::ty::TyVar;
-use rustc_middle::ty::{self, Ty, TyCtxt};
-
-use rustc_hir::def_id::DefId;
-use rustc_span::Span;
-
-/// Ensures `a` is made equal to `b`. Returns `a` on success.
-pub struct Equate<'combine, 'infcx, 'tcx> {
-    fields: &'combine mut CombineFields<'infcx, 'tcx>,
-    structurally_relate_aliases: StructurallyRelateAliases,
-    a_is_expected: bool,
-}
-
-impl<'combine, 'infcx, 'tcx> Equate<'combine, 'infcx, 'tcx> {
-    pub fn new(
-        fields: &'combine mut CombineFields<'infcx, 'tcx>,
-        structurally_relate_aliases: StructurallyRelateAliases,
-        a_is_expected: bool,
-    ) -> Equate<'combine, 'infcx, 'tcx> {
-        Equate { fields, structurally_relate_aliases, a_is_expected }
-    }
-}
-
-impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
-    fn tag(&self) -> &'static str {
-        "Equate"
-    }
-
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.fields.tcx()
-    }
-
-    fn a_is_expected(&self) -> bool {
-        self.a_is_expected
-    }
-
-    fn relate_item_args(
-        &mut self,
-        _item_def_id: DefId,
-        a_arg: GenericArgsRef<'tcx>,
-        b_arg: GenericArgsRef<'tcx>,
-    ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
-        // N.B., once we are equating types, we don't care about
-        // variance, so don't try to lookup the variance here. This
-        // also avoids some cycles (e.g., #41849) since looking up
-        // variance requires computing types which can require
-        // performing trait matching (which then performs equality
-        // unification).
-
-        relate::relate_args_invariantly(self, a_arg, b_arg)
-    }
-
-    fn relate_with_variance<T: Relate<'tcx>>(
-        &mut self,
-        _: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
-        a: T,
-        b: T,
-    ) -> RelateResult<'tcx, T> {
-        self.relate(a, b)
-    }
-
-    #[instrument(skip(self), level = "debug")]
-    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
-        if a == b {
-            return Ok(a);
-        }
-
-        trace!(a = ?a.kind(), b = ?b.kind());
-
-        let infcx = self.fields.infcx;
-
-        let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
-        let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
-
-        match (a.kind(), b.kind()) {
-            (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {
-                infcx.inner.borrow_mut().type_variables().equate(a_id, b_id);
-            }
-
-            (&ty::Infer(TyVar(a_vid)), _) => {
-                infcx.instantiate_ty_var(self, self.a_is_expected, a_vid, ty::Invariant, b)?;
-            }
-
-            (_, &ty::Infer(TyVar(b_vid))) => {
-                infcx.instantiate_ty_var(self, !self.a_is_expected, b_vid, ty::Invariant, a)?;
-            }
-
-            (&ty::Error(e), _) | (_, &ty::Error(e)) => {
-                infcx.set_tainted_by_errors(e);
-                return Ok(Ty::new_error(self.tcx(), 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)?;
-            }
-            (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
-            | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
-                if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
-                    && def_id.is_local()
-                    && !self.fields.infcx.next_trait_solver() =>
-            {
-                self.fields.obligations.extend(
-                    infcx
-                        .handle_opaque_type(
-                            a,
-                            b,
-                            self.a_is_expected(),
-                            &self.fields.trace.cause,
-                            self.param_env(),
-                        )?
-                        .obligations,
-                );
-            }
-            _ => {
-                infcx.super_combine_tys(self, a, b)?;
-            }
-        }
-
-        Ok(a)
-    }
-
-    fn regions(
-        &mut self,
-        a: ty::Region<'tcx>,
-        b: ty::Region<'tcx>,
-    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
-        debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
-        let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
-        self.fields
-            .infcx
-            .inner
-            .borrow_mut()
-            .unwrap_region_constraints()
-            .make_eqregion(origin, a, b);
-        Ok(a)
-    }
-
-    fn consts(
-        &mut self,
-        a: ty::Const<'tcx>,
-        b: ty::Const<'tcx>,
-    ) -> RelateResult<'tcx, ty::Const<'tcx>> {
-        self.fields.infcx.super_combine_consts(self, a, b)
-    }
-
-    fn binders<T>(
-        &mut self,
-        a: ty::Binder<'tcx, T>,
-        b: ty::Binder<'tcx, T>,
-    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
-    where
-        T: Relate<'tcx>,
-    {
-        // A binder is equal to itself if it's structurally equal to itself
-        if a == b {
-            return Ok(a);
-        }
-
-        if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) {
-            // Fast path for the common case.
-            self.relate(a, b)?;
-        } else {
-            // 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`.
-
-            let span = self.fields.trace.cause.span;
-            let infcx = self.fields.infcx;
-
-            // Check if `exists<..> A == for<..> B`
-            infcx.enter_forall(b, |b| {
-                let a = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, a);
-                self.relate(a, b)
-            })?;
-
-            // Check if `exists<..> B == for<..> A`.
-            infcx.enter_forall(a, |a| {
-                let b = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, b);
-                self.relate(a, b)
-            })?;
-        }
-        Ok(a)
-    }
-}
-
-impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> {
-    fn span(&self) -> Span {
-        self.fields.trace.span()
-    }
-
-    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
-        self.structurally_relate_aliases
-    }
-
-    fn param_env(&self) -> ty::ParamEnv<'tcx> {
-        self.fields.param_env
-    }
-
-    fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
-        self.fields.register_predicates(obligations);
-    }
-
-    fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
-        self.fields.register_obligations(obligations);
-    }
-
-    fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
-        ty::AliasRelationDirection::Equate
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs
index 52a2f4c7347..9b77e6888b2 100644
--- a/compiler/rustc_infer/src/infer/relate/glb.rs
+++ b/compiler/rustc_infer/src/infer/relate/glb.rs
@@ -158,8 +158,12 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
         self.fields.register_obligations(obligations);
     }
 
-    fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
-        // FIXME(deferred_projection_equality): This isn't right, I think?
-        ty::AliasRelationDirection::Equate
+    fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
+        self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate(
+            a.into(),
+            b.into(),
+            // FIXME(deferred_projection_equality): This isn't right, I think?
+            ty::AliasRelationDirection::Equate,
+        ))]);
     }
 }
diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs
index fa0da64ca65..db04e3231d6 100644
--- a/compiler/rustc_infer/src/infer/relate/lub.rs
+++ b/compiler/rustc_infer/src/infer/relate/lub.rs
@@ -158,8 +158,12 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
         self.fields.register_obligations(obligations)
     }
 
-    fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
-        // FIXME(deferred_projection_equality): This isn't right, I think?
-        ty::AliasRelationDirection::Equate
+    fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
+        self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate(
+            a.into(),
+            b.into(),
+            // FIXME(deferred_projection_equality): This isn't right, I think?
+            ty::AliasRelationDirection::Equate,
+        ))]);
     }
 }
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
index 8619cc502ad..86a01130167 100644
--- a/compiler/rustc_infer/src/infer/relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -2,13 +2,12 @@
 //! (except for some relations used for diagnostics and heuristics in the compiler).
 
 pub(super) mod combine;
-mod equate;
 mod generalize;
 mod glb;
 mod higher_ranked;
 mod lattice;
 mod lub;
-mod sub;
+mod type_relating;
 
 /// Whether aliases should be related structurally or not. Used
 /// to adjust the behavior of generalization and combine.
diff --git a/compiler/rustc_infer/src/infer/relate/sub.rs b/compiler/rustc_infer/src/infer/relate/sub.rs
deleted file mode 100644
index 2cc8d0d3b10..00000000000
--- a/compiler/rustc_infer/src/infer/relate/sub.rs
+++ /dev/null
@@ -1,229 +0,0 @@
-use super::combine::CombineFields;
-use super::StructurallyRelateAliases;
-use crate::infer::{DefineOpaqueTypes, ObligationEmittingRelation, SubregionOrigin};
-use crate::traits::{Obligation, PredicateObligations};
-
-use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
-use rustc_middle::ty::visit::TypeVisitableExt;
-use rustc_middle::ty::TyVar;
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::Span;
-use std::mem;
-
-/// Ensures `a` is made a subtype of `b`. Returns `a` on success.
-pub struct Sub<'combine, 'a, 'tcx> {
-    fields: &'combine mut CombineFields<'a, 'tcx>,
-    a_is_expected: bool,
-}
-
-impl<'combine, 'infcx, 'tcx> Sub<'combine, 'infcx, 'tcx> {
-    pub fn new(
-        f: &'combine mut CombineFields<'infcx, 'tcx>,
-        a_is_expected: bool,
-    ) -> Sub<'combine, 'infcx, 'tcx> {
-        Sub { fields: f, a_is_expected }
-    }
-
-    fn with_expected_switched<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R {
-        self.a_is_expected = !self.a_is_expected;
-        let result = f(self);
-        self.a_is_expected = !self.a_is_expected;
-        result
-    }
-}
-
-impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
-    fn tag(&self) -> &'static str {
-        "Sub"
-    }
-
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.fields.infcx.tcx
-    }
-
-    fn a_is_expected(&self) -> bool {
-        self.a_is_expected
-    }
-
-    fn with_cause<F, R>(&mut self, cause: Cause, f: F) -> R
-    where
-        F: FnOnce(&mut Self) -> R,
-    {
-        debug!("sub with_cause={:?}", cause);
-        let old_cause = mem::replace(&mut self.fields.cause, Some(cause));
-        let r = f(self);
-        debug!("sub old_cause={:?}", old_cause);
-        self.fields.cause = old_cause;
-        r
-    }
-
-    fn relate_with_variance<T: Relate<'tcx>>(
-        &mut self,
-        variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
-        a: T,
-        b: T,
-    ) -> RelateResult<'tcx, T> {
-        match variance {
-            ty::Invariant => {
-                self.fields.equate(StructurallyRelateAliases::No, self.a_is_expected).relate(a, b)
-            }
-            ty::Covariant => self.relate(a, b),
-            ty::Bivariant => Ok(a),
-            ty::Contravariant => self.with_expected_switched(|this| this.relate(b, a)),
-        }
-    }
-
-    #[instrument(skip(self), level = "debug")]
-    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
-        if a == b {
-            return Ok(a);
-        }
-
-        let infcx = self.fields.infcx;
-        let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
-        let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
-
-        match (a.kind(), b.kind()) {
-            (&ty::Infer(TyVar(_)), &ty::Infer(TyVar(_))) => {
-                // Shouldn't have any LBR here, so we can safely put
-                // this under a binder below without fear of accidental
-                // capture.
-                assert!(!a.has_escaping_bound_vars());
-                assert!(!b.has_escaping_bound_vars());
-
-                // can't make progress on `A <: B` if both A and B are
-                // type variables, so record an obligation.
-                self.fields.obligations.push(Obligation::new(
-                    self.tcx(),
-                    self.fields.trace.cause.clone(),
-                    self.fields.param_env,
-                    ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
-                        a_is_expected: self.a_is_expected,
-                        a,
-                        b,
-                    })),
-                ));
-
-                Ok(a)
-            }
-            (&ty::Infer(TyVar(a_vid)), _) => {
-                infcx.instantiate_ty_var(self, self.a_is_expected, a_vid, ty::Covariant, b)?;
-                Ok(a)
-            }
-            (_, &ty::Infer(TyVar(b_vid))) => {
-                infcx.instantiate_ty_var(self, !self.a_is_expected, b_vid, ty::Contravariant, a)?;
-                Ok(a)
-            }
-
-            (&ty::Error(e), _) | (_, &ty::Error(e)) => {
-                infcx.set_tainted_by_errors(e);
-                Ok(Ty::new_error(self.tcx(), 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 => {
-                self.fields.infcx.super_combine_tys(self, a, b)?;
-                Ok(a)
-            }
-            (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
-            | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
-                if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
-                    && def_id.is_local()
-                    && !self.fields.infcx.next_trait_solver() =>
-            {
-                self.fields.obligations.extend(
-                    infcx
-                        .handle_opaque_type(
-                            a,
-                            b,
-                            self.a_is_expected,
-                            &self.fields.trace.cause,
-                            self.param_env(),
-                        )?
-                        .obligations,
-                );
-                Ok(a)
-            }
-            _ => {
-                self.fields.infcx.super_combine_tys(self, a, b)?;
-                Ok(a)
-            }
-        }
-    }
-
-    fn regions(
-        &mut self,
-        a: ty::Region<'tcx>,
-        b: ty::Region<'tcx>,
-    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
-        debug!("{}.regions({:?}, {:?}) self.cause={:?}", self.tag(), a, b, self.fields.cause);
-
-        // FIXME -- we have more fine-grained information available
-        // from the "cause" field, we could perhaps give more tailored
-        // error messages.
-        let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
-        // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a)
-        self.fields
-            .infcx
-            .inner
-            .borrow_mut()
-            .unwrap_region_constraints()
-            .make_subregion(origin, b, a);
-
-        Ok(a)
-    }
-
-    fn consts(
-        &mut self,
-        a: ty::Const<'tcx>,
-        b: ty::Const<'tcx>,
-    ) -> RelateResult<'tcx, ty::Const<'tcx>> {
-        self.fields.infcx.super_combine_consts(self, a, b)
-    }
-
-    fn binders<T>(
-        &mut self,
-        a: ty::Binder<'tcx, T>,
-        b: ty::Binder<'tcx, T>,
-    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
-    where
-        T: Relate<'tcx>,
-    {
-        // A binder is always a subtype of itself if it's structurally equal to itself
-        if a == b {
-            return Ok(a);
-        }
-
-        self.fields.higher_ranked_sub(a, b, self.a_is_expected)?;
-        Ok(a)
-    }
-}
-
-impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> {
-    fn span(&self) -> Span {
-        self.fields.trace.span()
-    }
-
-    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
-        StructurallyRelateAliases::No
-    }
-
-    fn param_env(&self) -> ty::ParamEnv<'tcx> {
-        self.fields.param_env
-    }
-
-    fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
-        self.fields.register_predicates(obligations);
-    }
-
-    fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
-        self.fields.register_obligations(obligations);
-    }
-
-    fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
-        ty::AliasRelationDirection::Subtype
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs
new file mode 100644
index 00000000000..c4de324e6ff
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs
@@ -0,0 +1,325 @@
+use super::combine::CombineFields;
+use crate::infer::{
+    DefineOpaqueTypes, ObligationEmittingRelation, StructurallyRelateAliases, SubregionOrigin,
+};
+use crate::traits::{Obligation, PredicateObligations};
+
+use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::visit::TypeVisitableExt;
+use rustc_middle::ty::TyVar;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::Span;
+use std::mem;
+
+/// Enforce that `a` is equal to or a subtype of `b`.
+pub struct TypeRelating<'combine, 'a, 'tcx> {
+    fields: &'combine mut CombineFields<'a, 'tcx>,
+    a_is_expected: bool,
+    structurally_relate_aliases: StructurallyRelateAliases,
+    ambient_variance: ty::Variance,
+}
+
+impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> {
+    pub fn new(
+        f: &'combine mut CombineFields<'infcx, 'tcx>,
+        a_is_expected: bool,
+        structurally_relate_aliases: StructurallyRelateAliases,
+        ambient_variance: ty::Variance,
+    ) -> TypeRelating<'combine, 'infcx, 'tcx> {
+        TypeRelating { fields: f, a_is_expected, structurally_relate_aliases, ambient_variance }
+    }
+}
+
+impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
+    fn tag(&self) -> &'static str {
+        "TypeRelating"
+    }
+
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.fields.infcx.tcx
+    }
+
+    fn a_is_expected(&self) -> bool {
+        self.a_is_expected
+    }
+
+    fn with_cause<F, R>(&mut self, cause: Cause, f: F) -> R
+    where
+        F: FnOnce(&mut Self) -> R,
+    {
+        debug!("sub with_cause={:?}", cause);
+        let old_cause = mem::replace(&mut self.fields.cause, Some(cause));
+        let r = f(self);
+        debug!("sub old_cause={:?}", old_cause);
+        self.fields.cause = old_cause;
+        r
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, 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 = "debug")]
+    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        if a == b {
+            return Ok(a);
+        }
+
+        let infcx = self.fields.infcx;
+        let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
+        let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
+
+        match (a.kind(), b.kind()) {
+            (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {
+                // Shouldn't have any LBR here, so we can safely put
+                // this under a binder below without fear of accidental
+                // capture.
+                assert!(!a.has_escaping_bound_vars());
+                assert!(!b.has_escaping_bound_vars());
+
+                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.fields.obligations.push(Obligation::new(
+                            self.tcx(),
+                            self.fields.trace.cause.clone(),
+                            self.fields.param_env,
+                            ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
+                                a_is_expected: self.a_is_expected,
+                                a,
+                                b,
+                            })),
+                        ));
+                    }
+                    ty::Contravariant => {
+                        // can't make progress on `B <: A` if both A and B are
+                        // type variables, so record an obligation.
+                        self.fields.obligations.push(Obligation::new(
+                            self.tcx(),
+                            self.fields.trace.cause.clone(),
+                            self.fields.param_env,
+                            ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
+                                a_is_expected: !self.a_is_expected,
+                                a: b,
+                                b: a,
+                            })),
+                        ));
+                    }
+                    ty::Invariant => {
+                        infcx.inner.borrow_mut().type_variables().equate(a_id, b_id);
+                    }
+                    ty::Bivariant => {
+                        unreachable!("Expected bivariance to be handled in relate_with_variance")
+                    }
+                }
+            }
+
+            (&ty::Infer(TyVar(a_vid)), _) => {
+                infcx.instantiate_ty_var(
+                    self,
+                    self.a_is_expected,
+                    a_vid,
+                    self.ambient_variance,
+                    b,
+                )?;
+            }
+            (_, &ty::Infer(TyVar(b_vid))) => {
+                infcx.instantiate_ty_var(
+                    self,
+                    !self.a_is_expected,
+                    b_vid,
+                    self.ambient_variance.xform(ty::Contravariant),
+                    a,
+                )?;
+            }
+
+            (&ty::Error(e), _) | (_, &ty::Error(e)) => {
+                infcx.set_tainted_by_errors(e);
+                return Ok(Ty::new_error(self.tcx(), 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)?;
+            }
+
+            (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
+            | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
+                if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
+                    && def_id.is_local()
+                    && !infcx.next_trait_solver() =>
+            {
+                self.fields.obligations.extend(
+                    infcx
+                        .handle_opaque_type(
+                            a,
+                            b,
+                            self.a_is_expected,
+                            &self.fields.trace.cause,
+                            self.param_env(),
+                        )?
+                        .obligations,
+                );
+            }
+
+            _ => {
+                infcx.super_combine_tys(self, a, b)?;
+            }
+        }
+
+        Ok(a)
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug!("{}.regions({:?}, {:?}) self.cause={:?}", self.tag(), a, b, self.fields.cause);
+
+        // FIXME -- we have more fine-grained information available
+        // from the "cause" field, we could perhaps give more tailored
+        // error messages.
+        let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
+
+        match self.ambient_variance {
+            // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a)
+            ty::Covariant => {
+                self.fields
+                    .infcx
+                    .inner
+                    .borrow_mut()
+                    .unwrap_region_constraints()
+                    .make_subregion(origin, b, a);
+            }
+            // Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b)
+            ty::Contravariant => {
+                self.fields
+                    .infcx
+                    .inner
+                    .borrow_mut()
+                    .unwrap_region_constraints()
+                    .make_subregion(origin, a, b);
+            }
+            ty::Invariant => {
+                // The order of `make_eqregion` apparently matters.
+                self.fields
+                    .infcx
+                    .inner
+                    .borrow_mut()
+                    .unwrap_region_constraints()
+                    .make_eqregion(origin, a, b);
+            }
+            ty::Bivariant => {
+                unreachable!("Expected bivariance to be handled in relate_with_variance")
+            }
+        }
+
+        Ok(a)
+    }
+
+    fn consts(
+        &mut self,
+        a: ty::Const<'tcx>,
+        b: ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, ty::Const<'tcx>> {
+        self.fields.infcx.super_combine_consts(self, a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        if a == b {
+            // Do nothing
+        } else if let Some(a) = a.no_bound_vars()
+            && let Some(b) = b.no_bound_vars()
+        {
+            self.relate(a, b)?;
+        } else {
+            match self.ambient_variance {
+                ty::Covariant => {
+                    self.fields.higher_ranked_sub(a, b, self.a_is_expected)?;
+                }
+                ty::Contravariant => {
+                    self.fields.higher_ranked_sub(b, a, !self.a_is_expected)?;
+                }
+                ty::Invariant => {
+                    self.fields.higher_ranked_sub(a, b, self.a_is_expected)?;
+                    self.fields.higher_ranked_sub(b, a, !self.a_is_expected)?;
+                }
+                ty::Bivariant => {
+                    unreachable!("Expected bivariance to be handled in relate_with_variance")
+                }
+            }
+        }
+
+        Ok(a)
+    }
+}
+
+impl<'tcx> ObligationEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
+    fn span(&self) -> Span {
+        self.fields.trace.span()
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.fields.param_env
+    }
+
+    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
+        self.structurally_relate_aliases
+    }
+
+    fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
+        self.fields.register_predicates(obligations);
+    }
+
+    fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
+        self.fields.register_obligations(obligations);
+    }
+
+    fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
+        self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
+            ty::Variance::Covariant => ty::PredicateKind::AliasRelate(
+                a.into(),
+                b.into(),
+                ty::AliasRelationDirection::Subtype,
+            ),
+            // a :> b is b <: a
+            ty::Variance::Contravariant => ty::PredicateKind::AliasRelate(
+                b.into(),
+                a.into(),
+                ty::AliasRelationDirection::Subtype,
+            ),
+            ty::Variance::Invariant => ty::PredicateKind::AliasRelate(
+                a.into(),
+                b.into(),
+                ty::AliasRelationDirection::Equate,
+            ),
+            ty::Variance::Bivariant => {
+                unreachable!("Expected bivariance to be handled in relate_with_variance")
+            }
+        })]);
+    }
+}