about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-05-12 02:12:17 +0000
committerbors <bors@rust-lang.org>2024-05-12 02:12:17 +0000
commitee97564e3a9f9ac8c65103abb37c6aa48d95bfa2 (patch)
tree1b811dfd8d71d29d3553ace19ba168fbfeac93bd
parent8b64adc8cdf08f927d01bf9b02cc493ca5bcb5a1 (diff)
parentd13e5c483dcdfed9724d03dbb2c28827fda4c365 (diff)
downloadrust-ee97564e3a9f9ac8c65103abb37c6aa48d95bfa2.tar.gz
rust-ee97564e3a9f9ac8c65103abb37c6aa48d95bfa2.zip
Auto merge of #125001 - compiler-errors:uplift-trait-predicate, r=lcnr
Uplift various `*Predicate` types into `rustc_type_ir`

Uplifts `ProjectionPredicate`, `ExistentialTraitRef`, `ExistentialProjection`, `TraitPredicate`, `NormalizesTo`, `CoercePredicate`, and `SubtypePredicate`.

Adds `rustc_type_ir_macros`, which semi-duplicates the derive for `TypeVisitable`, `TypeFoldable`, and `Lift`, but in a way that is interner-agnostic.

Moves `rustc_type_ir::trait_ref` to `rustc_type_ir::predicate`. The specific placement of all these structs doesn't matter b/c of glob imports, tho.
-rw-r--r--Cargo.lock11
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs2
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs5
-rw-r--r--compiler/rustc_middle/src/ty/context.rs15
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs69
-rw-r--r--compiler/rustc_middle/src/ty/predicate.rs278
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs97
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs27
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs25
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs30
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs3
-rw-r--r--compiler/rustc_type_ir/Cargo.toml3
-rw-r--r--compiler/rustc_type_ir/src/canonical.rs151
-rw-r--r--compiler/rustc_type_ir/src/const_kind.rs4
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs27
-rw-r--r--compiler/rustc_type_ir/src/interner.rs26
-rw-r--r--compiler/rustc_type_ir/src/ir_print.rs24
-rw-r--r--compiler/rustc_type_ir/src/lib.rs4
-rw-r--r--compiler/rustc_type_ir/src/macros.rs1
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs434
-rw-r--r--compiler/rustc_type_ir/src/predicate_kind.rs127
-rw-r--r--compiler/rustc_type_ir/src/region_kind.rs5
-rw-r--r--compiler/rustc_type_ir/src/trait_ref.rs109
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs32
-rw-r--r--compiler/rustc_type_ir_macros/Cargo.toml15
-rw-r--r--compiler/rustc_type_ir_macros/src/lib.rs159
30 files changed, 886 insertions, 813 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f49bcf7c865..2aed7ebacd5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4837,10 +4837,21 @@ dependencies = [
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
+ "rustc_type_ir_macros",
  "smallvec",
 ]
 
 [[package]]
+name = "rustc_type_ir_macros"
+version = "0.0.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.62",
+ "synstructure",
+]
+
+[[package]]
 name = "rustc_version"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 40560a5ad79..2cc0167dbaa 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -100,6 +100,12 @@ impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::TraitRef<I> {
     }
 }
 
+impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::ExistentialTraitRef<I> {
+    fn into_diag_arg(self) -> DiagArgValue {
+        self.to_string().into_diag_arg()
+    }
+}
+
 into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
 
 impl IntoDiagArg for bool {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
index 297cfe7027e..37d4d4ec355 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
@@ -7,7 +7,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
 use rustc_middle::ty::fold::BottomUpFolder;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable};
 use rustc_middle::ty::{DynKind, ToPredicate};
 use rustc_span::{ErrorGuaranteed, Span};
 use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 1ea1f496e50..1d2e12ec575 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -1,9 +1,8 @@
 use rustc_hir as hir;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
-use rustc_middle::ty::{
-    self, fold::BottomUpFolder, print::TraitPredPrintModifiersAndPath, Ty, TypeFoldable,
-};
+use rustc_middle::ty::print::{PrintTraitPredicateExt as _, TraitPredPrintModifiersAndPath};
+use rustc_middle::ty::{self, fold::BottomUpFolder, Ty, TypeFoldable};
 use rustc_session::{declare_lint, declare_lint_pass};
 use rustc_span::{symbol::kw, Span};
 use rustc_trait_selection::traits;
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 0464be2df06..f9d1a77c3d9 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -26,6 +26,7 @@ use crate::traits::solve;
 use crate::traits::solve::{
     ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, PredefinedOpaquesData,
 };
+use crate::ty::predicate::ExistentialPredicateStableCmpExt as _;
 use crate::ty::{
     self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, ConstData,
     GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, ParamTy, Pattern,
@@ -99,17 +100,17 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type CanonicalVars = CanonicalVarInfos<'tcx>;
 
     type Ty = Ty<'tcx>;
-    type Pat = Pattern<'tcx>;
     type Tys = &'tcx List<Ty<'tcx>>;
     type AliasTy = ty::AliasTy<'tcx>;
     type ParamTy = ParamTy;
     type BoundTy = ty::BoundTy;
     type PlaceholderTy = ty::PlaceholderType;
-
     type ErrorGuaranteed = ErrorGuaranteed;
+
     type BoundExistentialPredicates = &'tcx List<PolyExistentialPredicate<'tcx>>;
     type PolyFnSig = PolyFnSig<'tcx>;
     type AllocId = crate::mir::interpret::AllocId;
+    type Pat = Pattern<'tcx>;
 
     type Const = ty::Const<'tcx>;
     type AliasConst = ty::UnevaluatedConst<'tcx>;
@@ -121,8 +122,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
 
     type Region = Region<'tcx>;
     type EarlyParamRegion = ty::EarlyParamRegion;
-    type BoundRegion = ty::BoundRegion;
     type LateParamRegion = ty::LateParamRegion;
+    type BoundRegion = ty::BoundRegion;
     type InferRegion = ty::RegionVid;
     type PlaceholderRegion = ty::PlaceholderRegion;
 
@@ -146,6 +147,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.generics_of(def_id)
     }
 
+    fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs {
+        self.mk_args(args)
+    }
+
     fn check_and_mk_args(
         self,
         def_id: DefId,
@@ -153,6 +158,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     ) -> ty::GenericArgsRef<'tcx> {
         self.check_and_mk_args(def_id, args)
     }
+
+    fn parent(self, def_id: Self::DefId) -> Self::DefId {
+        self.parent(def_id)
+    }
 }
 
 type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 12cefc23233..4d3b92bf2af 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -96,13 +96,13 @@ pub use self::list::{List, ListWithCachedTypeInfo};
 pub use self::parameterized::ParameterizedOverTcx;
 pub use self::pattern::{Pattern, PatternKind};
 pub use self::predicate::{
-    Clause, ClauseKind, CoercePredicate, ExistentialPredicate, ExistentialProjection,
-    ExistentialTraitRef, NormalizesTo, OutlivesPredicate, PolyCoercePredicate,
-    PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef,
-    PolyProjectionPredicate, PolyRegionOutlivesPredicate, PolySubtypePredicate, PolyTraitPredicate,
-    PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, PredicateKind, ProjectionPredicate,
-    RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitPredicate,
-    TraitRef, TypeOutlivesPredicate,
+    Clause, ClauseKind, CoercePredicate, ExistentialPredicate, ExistentialPredicateStableCmpExt,
+    ExistentialProjection, ExistentialTraitRef, NormalizesTo, OutlivesPredicate,
+    PolyCoercePredicate, PolyExistentialPredicate, PolyExistentialProjection,
+    PolyExistentialTraitRef, PolyProjectionPredicate, PolyRegionOutlivesPredicate,
+    PolySubtypePredicate, PolyTraitPredicate, PolyTraitRef, PolyTypeOutlivesPredicate, Predicate,
+    PredicateKind, ProjectionPredicate, RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef,
+    ToPredicate, TraitPredicate, TraitRef, TypeOutlivesPredicate,
 };
 pub use self::region::{
     BoundRegion, BoundRegionKind, BoundRegionKind::*, EarlyParamRegion, LateParamRegion, Region,
@@ -277,61 +277,6 @@ pub enum ImplSubject<'tcx> {
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)]
 #[derive(TypeFoldable, TypeVisitable)]
-pub enum ImplPolarity {
-    /// `impl Trait for Type`
-    Positive,
-    /// `impl !Trait for Type`
-    Negative,
-    /// `#[rustc_reservation_impl] impl Trait for Type`
-    ///
-    /// This is a "stability hack", not a real Rust feature.
-    /// See #64631 for details.
-    Reservation,
-}
-
-impl fmt::Display for ImplPolarity {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::Positive => f.write_str("positive"),
-            Self::Negative => f.write_str("negative"),
-            Self::Reservation => f.write_str("reservation"),
-        }
-    }
-}
-
-/// Polarity for a trait predicate. May either be negative or positive.
-/// Distinguished from [`ImplPolarity`] since we never compute goals with
-/// "reservation" level.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub enum PredicatePolarity {
-    /// `Type: Trait`
-    Positive,
-    /// `Type: !Trait`
-    Negative,
-}
-
-impl PredicatePolarity {
-    /// Flips polarity by turning `Positive` into `Negative` and `Negative` into `Positive`.
-    pub fn flip(&self) -> PredicatePolarity {
-        match self {
-            PredicatePolarity::Positive => PredicatePolarity::Negative,
-            PredicatePolarity::Negative => PredicatePolarity::Positive,
-        }
-    }
-}
-
-impl fmt::Display for PredicatePolarity {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::Positive => f.write_str("positive"),
-            Self::Negative => f.write_str("negative"),
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)]
-#[derive(TypeFoldable, TypeVisitable)]
 pub enum Asyncness {
     Yes,
     No,
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index 5387036d409..c1de23b2498 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -1,22 +1,28 @@
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::intern::Interned;
-use rustc_errors::{DiagArgValue, IntoDiagArg};
 use rustc_hir::def_id::DefId;
-use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
-use rustc_type_ir::ClauseKind as IrClauseKind;
-use rustc_type_ir::PredicateKind as IrPredicateKind;
-use rustc_type_ir::TraitRef as IrTraitRef;
+use rustc_macros::{
+    extension, HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable,
+};
+use rustc_type_ir as ir;
 use std::cmp::Ordering;
 
-use crate::ty::visit::TypeVisitableExt;
 use crate::ty::{
-    self, AliasTy, Binder, DebruijnIndex, DebugWithInfcx, EarlyBinder, GenericArgsRef,
-    PredicatePolarity, Term, Ty, TyCtxt, TypeFlags, WithCachedTypeInfo,
+    self, Binder, DebruijnIndex, EarlyBinder, PredicatePolarity, Term, Ty, TyCtxt, TypeFlags,
+    WithCachedTypeInfo,
 };
 
-pub type TraitRef<'tcx> = IrTraitRef<TyCtxt<'tcx>>;
-pub type ClauseKind<'tcx> = IrClauseKind<TyCtxt<'tcx>>;
-pub type PredicateKind<'tcx> = IrPredicateKind<TyCtxt<'tcx>>;
+pub type TraitRef<'tcx> = ir::TraitRef<TyCtxt<'tcx>>;
+pub type ProjectionPredicate<'tcx> = ir::ProjectionPredicate<TyCtxt<'tcx>>;
+pub type ExistentialPredicate<'tcx> = ir::ExistentialPredicate<TyCtxt<'tcx>>;
+pub type ExistentialTraitRef<'tcx> = ir::ExistentialTraitRef<TyCtxt<'tcx>>;
+pub type ExistentialProjection<'tcx> = ir::ExistentialProjection<TyCtxt<'tcx>>;
+pub type TraitPredicate<'tcx> = ir::TraitPredicate<TyCtxt<'tcx>>;
+pub type ClauseKind<'tcx> = ir::ClauseKind<TyCtxt<'tcx>>;
+pub type PredicateKind<'tcx> = ir::PredicateKind<TyCtxt<'tcx>>;
+pub type NormalizesTo<'tcx> = ir::NormalizesTo<TyCtxt<'tcx>>;
+pub type CoercePredicate<'tcx> = ir::CoercePredicate<TyCtxt<'tcx>>;
+pub type SubtypePredicate<'tcx> = ir::SubtypePredicate<TyCtxt<'tcx>>;
 
 /// A statement that can be proven by a trait solver. This includes things that may
 /// show up in where clauses, such as trait predicates and projection predicates,
@@ -195,43 +201,25 @@ impl<'tcx> Clause<'tcx> {
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub enum ExistentialPredicate<'tcx> {
-    /// E.g., `Iterator`.
-    Trait(ExistentialTraitRef<'tcx>),
-    /// E.g., `Iterator::Item = T`.
-    Projection(ExistentialProjection<'tcx>),
-    /// E.g., `Send`.
-    AutoTrait(DefId),
-}
-
-impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ExistentialPredicate<'tcx> {
-    fn fmt<Infcx: rustc_type_ir::InferCtxtLike<Interner = TyCtxt<'tcx>>>(
-        this: rustc_type_ir::WithInfcx<'_, Infcx, &Self>,
-        f: &mut std::fmt::Formatter<'_>,
-    ) -> std::fmt::Result {
-        std::fmt::Debug::fmt(&this.data, f)
-    }
-}
-
+#[extension(pub trait ExistentialPredicateStableCmpExt<'tcx>)]
 impl<'tcx> ExistentialPredicate<'tcx> {
     /// Compares via an ordering that will not change if modules are reordered or other changes are
     /// made to the tree. In particular, this ordering is preserved across incremental compilations.
-    pub fn stable_cmp(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Ordering {
-        use self::ExistentialPredicate::*;
+    fn stable_cmp(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Ordering {
         match (*self, *other) {
-            (Trait(_), Trait(_)) => Ordering::Equal,
-            (Projection(ref a), Projection(ref b)) => {
+            (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => Ordering::Equal,
+            (ExistentialPredicate::Projection(ref a), ExistentialPredicate::Projection(ref b)) => {
                 tcx.def_path_hash(a.def_id).cmp(&tcx.def_path_hash(b.def_id))
             }
-            (AutoTrait(ref a), AutoTrait(ref b)) => {
+            (ExistentialPredicate::AutoTrait(ref a), ExistentialPredicate::AutoTrait(ref b)) => {
                 tcx.def_path_hash(*a).cmp(&tcx.def_path_hash(*b))
             }
-            (Trait(_), _) => Ordering::Less,
-            (Projection(_), Trait(_)) => Ordering::Greater,
-            (Projection(_), _) => Ordering::Less,
-            (AutoTrait(_), _) => Ordering::Greater,
+            (ExistentialPredicate::Trait(_), _) => Ordering::Less,
+            (ExistentialPredicate::Projection(_), ExistentialPredicate::Trait(_)) => {
+                Ordering::Greater
+            }
+            (ExistentialPredicate::Projection(_), _) => Ordering::Less,
+            (ExistentialPredicate::AutoTrait(_), _) => Ordering::Greater,
         }
     }
 }
@@ -340,52 +328,6 @@ impl<'tcx> PolyTraitRef<'tcx> {
     }
 }
 
-/// An existential reference to a trait, where `Self` is erased.
-/// For example, the trait object `Trait<'a, 'b, X, Y>` is:
-/// ```ignore (illustrative)
-/// exists T. T: Trait<'a, 'b, X, Y>
-/// ```
-/// The generic parameters don't include the erased `Self`, only trait
-/// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above).
-#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct ExistentialTraitRef<'tcx> {
-    pub def_id: DefId,
-    pub args: GenericArgsRef<'tcx>,
-}
-
-impl<'tcx> ExistentialTraitRef<'tcx> {
-    pub fn erase_self_ty(
-        tcx: TyCtxt<'tcx>,
-        trait_ref: ty::TraitRef<'tcx>,
-    ) -> ty::ExistentialTraitRef<'tcx> {
-        // Assert there is a Self.
-        trait_ref.args.type_at(0);
-
-        ty::ExistentialTraitRef {
-            def_id: trait_ref.def_id,
-            args: tcx.mk_args(&trait_ref.args[1..]),
-        }
-    }
-
-    /// Object types don't have a self type specified. Therefore, when
-    /// we convert the principal trait-ref into a normal trait-ref,
-    /// you must give *some* self type. A common choice is `mk_err()`
-    /// or some placeholder type.
-    pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::TraitRef<'tcx> {
-        // otherwise the escaping vars would be captured by the binder
-        // debug_assert!(!self_ty.has_escaping_bound_vars());
-
-        ty::TraitRef::new(tcx, self.def_id, [self_ty.into()].into_iter().chain(self.args.iter()))
-    }
-}
-
-impl<'tcx> IntoDiagArg for ExistentialTraitRef<'tcx> {
-    fn into_diag_arg(self) -> DiagArgValue {
-        self.to_string().into_diag_arg()
-    }
-}
-
 pub type PolyExistentialTraitRef<'tcx> = ty::Binder<'tcx, ExistentialTraitRef<'tcx>>;
 
 impl<'tcx> PolyExistentialTraitRef<'tcx> {
@@ -402,62 +344,8 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
     }
 }
 
-/// A `ProjectionPredicate` for an `ExistentialTraitRef`.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct ExistentialProjection<'tcx> {
-    pub def_id: DefId,
-    pub args: GenericArgsRef<'tcx>,
-    pub term: Term<'tcx>,
-}
-
 pub type PolyExistentialProjection<'tcx> = ty::Binder<'tcx, ExistentialProjection<'tcx>>;
 
-impl<'tcx> ExistentialProjection<'tcx> {
-    /// Extracts the underlying existential trait reference from this projection.
-    /// For example, if this is a projection of `exists T. <T as Iterator>::Item == X`,
-    /// then this function would return an `exists T. T: Iterator` existential trait
-    /// reference.
-    pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::ExistentialTraitRef<'tcx> {
-        let def_id = tcx.parent(self.def_id);
-        let args_count = tcx.generics_of(def_id).count() - 1;
-        let args = tcx.mk_args(&self.args[..args_count]);
-        ty::ExistentialTraitRef { def_id, args }
-    }
-
-    pub fn with_self_ty(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        self_ty: Ty<'tcx>,
-    ) -> ty::ProjectionPredicate<'tcx> {
-        // otherwise the escaping regions would be captured by the binders
-        debug_assert!(!self_ty.has_escaping_bound_vars());
-
-        ty::ProjectionPredicate {
-            projection_ty: AliasTy::new(
-                tcx,
-                self.def_id,
-                [self_ty.into()].into_iter().chain(self.args),
-            ),
-            term: self.term,
-        }
-    }
-
-    pub fn erase_self_ty(
-        tcx: TyCtxt<'tcx>,
-        projection_predicate: ty::ProjectionPredicate<'tcx>,
-    ) -> Self {
-        // Assert there is a Self.
-        projection_predicate.projection_ty.args.type_at(0);
-
-        Self {
-            def_id: projection_predicate.projection_ty.def_id,
-            args: tcx.mk_args(&projection_predicate.projection_ty.args[1..]),
-            term: projection_predicate.term,
-        }
-    }
-}
-
 impl<'tcx> PolyExistentialProjection<'tcx> {
     pub fn with_self_ty(
         &self,
@@ -578,37 +466,8 @@ impl<'tcx> Clause<'tcx> {
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct TraitPredicate<'tcx> {
-    pub trait_ref: TraitRef<'tcx>,
-
-    /// If polarity is Positive: we are proving that the trait is implemented.
-    ///
-    /// If polarity is Negative: we are proving that a negative impl of this trait
-    /// exists. (Note that coherence also checks whether negative impls of supertraits
-    /// exist via a series of predicates.)
-    ///
-    /// If polarity is Reserved: that's a bug.
-    pub polarity: PredicatePolarity,
-}
-
 pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>;
 
-impl<'tcx> TraitPredicate<'tcx> {
-    pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
-        Self { trait_ref: self.trait_ref.with_self_ty(tcx, self_ty), ..self }
-    }
-
-    pub fn def_id(self) -> DefId {
-        self.trait_ref.def_id
-    }
-
-    pub fn self_ty(self) -> Ty<'tcx> {
-        self.trait_ref.self_ty()
-    }
-}
-
 impl<'tcx> PolyTraitPredicate<'tcx> {
     pub fn def_id(self) -> DefId {
         // Ok to skip binder since trait `DefId` does not care about regions.
@@ -634,64 +493,10 @@ pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<Ty<'tcx>, ty::Region<'t
 pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
 pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>;
 
-/// Encodes that `a` must be a subtype of `b`. The `a_is_expected` flag indicates
-/// whether the `a` type is the type that we should label as "expected" when
-/// presenting user diagnostics.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct SubtypePredicate<'tcx> {
-    pub a_is_expected: bool,
-    pub a: Ty<'tcx>,
-    pub b: Ty<'tcx>,
-}
 pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>;
 
-/// Encodes that we have to coerce *from* the `a` type to the `b` type.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct CoercePredicate<'tcx> {
-    pub a: Ty<'tcx>,
-    pub b: Ty<'tcx>,
-}
 pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>;
 
-/// This kind of predicate has no *direct* correspondent in the
-/// syntax, but it roughly corresponds to the syntactic forms:
-///
-/// 1. `T: TraitRef<..., Item = Type>`
-/// 2. `<T as TraitRef<...>>::Item == Type` (NYI)
-///
-/// In particular, form #1 is "desugared" to the combination of a
-/// normal trait predicate (`T: TraitRef<...>`) and one of these
-/// predicates. Form #2 is a broader form in that it also permits
-/// equality between arbitrary types. Processing an instance of
-/// Form #2 eventually yields one of these `ProjectionPredicate`
-/// instances to normalize the LHS.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct ProjectionPredicate<'tcx> {
-    pub projection_ty: AliasTy<'tcx>,
-    pub term: Term<'tcx>,
-}
-
-impl<'tcx> ProjectionPredicate<'tcx> {
-    pub fn self_ty(self) -> Ty<'tcx> {
-        self.projection_ty.self_ty()
-    }
-
-    pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ProjectionPredicate<'tcx> {
-        Self { projection_ty: self.projection_ty.with_self_ty(tcx, self_ty), ..self }
-    }
-
-    pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
-        self.projection_ty.trait_def_id(tcx)
-    }
-
-    pub fn def_id(self) -> DefId {
-        self.projection_ty.def_id
-    }
-}
-
 pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>;
 
 impl<'tcx> PolyProjectionPredicate<'tcx> {
@@ -728,33 +533,6 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
     }
 }
 
-/// Used by the new solver. Unlike a `ProjectionPredicate` this can only be
-/// proven by actually normalizing `alias`.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct NormalizesTo<'tcx> {
-    pub alias: AliasTy<'tcx>,
-    pub term: Term<'tcx>,
-}
-
-impl<'tcx> NormalizesTo<'tcx> {
-    pub fn self_ty(self) -> Ty<'tcx> {
-        self.alias.self_ty()
-    }
-
-    pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> NormalizesTo<'tcx> {
-        Self { alias: self.alias.with_self_ty(tcx, self_ty), ..self }
-    }
-
-    pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
-        self.alias.trait_def_id(tcx)
-    }
-
-    pub fn def_id(self) -> DefId {
-        self.alias.def_id
-    }
-}
-
 pub trait ToPolyTraitRef<'tcx> {
     fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
 }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 14a628ab064..be9525a083c 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -2953,8 +2953,9 @@ impl<'tcx> fmt::Debug for TraitPredPrintModifiersAndPath<'tcx> {
     }
 }
 
+#[extension(pub trait PrintTraitPredicateExt<'tcx>)]
 impl<'tcx> ty::TraitPredicate<'tcx> {
-    pub fn print_modifiers_and_trait_path(self) -> TraitPredPrintModifiersAndPath<'tcx> {
+    fn print_modifiers_and_trait_path(self) -> TraitPredPrintModifiersAndPath<'tcx> {
         TraitPredPrintModifiersAndPath(self)
     }
 }
@@ -3037,6 +3038,15 @@ define_print! {
         p!(write("<{} as {}>", self.self_ty(), self.print_only_trait_path()))
     }
 
+    ty::TraitPredicate<'tcx> {
+        p!(print(self.trait_ref.self_ty()), ": ");
+        p!(pretty_print_bound_constness(self.trait_ref));
+        if let ty::PredicatePolarity::Negative = self.polarity {
+            p!("!");
+        }
+        p!(print(self.trait_ref.print_trait_sugared()))
+    }
+
     ty::TypeAndMut<'tcx> {
         p!(write("{}", self.mutbl.prefix_str()), print(self.ty))
     }
@@ -3077,13 +3087,15 @@ define_print! {
             ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)),
         }
     }
-}
-
-define_print_and_forward_display! {
-    (self, cx):
 
-    &'tcx ty::List<Ty<'tcx>> {
-        p!("{{", comma_sep(self.iter()), "}}")
+    ty::ExistentialPredicate<'tcx> {
+        match *self {
+            ty::ExistentialPredicate::Trait(x) => p!(print(x)),
+            ty::ExistentialPredicate::Projection(x) => p!(print(x)),
+            ty::ExistentialPredicate::AutoTrait(def_id) => {
+                p!(print_def_path(def_id, &[]));
+            }
+        }
     }
 
     ty::ExistentialTraitRef<'tcx> {
@@ -3098,14 +3110,36 @@ define_print_and_forward_display! {
         p!(write("{} = ", name), print(self.term))
     }
 
-    ty::ExistentialPredicate<'tcx> {
-        match *self {
-            ty::ExistentialPredicate::Trait(x) => p!(print(x)),
-            ty::ExistentialPredicate::Projection(x) => p!(print(x)),
-            ty::ExistentialPredicate::AutoTrait(def_id) => {
-                p!(print_def_path(def_id, &[]));
-            }
-        }
+    ty::ProjectionPredicate<'tcx> {
+        p!(print(self.projection_ty), " == ");
+        cx.reset_type_limit();
+        p!(print(self.term))
+    }
+
+    ty::SubtypePredicate<'tcx> {
+        p!(print(self.a), " <: ");
+        cx.reset_type_limit();
+        p!(print(self.b))
+    }
+
+    ty::CoercePredicate<'tcx> {
+        p!(print(self.a), " -> ");
+        cx.reset_type_limit();
+        p!(print(self.b))
+    }
+
+    ty::NormalizesTo<'tcx> {
+        p!(print(self.alias), " normalizes-to ");
+        cx.reset_type_limit();
+        p!(print(self.term))
+    }
+}
+
+define_print_and_forward_display! {
+    (self, cx):
+
+    &'tcx ty::List<Ty<'tcx>> {
+        p!("{{", comma_sep(self.iter()), "}}")
     }
 
     ty::FnSig<'tcx> {
@@ -3164,39 +3198,6 @@ define_print_and_forward_display! {
         p!(write("{}", self.name))
     }
 
-    ty::SubtypePredicate<'tcx> {
-        p!(print(self.a), " <: ");
-        cx.reset_type_limit();
-        p!(print(self.b))
-    }
-
-    ty::CoercePredicate<'tcx> {
-        p!(print(self.a), " -> ");
-        cx.reset_type_limit();
-        p!(print(self.b))
-    }
-
-    ty::TraitPredicate<'tcx> {
-        p!(print(self.trait_ref.self_ty()), ": ");
-        p!(pretty_print_bound_constness(self.trait_ref));
-        if let ty::PredicatePolarity::Negative = self.polarity {
-            p!("!");
-        }
-        p!(print(self.trait_ref.print_trait_sugared()))
-    }
-
-    ty::ProjectionPredicate<'tcx> {
-        p!(print(self.projection_ty), " == ");
-        cx.reset_type_limit();
-        p!(print(self.term))
-    }
-
-    ty::NormalizesTo<'tcx> {
-        p!(print(self.alias), " normalizes-to ");
-        cx.reset_type_limit();
-        p!(print(self.term))
-    }
-
     ty::Term<'tcx> {
       match self.unpack() {
         ty::TermKind::Ty(ty) => p!(print(ty)),
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 7063ef07201..417edda10ca 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -5,8 +5,10 @@
 //! subtyping, type equality, etc.
 
 use crate::ty::error::{ExpectedFound, TypeError};
-use crate::ty::{self, Expr, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable};
-use crate::ty::{GenericArg, GenericArgKind, GenericArgsRef};
+use crate::ty::{
+    self, ExistentialPredicate, ExistentialPredicateStableCmpExt as _, Expr, GenericArg,
+    GenericArgKind, GenericArgsRef, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable,
+};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
@@ -702,14 +704,21 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
         }
 
         let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| {
-            use crate::ty::ExistentialPredicate::*;
             match (ep_a.skip_binder(), ep_b.skip_binder()) {
-                (Trait(a), Trait(b)) => Ok(ep_a
-                    .rebind(Trait(relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder()))),
-                (Projection(a), Projection(b)) => Ok(ep_a.rebind(Projection(
-                    relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
-                ))),
-                (AutoTrait(a), AutoTrait(b)) if a == b => Ok(ep_a.rebind(AutoTrait(a))),
+                (ExistentialPredicate::Trait(a), ExistentialPredicate::Trait(b)) => Ok(ep_a
+                    .rebind(ExistentialPredicate::Trait(
+                        relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
+                    ))),
+                (ExistentialPredicate::Projection(a), ExistentialPredicate::Projection(b)) => {
+                    Ok(ep_a.rebind(ExistentialPredicate::Projection(
+                        relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
+                    )))
+                }
+                (ExistentialPredicate::AutoTrait(a), ExistentialPredicate::AutoTrait(b))
+                    if a == b =>
+                {
+                    Ok(ep_a.rebind(ExistentialPredicate::AutoTrait(a)))
+                }
                 _ => Err(TypeError::ExistentialMismatch(expected_found(a, b))),
             }
         });
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index a7770f71926..dc071b295aa 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -55,12 +55,6 @@ impl fmt::Debug for ty::UpvarId {
     }
 }
 
-impl<'tcx> fmt::Debug for ty::ExistentialTraitRef<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        with_no_trimmed_paths!(fmt::Display::fmt(self, f))
-    }
-}
-
 impl<'tcx> fmt::Debug for ty::adjustment::Adjustment<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{:?} -> {}", self.kind, self.target)
@@ -158,25 +152,6 @@ impl fmt::Debug for ty::ParamConst {
     }
 }
 
-impl<'tcx> fmt::Debug for ty::TraitPredicate<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // FIXME(effects) printing?
-        write!(f, "TraitPredicate({:?}, polarity:{:?})", self.trait_ref, self.polarity)
-    }
-}
-
-impl<'tcx> fmt::Debug for ty::ProjectionPredicate<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_ty, self.term)
-    }
-}
-
-impl<'tcx> fmt::Debug for ty::NormalizesTo<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "NormalizesTo({:?}, {:?})", self.alias, self.term)
-    }
-}
-
 impl<'tcx> fmt::Debug for ty::Predicate<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{:?}", self.kind())
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index a97244dda9a..1fc8eefd65f 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1142,6 +1142,36 @@ pub struct AliasTy<'tcx> {
     _use_alias_ty_new_instead: (),
 }
 
+impl<'tcx> rustc_type_ir::inherent::AliasTy<TyCtxt<'tcx>> for AliasTy<'tcx> {
+    fn new(
+        interner: TyCtxt<'tcx>,
+        trait_def_id: DefId,
+        args: impl IntoIterator<Item: Into<ty::GenericArg<'tcx>>>,
+    ) -> Self {
+        AliasTy::new(interner, trait_def_id, args)
+    }
+
+    fn def_id(self) -> DefId {
+        self.def_id
+    }
+
+    fn args(self) -> ty::GenericArgsRef<'tcx> {
+        self.args
+    }
+
+    fn trait_def_id(self, interner: TyCtxt<'tcx>) -> DefId {
+        self.trait_def_id(interner)
+    }
+
+    fn self_ty(self) -> Ty<'tcx> {
+        self.self_ty()
+    }
+
+    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+        self.with_self_ty(tcx, self_ty)
+    }
+}
+
 impl<'tcx> AliasTy<'tcx> {
     pub fn new(
         tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
index 7141c6c9bb0..21433cfdb61 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
@@ -8,7 +8,8 @@ use rustc_hir::LangItem;
 use rustc_middle::bug;
 use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::{
-    self, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, UintTy,
+    self, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable,
+    TypeVisitableExt, UintTy,
 };
 use rustc_span::sym;
 use rustc_trait_selection::traits;
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 51a4e910d62..ea1752a6e98 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -45,7 +45,9 @@ use std::iter;
 use crate::infer::InferCtxtExt as _;
 use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
+use rustc_middle::ty::print::{
+    with_forced_trimmed_paths, with_no_trimmed_paths, PrintTraitPredicateExt as _,
+};
 
 use itertools::EitherOrBoth;
 use itertools::Itertools;
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 92fe50883d0..08ffe37b8b4 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -38,7 +38,8 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::print::{
-    with_forced_trimmed_paths, FmtPrinter, Print, PrintTraitRefExt as _,
+    with_forced_trimmed_paths, FmtPrinter, Print, PrintTraitPredicateExt as _,
+    PrintTraitRefExt as _,
 };
 use rustc_middle::ty::{
     self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 5cb61dff831..75e43bc8f5d 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -18,7 +18,8 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
-    self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
+    self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable,
+    TypeVisitable, TypeVisitor,
 };
 use rustc_middle::ty::{GenericArg, GenericArgs};
 use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml
index 79ff60802d2..cef3f4e8ce0 100644
--- a/compiler/rustc_type_ir/Cargo.toml
+++ b/compiler/rustc_type_ir/Cargo.toml
@@ -13,7 +13,8 @@ rustc_index = { path = "../rustc_index", default-features = false }
 rustc_macros = { path = "../rustc_macros", optional = true }
 rustc_serialize = { path = "../rustc_serialize", optional = true }
 rustc_span = { path = "../rustc_span", optional = true }
-smallvec = { version = "1.8.1" }
+rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
+smallvec = { version = "1.8.1", default-features = false }
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs
index f7c7ec242c1..efefd174cd6 100644
--- a/compiler/rustc_type_ir/src/canonical.rs
+++ b/compiler/rustc_type_ir/src/canonical.rs
@@ -1,20 +1,25 @@
-use rustc_ast_ir::try_visit;
-use rustc_ast_ir::visit::VisitorResult;
 #[cfg(feature = "nightly")]
 use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
+use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
 use std::fmt;
 use std::hash::Hash;
 
-use crate::fold::{FallibleTypeFolder, TypeFoldable};
 use crate::inherent::*;
-use crate::visit::{TypeVisitable, TypeVisitor};
 use crate::{Interner, UniverseIndex};
 
 /// A "canonicalized" type `V` is one where all free inference
 /// variables have been rewritten to "canonical vars". These are
 /// numbered starting from 0 in order of first appearance.
 #[derive(derivative::Derivative)]
-#[derivative(Clone(bound = "V: Clone"), Hash(bound = "V: Hash"))]
+#[derivative(
+    Clone(bound = "V: Clone"),
+    Hash(bound = "V: Hash"),
+    PartialEq(bound = "V: PartialEq"),
+    Eq(bound = "V: Eq"),
+    Debug(bound = "V: fmt::Debug"),
+    Copy(bound = "V: Copy")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
 pub struct Canonical<I: Interner, V> {
     pub value: V,
@@ -64,18 +69,6 @@ impl<I: Interner, V> Canonical<I, V> {
     }
 }
 
-impl<I: Interner, V: Eq> Eq for Canonical<I, V> {}
-
-impl<I: Interner, V: PartialEq> PartialEq for Canonical<I, V> {
-    fn eq(&self, other: &Self) -> bool {
-        let Self { value, max_universe, variables, defining_opaque_types } = self;
-        *value == other.value
-            && *max_universe == other.max_universe
-            && *variables == other.variables
-            && *defining_opaque_types == other.defining_opaque_types
-    }
-}
-
 impl<I: Interner, V: fmt::Display> fmt::Display for Canonical<I, V> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let Self { value, max_universe, variables, defining_opaque_types } = self;
@@ -86,84 +79,25 @@ impl<I: Interner, V: fmt::Display> fmt::Display for Canonical<I, V> {
     }
 }
 
-impl<I: Interner, V: fmt::Debug> fmt::Debug for Canonical<I, V> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { value, max_universe, variables, defining_opaque_types } = self;
-        f.debug_struct("Canonical")
-            .field("value", &value)
-            .field("max_universe", &max_universe)
-            .field("variables", &variables)
-            .field("defining_opaque_types", &defining_opaque_types)
-            .finish()
-    }
-}
-
-impl<I: Interner, V: Copy> Copy for Canonical<I, V> where I::CanonicalVars: Copy {}
-
-impl<I: Interner, V: TypeFoldable<I>> TypeFoldable<I> for Canonical<I, V>
-where
-    I::CanonicalVars: TypeFoldable<I>,
-{
-    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
-        Ok(Canonical {
-            value: self.value.try_fold_with(folder)?,
-            max_universe: self.max_universe.try_fold_with(folder)?,
-            variables: self.variables.try_fold_with(folder)?,
-            defining_opaque_types: self.defining_opaque_types,
-        })
-    }
-}
-
-impl<I: Interner, V: TypeVisitable<I>> TypeVisitable<I> for Canonical<I, V>
-where
-    I::CanonicalVars: TypeVisitable<I>,
-{
-    fn visit_with<F: TypeVisitor<I>>(&self, folder: &mut F) -> F::Result {
-        let Self { value, max_universe, variables, defining_opaque_types } = self;
-        try_visit!(value.visit_with(folder));
-        try_visit!(max_universe.visit_with(folder));
-        try_visit!(defining_opaque_types.visit_with(folder));
-        variables.visit_with(folder)
-    }
-}
-
 /// Information about a canonical variable that is included with the
 /// canonical value. This is sufficient information for code to create
 /// a copy of the canonical value in some other inference context,
 /// with fresh inference variables replacing the canonical values.
 #[derive(derivative::Derivative)]
-#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Debug(bound = ""))]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    Debug(bound = ""),
+    Eq(bound = ""),
+    PartialEq(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
 pub struct CanonicalVarInfo<I: Interner> {
     pub kind: CanonicalVarKind<I>,
 }
 
-impl<I: Interner> PartialEq for CanonicalVarInfo<I> {
-    fn eq(&self, other: &Self) -> bool {
-        self.kind == other.kind
-    }
-}
-
-impl<I: Interner> Eq for CanonicalVarInfo<I> {}
-
-impl<I: Interner> TypeVisitable<I> for CanonicalVarInfo<I>
-where
-    I::Ty: TypeVisitable<I>,
-{
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
-        self.kind.visit_with(visitor)
-    }
-}
-
-impl<I: Interner> TypeFoldable<I> for CanonicalVarInfo<I>
-where
-    I::Ty: TypeFoldable<I>,
-{
-    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
-        Ok(CanonicalVarInfo { kind: self.kind.try_fold_with(folder)? })
-    }
-}
-
 impl<I: Interner> CanonicalVarInfo<I> {
     pub fn universe(self) -> UniverseIndex {
         self.kind.universe()
@@ -216,6 +150,7 @@ impl<I: Interner> CanonicalVarInfo<I> {
 /// that analyzes type-like values.
 #[derive(derivative::Derivative)]
 #[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Debug(bound = ""))]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
 pub enum CanonicalVarKind<I: Interner> {
     /// Some kind of type inference variable.
@@ -258,51 +193,6 @@ impl<I: Interner> PartialEq for CanonicalVarKind<I> {
     }
 }
 
-impl<I: Interner> Eq for CanonicalVarKind<I> {}
-
-impl<I: Interner> TypeVisitable<I> for CanonicalVarKind<I>
-where
-    I::Ty: TypeVisitable<I>,
-{
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
-        match self {
-            CanonicalVarKind::Ty(_)
-            | CanonicalVarKind::PlaceholderTy(_)
-            | CanonicalVarKind::Region(_)
-            | CanonicalVarKind::PlaceholderRegion(_)
-            | CanonicalVarKind::Effect => V::Result::output(),
-            CanonicalVarKind::Const(_, ty) | CanonicalVarKind::PlaceholderConst(_, ty) => {
-                ty.visit_with(visitor)
-            }
-        }
-    }
-}
-
-impl<I: Interner> TypeFoldable<I> for CanonicalVarKind<I>
-where
-    I::Ty: TypeFoldable<I>,
-{
-    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
-        Ok(match self {
-            CanonicalVarKind::Ty(kind) => CanonicalVarKind::Ty(kind),
-            CanonicalVarKind::Region(kind) => CanonicalVarKind::Region(kind),
-            CanonicalVarKind::Const(kind, ty) => {
-                CanonicalVarKind::Const(kind, ty.try_fold_with(folder)?)
-            }
-            CanonicalVarKind::PlaceholderTy(placeholder) => {
-                CanonicalVarKind::PlaceholderTy(placeholder)
-            }
-            CanonicalVarKind::PlaceholderRegion(placeholder) => {
-                CanonicalVarKind::PlaceholderRegion(placeholder)
-            }
-            CanonicalVarKind::PlaceholderConst(placeholder, ty) => {
-                CanonicalVarKind::PlaceholderConst(placeholder, ty.try_fold_with(folder)?)
-            }
-            CanonicalVarKind::Effect => CanonicalVarKind::Effect,
-        })
-    }
-}
-
 impl<I: Interner> CanonicalVarKind<I> {
     pub fn universe(self) -> UniverseIndex {
         match self {
@@ -355,6 +245,7 @@ impl<I: Interner> CanonicalVarKind<I> {
 /// usize or f32). In order to faithfully reproduce a type, we need to
 /// know what set of types a given type variable can be unified with.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
 pub enum CanonicalTyVarKind {
     /// General type variable `?T` that can be unified with arbitrary types.
diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs
index c1506f9252b..c748cdf6ed2 100644
--- a/compiler/rustc_type_ir/src/const_kind.rs
+++ b/compiler/rustc_type_ir/src/const_kind.rs
@@ -10,7 +10,7 @@ use self::ConstKind::*;
 
 /// Represents a constant in Rust.
 #[derive(derivative::Derivative)]
-#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""))]
+#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Eq(bound = ""))]
 #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
 pub enum ConstKind<I: Interner> {
     /// A const generic parameter.
@@ -58,8 +58,6 @@ impl<I: Interner> PartialEq for ConstKind<I> {
     }
 }
 
-impl<I: Interner> Eq for ConstKind<I> {}
-
 impl<I: Interner> fmt::Debug for ConstKind<I> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         WithInfcx::with_no_infcx(self).fmt(f)
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 0fd34e0a65f..a50967a3b18 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -1,5 +1,6 @@
 use std::fmt::Debug;
 use std::hash::Hash;
+use std::ops::Deref;
 
 use crate::fold::TypeSuperFoldable;
 use crate::visit::{Flags, TypeSuperVisitable};
@@ -50,7 +51,12 @@ pub trait GenericsOf<I: Interner<GenericsOf = Self>> {
 }
 
 pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
-    Copy + DebugWithInfcx<I> + Hash + Eq + IntoIterator<Item = I::GenericArg>
+    Copy
+    + DebugWithInfcx<I>
+    + Hash
+    + Eq
+    + IntoIterator<Item = I::GenericArg>
+    + Deref<Target: Deref<Target = [I::GenericArg]>>
 {
     fn type_at(self, i: usize) -> I::Ty;
 
@@ -83,3 +89,22 @@ pub trait BoundVars<I: Interner> {
 
     fn has_no_bound_vars(&self) -> bool;
 }
+
+// FIXME: Uplift `AliasTy`
+pub trait AliasTy<I: Interner>: Copy + DebugWithInfcx<I> + Hash + Eq + Sized {
+    fn new(
+        interner: I,
+        trait_def_id: I::DefId,
+        args: impl IntoIterator<Item: Into<I::GenericArg>>,
+    ) -> Self;
+
+    fn def_id(self) -> I::DefId;
+
+    fn args(self) -> I::GenericArgs;
+
+    fn trait_def_id(self, interner: I) -> I::DefId;
+
+    fn self_ty(self) -> I::Ty;
+
+    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self;
+}
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index c8bd7fea11b..17d9f4242fd 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -5,9 +5,23 @@ use std::hash::Hash;
 use crate::inherent::*;
 use crate::ir_print::IrPrint;
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
-use crate::{CanonicalVarInfo, DebugWithInfcx, TraitRef};
-
-pub trait Interner: Sized + Copy + IrPrint<TraitRef<Self>> {
+use crate::{
+    CanonicalVarInfo, CoercePredicate, DebugWithInfcx, ExistentialProjection, ExistentialTraitRef,
+    NormalizesTo, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef,
+};
+
+pub trait Interner:
+    Sized
+    + Copy
+    + IrPrint<TraitRef<Self>>
+    + IrPrint<TraitPredicate<Self>>
+    + IrPrint<ExistentialTraitRef<Self>>
+    + IrPrint<ExistentialProjection<Self>>
+    + IrPrint<ProjectionPredicate<Self>>
+    + IrPrint<NormalizesTo<Self>>
+    + IrPrint<SubtypePredicate<Self>>
+    + IrPrint<CoercePredicate<Self>>
+{
     type DefId: Copy + Debug + Hash + Eq;
     type DefiningOpaqueTypes: Copy + Debug + Hash + Default + Eq + TypeVisitable<Self>;
     type AdtDef: Copy + Debug + Hash + Eq;
@@ -25,7 +39,7 @@ pub trait Interner: Sized + Copy + IrPrint<TraitRef<Self>> {
     // Kinds of tys
     type Ty: Ty<Self>;
     type Tys: Copy + Debug + Hash + Eq + IntoIterator<Item = Self::Ty>;
-    type AliasTy: Copy + DebugWithInfcx<Self> + Hash + Eq;
+    type AliasTy: AliasTy<Self>;
     type ParamTy: Copy + Debug + Hash + Eq;
     type BoundTy: Copy + Debug + Hash + Eq;
     type PlaceholderTy: PlaceholderLike;
@@ -71,11 +85,15 @@ pub trait Interner: Sized + Copy + IrPrint<TraitRef<Self>> {
     type GenericsOf: GenericsOf<Self>;
     fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf;
 
+    fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs;
+
     fn check_and_mk_args(
         self,
         def_id: Self::DefId,
         args: impl IntoIterator<Item: Into<Self::GenericArg>>,
     ) -> Self::GenericArgs;
+
+    fn parent(self, def_id: Self::DefId) -> Self::DefId;
 }
 
 /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs
index 84e889b486a..5885139754a 100644
--- a/compiler/rustc_type_ir/src/ir_print.rs
+++ b/compiler/rustc_type_ir/src/ir_print.rs
@@ -1,6 +1,9 @@
 use std::fmt;
 
-use crate::{Interner, TraitRef};
+use crate::{
+    CoercePredicate, ExistentialProjection, ExistentialTraitRef, Interner, NormalizesTo,
+    ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef,
+};
 
 pub trait IrPrint<T> {
     fn print(t: &T, fmt: &mut fmt::Formatter<'_>) -> fmt::Result;
@@ -15,7 +18,13 @@ macro_rules! define_display_via_print {
                     <I as IrPrint<$ty<I>>>::print(self, fmt)
                 }
             }
+        )*
+    }
+}
 
+macro_rules! define_debug_via_print {
+    ($($ty:ident),+ $(,)?) => {
+        $(
             impl<I: Interner> fmt::Debug for $ty<I> {
                 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
                     <I as IrPrint<$ty<I>>>::print_debug(self, fmt)
@@ -25,4 +34,15 @@ macro_rules! define_display_via_print {
     }
 }
 
-define_display_via_print!(TraitRef,);
+define_display_via_print!(
+    TraitRef,
+    TraitPredicate,
+    ExistentialTraitRef,
+    ExistentialProjection,
+    ProjectionPredicate,
+    NormalizesTo,
+    SubtypePredicate,
+    CoercePredicate,
+);
+
+define_debug_via_print!(TraitRef, ExistentialTraitRef, ExistentialProjection);
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 62efa32c9f2..04cacd987d0 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -37,9 +37,9 @@ mod debug;
 mod flags;
 mod infcx;
 mod interner;
+mod predicate;
 mod predicate_kind;
 mod region_kind;
-mod trait_ref;
 
 pub use canonical::*;
 #[cfg(feature = "nightly")]
@@ -49,9 +49,9 @@ pub use debug::{DebugWithInfcx, WithInfcx};
 pub use flags::*;
 pub use infcx::InferCtxtLike;
 pub use interner::*;
+pub use predicate::*;
 pub use predicate_kind::*;
 pub use region_kind::*;
-pub use trait_ref::*;
 pub use ty_info::*;
 pub use ty_kind::*;
 pub use AliasKind::*;
diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs
index 7dcc8851a43..f2f7b165de5 100644
--- a/compiler/rustc_type_ir/src/macros.rs
+++ b/compiler/rustc_type_ir/src/macros.rs
@@ -53,4 +53,5 @@ TrivialTypeTraversalImpls! {
     crate::UniverseIndex,
     rustc_ast_ir::Mutability,
     rustc_ast_ir::Movability,
+    crate::PredicatePolarity,
 }
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
new file mode 100644
index 00000000000..f84f8f47c67
--- /dev/null
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -0,0 +1,434 @@
+use std::fmt;
+
+use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
+use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
+
+use crate::inherent::*;
+use crate::visit::TypeVisitableExt as _;
+use crate::{DebugWithInfcx, Interner};
+
+/// A complete reference to a trait. These take numerous guises in syntax,
+/// but perhaps the most recognizable form is in a where-clause:
+/// ```ignore (illustrative)
+/// T: Foo<U>
+/// ```
+/// This would be represented by a trait-reference where the `DefId` is the
+/// `DefId` for the trait `Foo` and the args define `T` as parameter 0,
+/// and `U` as parameter 1.
+///
+/// Trait references also appear in object types like `Foo<U>`, but in
+/// that case the `Self` parameter is absent from the generic parameters.
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct TraitRef<I: Interner> {
+    pub def_id: I::DefId,
+    pub args: I::GenericArgs,
+    /// This field exists to prevent the creation of `TraitRef` without
+    /// calling [`TraitRef::new`].
+    _use_trait_ref_new_instead: (),
+}
+
+impl<I: Interner> TraitRef<I> {
+    pub fn new(
+        interner: I,
+        trait_def_id: I::DefId,
+        args: impl IntoIterator<Item: Into<I::GenericArg>>,
+    ) -> Self {
+        let args = interner.check_and_mk_args(trait_def_id, args);
+        Self { def_id: trait_def_id, args, _use_trait_ref_new_instead: () }
+    }
+
+    pub fn from_method(interner: I, trait_id: I::DefId, args: I::GenericArgs) -> TraitRef<I> {
+        let generics = interner.generics_of(trait_id);
+        TraitRef::new(interner, trait_id, args.into_iter().take(generics.count()))
+    }
+
+    /// Returns a `TraitRef` of the form `P0: Foo<P1..Pn>` where `Pi`
+    /// are the parameters defined on trait.
+    pub fn identity(interner: I, def_id: I::DefId) -> TraitRef<I> {
+        TraitRef::new(interner, def_id, I::GenericArgs::identity_for_item(interner, def_id))
+    }
+
+    pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> Self {
+        TraitRef::new(
+            interner,
+            self.def_id,
+            [self_ty.into()].into_iter().chain(self.args.into_iter().skip(1)),
+        )
+    }
+
+    #[inline]
+    pub fn self_ty(&self) -> I::Ty {
+        self.args.type_at(0)
+    }
+}
+
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct TraitPredicate<I: Interner> {
+    pub trait_ref: TraitRef<I>,
+
+    /// If polarity is Positive: we are proving that the trait is implemented.
+    ///
+    /// If polarity is Negative: we are proving that a negative impl of this trait
+    /// exists. (Note that coherence also checks whether negative impls of supertraits
+    /// exist via a series of predicates.)
+    ///
+    /// If polarity is Reserved: that's a bug.
+    pub polarity: PredicatePolarity,
+}
+
+impl<I: Interner> TraitPredicate<I> {
+    pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> Self {
+        Self { trait_ref: self.trait_ref.with_self_ty(interner, self_ty), polarity: self.polarity }
+    }
+
+    pub fn def_id(self) -> I::DefId {
+        self.trait_ref.def_id
+    }
+
+    pub fn self_ty(self) -> I::Ty {
+        self.trait_ref.self_ty()
+    }
+}
+
+impl<I: Interner> fmt::Debug for TraitPredicate<I> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // FIXME(effects) printing?
+        write!(f, "TraitPredicate({:?}, polarity:{:?})", self.trait_ref, self.polarity)
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub enum ImplPolarity {
+    /// `impl Trait for Type`
+    Positive,
+    /// `impl !Trait for Type`
+    Negative,
+    /// `#[rustc_reservation_impl] impl Trait for Type`
+    ///
+    /// This is a "stability hack", not a real Rust feature.
+    /// See #64631 for details.
+    Reservation,
+}
+
+impl fmt::Display for ImplPolarity {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Positive => f.write_str("positive"),
+            Self::Negative => f.write_str("negative"),
+            Self::Reservation => f.write_str("reservation"),
+        }
+    }
+}
+
+/// Polarity for a trait predicate. May either be negative or positive.
+/// Distinguished from [`ImplPolarity`] since we never compute goals with
+/// "reservation" level.
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub enum PredicatePolarity {
+    /// `Type: Trait`
+    Positive,
+    /// `Type: !Trait`
+    Negative,
+}
+
+impl PredicatePolarity {
+    /// Flips polarity by turning `Positive` into `Negative` and `Negative` into `Positive`.
+    pub fn flip(&self) -> PredicatePolarity {
+        match self {
+            PredicatePolarity::Positive => PredicatePolarity::Negative,
+            PredicatePolarity::Negative => PredicatePolarity::Positive,
+        }
+    }
+}
+
+impl fmt::Display for PredicatePolarity {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Positive => f.write_str("positive"),
+            Self::Negative => f.write_str("negative"),
+        }
+    }
+}
+
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub enum ExistentialPredicate<I: Interner> {
+    /// E.g., `Iterator`.
+    Trait(ExistentialTraitRef<I>),
+    /// E.g., `Iterator::Item = T`.
+    Projection(ExistentialProjection<I>),
+    /// E.g., `Send`.
+    AutoTrait(I::DefId),
+}
+
+// FIXME: Implement this the right way after
+impl<I: Interner> DebugWithInfcx<I> for ExistentialPredicate<I> {
+    fn fmt<Infcx: rustc_type_ir::InferCtxtLike<Interner = I>>(
+        this: rustc_type_ir::WithInfcx<'_, Infcx, &Self>,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        fmt::Debug::fmt(&this.data, f)
+    }
+}
+
+/// An existential reference to a trait, where `Self` is erased.
+/// For example, the trait object `Trait<'a, 'b, X, Y>` is:
+/// ```ignore (illustrative)
+/// exists T. T: Trait<'a, 'b, X, Y>
+/// ```
+/// The generic parameters don't include the erased `Self`, only trait
+/// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above).
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct ExistentialTraitRef<I: Interner> {
+    pub def_id: I::DefId,
+    pub args: I::GenericArgs,
+}
+
+impl<I: Interner> ExistentialTraitRef<I> {
+    pub fn erase_self_ty(interner: I, trait_ref: TraitRef<I>) -> ExistentialTraitRef<I> {
+        // Assert there is a Self.
+        trait_ref.args.type_at(0);
+
+        ExistentialTraitRef {
+            def_id: trait_ref.def_id,
+            args: interner.mk_args(&trait_ref.args[1..]),
+        }
+    }
+
+    /// Object types don't have a self type specified. Therefore, when
+    /// we convert the principal trait-ref into a normal trait-ref,
+    /// you must give *some* self type. A common choice is `mk_err()`
+    /// or some placeholder type.
+    pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> TraitRef<I> {
+        // otherwise the escaping vars would be captured by the binder
+        // debug_assert!(!self_ty.has_escaping_bound_vars());
+
+        TraitRef::new(
+            interner,
+            self.def_id,
+            [self_ty.into()].into_iter().chain(self.args.into_iter()),
+        )
+    }
+}
+
+/// A `ProjectionPredicate` for an `ExistentialTraitRef`.
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct ExistentialProjection<I: Interner> {
+    pub def_id: I::DefId,
+    pub args: I::GenericArgs,
+    pub term: I::Term,
+}
+
+impl<I: Interner> ExistentialProjection<I> {
+    /// Extracts the underlying existential trait reference from this projection.
+    /// For example, if this is a projection of `exists T. <T as Iterator>::Item == X`,
+    /// then this function would return an `exists T. T: Iterator` existential trait
+    /// reference.
+    pub fn trait_ref(&self, tcx: I) -> ExistentialTraitRef<I> {
+        let def_id = tcx.parent(self.def_id);
+        let args_count = tcx.generics_of(def_id).count() - 1;
+        let args = tcx.mk_args(&self.args[..args_count]);
+        ExistentialTraitRef { def_id, args }
+    }
+
+    pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> ProjectionPredicate<I> {
+        // otherwise the escaping regions would be captured by the binders
+        debug_assert!(!self_ty.has_escaping_bound_vars());
+
+        ProjectionPredicate {
+            projection_ty: I::AliasTy::new(
+                tcx,
+                self.def_id,
+                [self_ty.into()].into_iter().chain(self.args),
+            ),
+            term: self.term,
+        }
+    }
+
+    pub fn erase_self_ty(tcx: I, projection_predicate: ProjectionPredicate<I>) -> Self {
+        // Assert there is a Self.
+        projection_predicate.projection_ty.args().type_at(0);
+
+        Self {
+            def_id: projection_predicate.projection_ty.def_id(),
+            args: tcx.mk_args(&projection_predicate.projection_ty.args()[1..]),
+            term: projection_predicate.term,
+        }
+    }
+}
+
+/// This kind of predicate has no *direct* correspondent in the
+/// syntax, but it roughly corresponds to the syntactic forms:
+///
+/// 1. `T: TraitRef<..., Item = Type>`
+/// 2. `<T as TraitRef<...>>::Item == Type` (NYI)
+///
+/// In particular, form #1 is "desugared" to the combination of a
+/// normal trait predicate (`T: TraitRef<...>`) and one of these
+/// predicates. Form #2 is a broader form in that it also permits
+/// equality between arbitrary types. Processing an instance of
+/// Form #2 eventually yields one of these `ProjectionPredicate`
+/// instances to normalize the LHS.
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct ProjectionPredicate<I: Interner> {
+    pub projection_ty: I::AliasTy,
+    pub term: I::Term,
+}
+
+impl<I: Interner> ProjectionPredicate<I> {
+    pub fn self_ty(self) -> I::Ty {
+        self.projection_ty.self_ty()
+    }
+
+    pub fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> ProjectionPredicate<I> {
+        Self { projection_ty: self.projection_ty.with_self_ty(tcx, self_ty), ..self }
+    }
+
+    pub fn trait_def_id(self, tcx: I) -> I::DefId {
+        self.projection_ty.trait_def_id(tcx)
+    }
+
+    pub fn def_id(self) -> I::DefId {
+        self.projection_ty.def_id()
+    }
+}
+
+impl<I: Interner> fmt::Debug for ProjectionPredicate<I> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_ty, self.term)
+    }
+}
+
+/// Used by the new solver. Unlike a `ProjectionPredicate` this can only be
+/// proven by actually normalizing `alias`.
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct NormalizesTo<I: Interner> {
+    pub alias: I::AliasTy,
+    pub term: I::Term,
+}
+
+impl<I: Interner> NormalizesTo<I> {
+    pub fn self_ty(self) -> I::Ty {
+        self.alias.self_ty()
+    }
+
+    pub fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> NormalizesTo<I> {
+        Self { alias: self.alias.with_self_ty(tcx, self_ty), ..self }
+    }
+
+    pub fn trait_def_id(self, tcx: I) -> I::DefId {
+        self.alias.trait_def_id(tcx)
+    }
+
+    pub fn def_id(self) -> I::DefId {
+        self.alias.def_id()
+    }
+}
+
+impl<I: Interner> fmt::Debug for NormalizesTo<I> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "NormalizesTo({:?}, {:?})", self.alias, self.term)
+    }
+}
+
+/// Encodes that `a` must be a subtype of `b`. The `a_is_expected` flag indicates
+/// whether the `a` type is the type that we should label as "expected" when
+/// presenting user diagnostics.
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct SubtypePredicate<I: Interner> {
+    pub a_is_expected: bool,
+    pub a: I::Ty,
+    pub b: I::Ty,
+}
+
+/// Encodes that we have to coerce *from* the `a` type to the `b` type.
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct CoercePredicate<I: Interner> {
+    pub a: I::Ty,
+    pub b: I::Ty,
+}
diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs
index 5260d9061cf..c477ab14153 100644
--- a/compiler/rustc_type_ir/src/predicate_kind.rs
+++ b/compiler/rustc_type_ir/src/predicate_kind.rs
@@ -1,17 +1,15 @@
-use rustc_ast_ir::try_visit;
-use rustc_ast_ir::visit::VisitorResult;
 #[cfg(feature = "nightly")]
 use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable};
+use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
 use std::fmt;
 
-use crate::fold::{FallibleTypeFolder, TypeFoldable};
-use crate::visit::{TypeVisitable, TypeVisitor};
 use crate::Interner;
 
 /// A clause is something that can appear in where bounds or be inferred
 /// by implied bounds.
 #[derive(derivative::Derivative)]
-#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""))]
+#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Eq(bound = ""))]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
 pub enum ClauseKind<I: Interner> {
     /// Corresponds to `where Foo: Bar<A, B, C>`. `Foo` here would be
@@ -55,61 +53,6 @@ impl<I: Interner> PartialEq for ClauseKind<I> {
     }
 }
 
-impl<I: Interner> Eq for ClauseKind<I> {}
-
-impl<I: Interner> TypeFoldable<I> for ClauseKind<I>
-where
-    I::Ty: TypeFoldable<I>,
-    I::Const: TypeFoldable<I>,
-    I::GenericArg: TypeFoldable<I>,
-    I::TraitPredicate: TypeFoldable<I>,
-    I::ProjectionPredicate: TypeFoldable<I>,
-    I::TypeOutlivesPredicate: TypeFoldable<I>,
-    I::RegionOutlivesPredicate: TypeFoldable<I>,
-{
-    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
-        Ok(match self {
-            ClauseKind::Trait(p) => ClauseKind::Trait(p.try_fold_with(folder)?),
-            ClauseKind::RegionOutlives(p) => ClauseKind::RegionOutlives(p.try_fold_with(folder)?),
-            ClauseKind::TypeOutlives(p) => ClauseKind::TypeOutlives(p.try_fold_with(folder)?),
-            ClauseKind::Projection(p) => ClauseKind::Projection(p.try_fold_with(folder)?),
-            ClauseKind::ConstArgHasType(c, t) => {
-                ClauseKind::ConstArgHasType(c.try_fold_with(folder)?, t.try_fold_with(folder)?)
-            }
-            ClauseKind::WellFormed(p) => ClauseKind::WellFormed(p.try_fold_with(folder)?),
-            ClauseKind::ConstEvaluatable(p) => {
-                ClauseKind::ConstEvaluatable(p.try_fold_with(folder)?)
-            }
-        })
-    }
-}
-
-impl<I: Interner> TypeVisitable<I> for ClauseKind<I>
-where
-    I::Ty: TypeVisitable<I>,
-    I::Const: TypeVisitable<I>,
-    I::GenericArg: TypeVisitable<I>,
-    I::TraitPredicate: TypeVisitable<I>,
-    I::ProjectionPredicate: TypeVisitable<I>,
-    I::TypeOutlivesPredicate: TypeVisitable<I>,
-    I::RegionOutlivesPredicate: TypeVisitable<I>,
-{
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
-        match self {
-            ClauseKind::Trait(p) => p.visit_with(visitor),
-            ClauseKind::RegionOutlives(p) => p.visit_with(visitor),
-            ClauseKind::TypeOutlives(p) => p.visit_with(visitor),
-            ClauseKind::Projection(p) => p.visit_with(visitor),
-            ClauseKind::ConstArgHasType(c, t) => {
-                try_visit!(c.visit_with(visitor));
-                t.visit_with(visitor)
-            }
-            ClauseKind::WellFormed(p) => p.visit_with(visitor),
-            ClauseKind::ConstEvaluatable(p) => p.visit_with(visitor),
-        }
-    }
-}
-
 #[derive(derivative::Derivative)]
 #[derivative(
     Clone(bound = ""),
@@ -118,6 +61,7 @@ where
     PartialEq(bound = ""),
     Eq(bound = "")
 )]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
 pub enum PredicateKind<I: Interner> {
     /// Prove a clause
@@ -167,69 +111,6 @@ pub enum PredicateKind<I: Interner> {
     AliasRelate(I::Term, I::Term, AliasRelationDirection),
 }
 
-impl<I: Interner> TypeFoldable<I> for PredicateKind<I>
-where
-    I::DefId: TypeFoldable<I>,
-    I::Const: TypeFoldable<I>,
-    I::GenericArgs: TypeFoldable<I>,
-    I::Term: TypeFoldable<I>,
-    I::CoercePredicate: TypeFoldable<I>,
-    I::SubtypePredicate: TypeFoldable<I>,
-    I::NormalizesTo: TypeFoldable<I>,
-    ClauseKind<I>: TypeFoldable<I>,
-{
-    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
-        Ok(match self {
-            PredicateKind::Clause(c) => PredicateKind::Clause(c.try_fold_with(folder)?),
-            PredicateKind::ObjectSafe(d) => PredicateKind::ObjectSafe(d.try_fold_with(folder)?),
-            PredicateKind::Subtype(s) => PredicateKind::Subtype(s.try_fold_with(folder)?),
-            PredicateKind::Coerce(s) => PredicateKind::Coerce(s.try_fold_with(folder)?),
-            PredicateKind::ConstEquate(a, b) => {
-                PredicateKind::ConstEquate(a.try_fold_with(folder)?, b.try_fold_with(folder)?)
-            }
-            PredicateKind::Ambiguous => PredicateKind::Ambiguous,
-            PredicateKind::NormalizesTo(p) => PredicateKind::NormalizesTo(p.try_fold_with(folder)?),
-            PredicateKind::AliasRelate(a, b, d) => PredicateKind::AliasRelate(
-                a.try_fold_with(folder)?,
-                b.try_fold_with(folder)?,
-                d.try_fold_with(folder)?,
-            ),
-        })
-    }
-}
-
-impl<I: Interner> TypeVisitable<I> for PredicateKind<I>
-where
-    I::DefId: TypeVisitable<I>,
-    I::Const: TypeVisitable<I>,
-    I::GenericArgs: TypeVisitable<I>,
-    I::Term: TypeVisitable<I>,
-    I::CoercePredicate: TypeVisitable<I>,
-    I::SubtypePredicate: TypeVisitable<I>,
-    I::NormalizesTo: TypeVisitable<I>,
-    ClauseKind<I>: TypeVisitable<I>,
-{
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
-        match self {
-            PredicateKind::Clause(p) => p.visit_with(visitor),
-            PredicateKind::ObjectSafe(d) => d.visit_with(visitor),
-            PredicateKind::Subtype(s) => s.visit_with(visitor),
-            PredicateKind::Coerce(s) => s.visit_with(visitor),
-            PredicateKind::ConstEquate(a, b) => {
-                try_visit!(a.visit_with(visitor));
-                b.visit_with(visitor)
-            }
-            PredicateKind::Ambiguous => V::Result::output(),
-            PredicateKind::NormalizesTo(p) => p.visit_with(visitor),
-            PredicateKind::AliasRelate(a, b, d) => {
-                try_visit!(a.visit_with(visitor));
-                try_visit!(b.visit_with(visitor));
-                d.visit_with(visitor)
-            }
-        }
-    }
-}
-
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, Encodable, Decodable))]
 pub enum AliasRelationDirection {
diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs
index d1b86b495e9..eaae4ee0130 100644
--- a/compiler/rustc_type_ir/src/region_kind.rs
+++ b/compiler/rustc_type_ir/src/region_kind.rs
@@ -115,7 +115,7 @@ use self::RegionKind::*;
 /// [2]: https://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/
 /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
 #[derive(derivative::Derivative)]
-#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""))]
+#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Eq(bound = ""))]
 #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))]
 pub enum RegionKind<I: Interner> {
     /// A region parameter; for example `'a` in `impl<'a> Trait for &'a ()`.
@@ -208,9 +208,6 @@ impl<I: Interner> PartialEq for RegionKind<I> {
     }
 }
 
-// This is manually implemented because a derive would require `I: Eq`
-impl<I: Interner> Eq for RegionKind<I> {}
-
 impl<I: Interner> DebugWithInfcx<I> for RegionKind<I> {
     fn fmt<Infcx: InferCtxtLike<Interner = I>>(
         this: WithInfcx<'_, Infcx, &Self>,
diff --git a/compiler/rustc_type_ir/src/trait_ref.rs b/compiler/rustc_type_ir/src/trait_ref.rs
deleted file mode 100644
index 4bd513ab7e1..00000000000
--- a/compiler/rustc_type_ir/src/trait_ref.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
-
-use crate::fold::{FallibleTypeFolder, TypeFoldable};
-use crate::inherent::*;
-use crate::lift::Lift;
-use crate::visit::{TypeVisitable, TypeVisitor};
-use crate::Interner;
-
-/// A complete reference to a trait. These take numerous guises in syntax,
-/// but perhaps the most recognizable form is in a where-clause:
-/// ```ignore (illustrative)
-/// T: Foo<U>
-/// ```
-/// This would be represented by a trait-reference where the `DefId` is the
-/// `DefId` for the trait `Foo` and the args define `T` as parameter 0,
-/// and `U` as parameter 1.
-///
-/// Trait references also appear in object types like `Foo<U>`, but in
-/// that case the `Self` parameter is absent from the generic parameters.
-#[derive(derivative::Derivative)]
-#[derivative(
-    Clone(bound = ""),
-    Copy(bound = ""),
-    Hash(bound = ""),
-    PartialEq(bound = ""),
-    Eq(bound = "")
-)]
-#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
-pub struct TraitRef<I: Interner> {
-    pub def_id: I::DefId,
-    pub args: I::GenericArgs,
-    /// This field exists to prevent the creation of `TraitRef` without
-    /// calling [`TraitRef::new`].
-    _use_trait_ref_new_instead: (),
-}
-
-impl<I: Interner> TraitRef<I> {
-    pub fn new(
-        interner: I,
-        trait_def_id: I::DefId,
-        args: impl IntoIterator<Item: Into<I::GenericArg>>,
-    ) -> Self {
-        let args = interner.check_and_mk_args(trait_def_id, args);
-        Self { def_id: trait_def_id, args, _use_trait_ref_new_instead: () }
-    }
-
-    pub fn from_method(interner: I, trait_id: I::DefId, args: I::GenericArgs) -> TraitRef<I> {
-        let generics = interner.generics_of(trait_id);
-        TraitRef::new(interner, trait_id, args.into_iter().take(generics.count()))
-    }
-
-    /// Returns a `TraitRef` of the form `P0: Foo<P1..Pn>` where `Pi`
-    /// are the parameters defined on trait.
-    pub fn identity(interner: I, def_id: I::DefId) -> TraitRef<I> {
-        TraitRef::new(interner, def_id, I::GenericArgs::identity_for_item(interner, def_id))
-    }
-
-    pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> Self {
-        TraitRef::new(
-            interner,
-            self.def_id,
-            [self_ty.into()].into_iter().chain(self.args.into_iter().skip(1)),
-        )
-    }
-
-    #[inline]
-    pub fn self_ty(&self) -> I::Ty {
-        self.args.type_at(0)
-    }
-}
-
-// FIXME(compiler-errors): Make this into a `Lift_Generic` impl.
-impl<I: Interner, U: Interner> Lift<U> for TraitRef<I>
-where
-    I::DefId: Lift<U, Lifted = U::DefId>,
-    I::GenericArgs: Lift<U, Lifted = U::GenericArgs>,
-{
-    type Lifted = TraitRef<U>;
-
-    fn lift_to_tcx(self, tcx: U) -> Option<Self::Lifted> {
-        Some(TraitRef {
-            def_id: self.def_id.lift_to_tcx(tcx)?,
-            args: self.args.lift_to_tcx(tcx)?,
-            _use_trait_ref_new_instead: (),
-        })
-    }
-}
-
-impl<I: Interner> TypeVisitable<I> for TraitRef<I>
-where
-    I::GenericArgs: TypeVisitable<I>,
-{
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
-        self.args.visit_with(visitor)
-    }
-}
-
-impl<I: Interner> TypeFoldable<I> for TraitRef<I>
-where
-    I::GenericArgs: TypeFoldable<I>,
-{
-    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
-        Ok(TraitRef {
-            def_id: self.def_id,
-            args: self.args.try_fold_with(folder)?,
-            _use_trait_ref_new_instead: (),
-        })
-    }
-}
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index f2e4afecc40..d67327926ff 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -1,14 +1,12 @@
-use rustc_ast_ir::try_visit;
 #[cfg(feature = "nightly")]
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 #[cfg(feature = "nightly")]
 use rustc_data_structures::unify::{EqUnifyValue, UnifyKey};
 #[cfg(feature = "nightly")]
 use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable};
+use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
 use std::fmt;
 
-use crate::fold::{FallibleTypeFolder, TypeFoldable};
-use crate::visit::{TypeVisitable, TypeVisitor};
 use crate::Interner;
 use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, WithInfcx};
 
@@ -65,7 +63,7 @@ impl AliasKind {
 /// converted to this representation using `<dyn HirTyLowerer>::lower_ty`.
 #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "IrTyKind")]
 #[derive(derivative::Derivative)]
-#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""))]
+#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""), Eq(bound = ""))]
 #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
 pub enum TyKind<I: Interner> {
     /// The primitive boolean type. Written as `bool`.
@@ -341,9 +339,6 @@ impl<I: Interner> PartialEq for TyKind<I> {
     }
 }
 
-// This is manually implemented because a derive would require `I: Eq`
-impl<I: Interner> Eq for TyKind<I> {}
-
 impl<I: Interner> DebugWithInfcx<I> for TyKind<I> {
     fn fmt<Infcx: InferCtxtLike<Interner = I>>(
         this: WithInfcx<'_, Infcx, &Self>,
@@ -804,29 +799,8 @@ impl<I: Interner> DebugWithInfcx<I> for InferTy {
     Debug(bound = "")
 )]
 #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 pub struct TypeAndMut<I: Interner> {
     pub ty: I::Ty,
     pub mutbl: Mutability,
 }
-
-impl<I: Interner> TypeFoldable<I> for TypeAndMut<I>
-where
-    I::Ty: TypeFoldable<I>,
-{
-    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
-        Ok(TypeAndMut {
-            ty: self.ty.try_fold_with(folder)?,
-            mutbl: self.mutbl.try_fold_with(folder)?,
-        })
-    }
-}
-
-impl<I: Interner> TypeVisitable<I> for TypeAndMut<I>
-where
-    I::Ty: TypeVisitable<I>,
-{
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
-        try_visit!(self.ty.visit_with(visitor));
-        self.mutbl.visit_with(visitor)
-    }
-}
diff --git a/compiler/rustc_type_ir_macros/Cargo.toml b/compiler/rustc_type_ir_macros/Cargo.toml
new file mode 100644
index 00000000000..cb95ca68346
--- /dev/null
+++ b/compiler/rustc_type_ir_macros/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "rustc_type_ir_macros"
+version = "0.0.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+# tidy-alphabetical-start
+proc-macro2 = "1"
+quote = "1"
+syn = { version = "2.0.9", features = ["full"] }
+synstructure = "0.13.0"
+# tidy-alphabetical-end
diff --git a/compiler/rustc_type_ir_macros/src/lib.rs b/compiler/rustc_type_ir_macros/src/lib.rs
new file mode 100644
index 00000000000..000bcf2d3b2
--- /dev/null
+++ b/compiler/rustc_type_ir_macros/src/lib.rs
@@ -0,0 +1,159 @@
+use quote::quote;
+use syn::{parse_quote, visit_mut::VisitMut};
+use synstructure::decl_derive;
+
+decl_derive!(
+    [TypeFoldable_Generic] => type_foldable_derive
+);
+decl_derive!(
+    [TypeVisitable_Generic] => type_visitable_derive
+);
+decl_derive!(
+    [Lift_Generic] => lift_derive
+);
+
+fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    if let syn::Data::Union(_) = s.ast().data {
+        panic!("cannot derive on union")
+    }
+
+    if !s.ast().generics.type_params().any(|ty| ty.ident == "I") {
+        s.add_impl_generic(parse_quote! { I });
+    }
+
+    s.add_where_predicate(parse_quote! { I: Interner });
+    s.add_bounds(synstructure::AddBounds::Fields);
+    s.bind_with(|_| synstructure::BindStyle::Move);
+    let body_fold = s.each_variant(|vi| {
+        let bindings = vi.bindings();
+        vi.construct(|_, index| {
+            let bind = &bindings[index];
+            quote! {
+                ::rustc_type_ir::fold::TypeFoldable::try_fold_with(#bind, __folder)?
+            }
+        })
+    });
+
+    s.bound_impl(
+        quote!(::rustc_type_ir::fold::TypeFoldable<I>),
+        quote! {
+            fn try_fold_with<__F: ::rustc_type_ir::fold::FallibleTypeFolder<I>>(
+                self,
+                __folder: &mut __F
+            ) -> Result<Self, __F::Error> {
+                Ok(match self { #body_fold })
+            }
+        },
+    )
+}
+
+fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    if let syn::Data::Union(_) = s.ast().data {
+        panic!("cannot derive on union")
+    }
+
+    if !s.ast().generics.type_params().any(|ty| ty.ident == "I") {
+        s.add_impl_generic(parse_quote! { I });
+    }
+
+    s.add_bounds(synstructure::AddBounds::None);
+    s.add_where_predicate(parse_quote! { I: Interner });
+    s.add_impl_generic(parse_quote! { J });
+    s.add_where_predicate(parse_quote! { J: Interner });
+
+    let mut wc = vec![];
+    s.bind_with(|_| synstructure::BindStyle::Move);
+    let body_fold = s.each_variant(|vi| {
+        let bindings = vi.bindings();
+        vi.construct(|field, index| {
+            let ty = field.ty.clone();
+            let lifted_ty = lift(ty.clone());
+            wc.push(parse_quote! { #ty: ::rustc_type_ir::lift::Lift<J, Lifted = #lifted_ty> });
+            let bind = &bindings[index];
+            quote! {
+                #bind.lift_to_tcx(interner)?
+            }
+        })
+    });
+    for wc in wc {
+        s.add_where_predicate(wc);
+    }
+
+    let (_, ty_generics, _) = s.ast().generics.split_for_impl();
+    let name = s.ast().ident.clone();
+    let self_ty: syn::Type = parse_quote! { #name #ty_generics };
+    let lifted_ty = lift(self_ty);
+
+    s.bound_impl(
+        quote!(::rustc_type_ir::lift::Lift<J>),
+        quote! {
+            type Lifted = #lifted_ty;
+
+            fn lift_to_tcx(
+                self,
+                interner: J,
+            ) -> Option<Self::Lifted> {
+                Some(match self { #body_fold })
+            }
+        },
+    )
+}
+
+fn lift(mut ty: syn::Type) -> syn::Type {
+    struct ItoJ;
+    impl VisitMut for ItoJ {
+        fn visit_type_path_mut(&mut self, i: &mut syn::TypePath) {
+            if i.qself.is_none() {
+                if let Some(first) = i.path.segments.first_mut() {
+                    if first.ident == "I" {
+                        *first = parse_quote! { J };
+                    }
+                }
+            }
+            syn::visit_mut::visit_type_path_mut(self, i);
+        }
+    }
+
+    ItoJ.visit_type_mut(&mut ty);
+
+    ty
+}
+
+fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    if let syn::Data::Union(_) = s.ast().data {
+        panic!("cannot derive on union")
+    }
+
+    if !s.ast().generics.type_params().any(|ty| ty.ident == "I") {
+        s.add_impl_generic(parse_quote! { I });
+    }
+
+    s.add_where_predicate(parse_quote! { I: Interner });
+    s.add_bounds(synstructure::AddBounds::Fields);
+    let body_visit = s.each(|bind| {
+        quote! {
+            match ::rustc_ast_ir::visit::VisitorResult::branch(
+                ::rustc_type_ir::visit::TypeVisitable::visit_with(#bind, __visitor)
+            ) {
+                ::core::ops::ControlFlow::Continue(()) => {},
+                ::core::ops::ControlFlow::Break(r) => {
+                    return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
+                },
+            }
+        }
+    });
+    s.bind_with(|_| synstructure::BindStyle::Move);
+
+    s.bound_impl(
+        quote!(::rustc_type_ir::visit::TypeVisitable<I>),
+        quote! {
+            fn visit_with<__V: ::rustc_type_ir::visit::TypeVisitor<I>>(
+                &self,
+                __visitor: &mut __V
+            ) -> __V::Result {
+                match *self { #body_visit }
+                <__V::Result as ::rustc_ast_ir::visit::VisitorResult>::output()
+            }
+        },
+    )
+}