about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-06-07 06:49:55 +0000
committerbors <bors@rust-lang.org>2024-06-07 06:49:55 +0000
commit468310ea0cdec87460b83710a3a78825bcae73c5 (patch)
tree1dac4c134ac4b819b6d34f908886f94c9d2f1f46
parentb74702fbb2a2bd56b39849d66bd59a453ed8c89e (diff)
parent9436304871dc3ec411f14cbed68f418d2ed48a90 (diff)
downloadrust-468310ea0cdec87460b83710a3a78825bcae73c5.tar.gz
rust-468310ea0cdec87460b83710a3a78825bcae73c5.zip
Auto merge of #126108 - workingjubilee:rollup-g7m92b6, r=workingjubilee
Rollup of 7 pull requests

Successful merges:

 - #125606 (Size optimize int formatting)
 - #125724 (Uplift `Relate`/`TypeRelation` into `rustc_next_trait_solver`)
 - #126040 (Don't warn on fields in the `unreachable_pub` lint )
 - #126098 (Remove `same-lib-two-locations-no-panic` run-make test)
 - #126099 (Crate loader cleanups)
 - #126101 (Revert "Disallow ambiguous attributes on expressions" on nightly)
 - #126103 (Improve Docs for `hir::Impl` and `hir::ImplItem`)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock3
-rw-r--r--compiler/rustc_borrowck/src/constraints/mod.rs4
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs16
-rw-r--r--compiler/rustc_hir/src/hir.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs5
-rw-r--r--compiler/rustc_infer/Cargo.toml1
-rw-r--r--compiler/rustc_infer/src/infer/at.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs13
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs7
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs7
-rw-r--r--compiler/rustc_infer/src/infer/outlives/test_type_match.rs17
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/leak_check.rs4
-rw-r--r--compiler/rustc_infer/src/infer/relate/_match.rs (renamed from compiler/rustc_middle/src/ty/_match.rs)22
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs17
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs15
-rw-r--r--compiler/rustc_infer/src/infer/relate/glb.rs10
-rw-r--r--compiler/rustc_infer/src/infer/relate/higher_ranked.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lattice.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lub.rs10
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs20
-rw-r--r--compiler/rustc_infer/src/infer/relate/type_relating.rs17
-rw-r--r--compiler/rustc_lint/src/builtin.rs21
-rw-r--r--compiler/rustc_metadata/src/creader.rs1
-rw-r--r--compiler/rustc_metadata/src/locator.rs34
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/src/arena.rs2
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs2
-rw-r--r--compiler/rustc_middle/src/traits/query.rs9
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs4
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs6
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs8
-rw-r--r--compiler/rustc_middle/src/ty/context.rs38
-rw-r--r--compiler/rustc_middle/src/ty/error.rs163
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs36
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs817
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs8
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs119
-rw-r--r--compiler/rustc_next_trait_solver/Cargo.toml11
-rw-r--r--compiler/rustc_next_trait_solver/src/resolve.rs8
-rw-r--r--compiler/rustc_parse/messages.ftl2
-rw-r--r--compiler/rustc_parse/src/errors.rs9
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs32
-rw-r--r--compiler/rustc_session/src/filesearch.rs18
-rw-r--r--compiler/rustc_target/src/lib.rs12
-rw-r--r--compiler/rustc_target/src/spec/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs5
-rw-r--r--compiler/rustc_type_ir/src/error.rs106
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs56
-rw-r--r--compiler/rustc_type_ir/src/interner.rs46
-rw-r--r--compiler/rustc_type_ir/src/lib.rs8
-rw-r--r--compiler/rustc_type_ir/src/macros.rs18
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs33
-rw-r--r--compiler/rustc_type_ir/src/relate.rs666
-rw-r--r--compiler/rustc_type_ir/src/solve.rs6
-rw-r--r--library/core/src/fmt/num.rs33
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed6
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs6
-rw-r--r--src/tools/rustfmt/tests/source/attrib.rs4
-rw-r--r--src/tools/rustfmt/tests/target/attrib.rs4
-rw-r--r--tests/pretty/ast-stmt-expr-attr.rs18
-rw-r--r--tests/pretty/stmt_expr_attributes.rs12
-rw-r--r--tests/run-make/same-lib-two-locations-no-panic/bar.rs3
-rw-r--r--tests/run-make/same-lib-two-locations-no-panic/foo.rs1
-rw-r--r--tests/run-make/same-lib-two-locations-no-panic/rmake.rs28
-rw-r--r--tests/ui/lint/unreachable_pub.rs10
-rw-r--r--tests/ui/lint/unreachable_pub.stderr32
-rw-r--r--tests/ui/lint/unused/unused-doc-comments-edge-cases.rs4
-rw-r--r--tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr14
-rw-r--r--tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed15
-rw-r--r--tests/ui/parser/attribute/attr-binary-expr-ambigous.rs15
-rw-r--r--tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr46
-rw-r--r--tests/ui/proc-macro/issue-81555.rs3
77 files changed, 1485 insertions, 1305 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0e950e28807..019e7a3b16b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4201,6 +4201,7 @@ dependencies = [
  "rustc_middle",
  "rustc_span",
  "rustc_target",
+ "rustc_type_ir",
  "smallvec",
  "tracing",
 ]
@@ -4392,7 +4393,6 @@ dependencies = [
  "rustc_hir_pretty",
  "rustc_index",
  "rustc_macros",
- "rustc_next_trait_solver",
  "rustc_query_system",
  "rustc_serialize",
  "rustc_session",
@@ -4509,6 +4509,7 @@ dependencies = [
  "rustc_serialize",
  "rustc_type_ir",
  "rustc_type_ir_macros",
+ "tracing",
 ]
 
 [[package]]
diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs
index ff11e4db121..97408fa20d7 100644
--- a/compiler/rustc_borrowck/src/constraints/mod.rs
+++ b/compiler/rustc_borrowck/src/constraints/mod.rs
@@ -1,7 +1,7 @@
 use rustc_data_structures::graph::scc::Sccs;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::mir::ConstraintCategory;
-use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
+use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
 use rustc_span::Span;
 use std::fmt;
 use std::ops::Index;
@@ -97,7 +97,7 @@ pub struct OutlivesConstraint<'tcx> {
     pub category: ConstraintCategory<'tcx>,
 
     /// Variance diagnostic information
-    pub variance_info: VarianceDiagInfo<'tcx>,
+    pub variance_info: VarianceDiagInfo<TyCtxt<'tcx>>,
 
     /// If this constraint is promoted from closure requirements.
     pub from_closure: bool,
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index b57cf9066cf..0e3140ca98b 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -2304,5 +2304,5 @@ pub struct BlameConstraint<'tcx> {
     pub category: ConstraintCategory<'tcx>,
     pub from_closure: bool,
     pub cause: ObligationCause<'tcx>,
-    pub variance_info: ty::VarianceDiagInfo<'tcx>,
+    pub variance_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
 }
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index 9863c4a3883..2c34fc583c8 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -1,14 +1,14 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorGuaranteed;
+use rustc_infer::infer::relate::{ObligationEmittingRelation, StructurallyRelateAliases};
+use rustc_infer::infer::relate::{Relate, RelateResult, TypeRelation};
 use rustc_infer::infer::NllRegionVariableOrigin;
-use rustc_infer::infer::{ObligationEmittingRelation, StructurallyRelateAliases};
 use rustc_infer::traits::{Obligation, PredicateObligations};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::span_bug;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::fold::FnMutDelegate;
-use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
@@ -82,7 +82,7 @@ pub struct NllTypeRelating<'me, 'bccx, 'tcx> {
     /// - Bivariant means that it doesn't matter.
     ambient_variance: ty::Variance,
 
-    ambient_variance_info: ty::VarianceDiagInfo<'tcx>,
+    ambient_variance_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
 }
 
 impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
@@ -296,7 +296,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
         &mut self,
         sup: ty::Region<'tcx>,
         sub: ty::Region<'tcx>,
-        info: ty::VarianceDiagInfo<'tcx>,
+        info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
     ) {
         let sub = self.type_checker.borrowck_context.universal_regions.to_region_vid(sub);
         let sup = self.type_checker.borrowck_context.universal_regions.to_region_vid(sup);
@@ -314,7 +314,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
     }
 }
 
-impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
+impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.type_checker.infcx.tcx
     }
@@ -324,10 +324,10 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
     }
 
     #[instrument(skip(self, info), level = "trace", ret)]
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        info: ty::VarianceDiagInfo<'tcx>,
+        info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -445,7 +445,7 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         // We want that
         //
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 770dfcb98c9..29bc87eae7c 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2345,7 +2345,9 @@ impl ImplItemId {
     }
 }
 
-/// Represents anything within an `impl` block.
+/// Represents an associated item within an impl block.
+///
+/// Refer to [`Impl`] for an impl block declaration.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct ImplItem<'hir> {
     pub ident: Ident,
@@ -3327,6 +3329,10 @@ pub enum ItemKind<'hir> {
     Impl(&'hir Impl<'hir>),
 }
 
+/// Represents an impl block declaration.
+///
+/// E.g., `impl $Type { .. }` or `impl $Trait for $Type { .. }`
+/// Refer to [`ImplItem`] for an associated item within an impl block.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct Impl<'hir> {
     pub safety: Safety,
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 8a0623ef93e..72e5995e892 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -15,6 +15,7 @@ use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
 use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
 use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::span_bug;
+use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
 use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt};
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 93726ce2b3e..dbaa6e398c8 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -41,6 +41,7 @@ use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
+use rustc_infer::infer::relate::RelateResult;
 use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
 use rustc_infer::traits::{IfExpressionCause, MatchExpressionArmCause};
 use rustc_infer::traits::{Obligation, PredicateObligation};
@@ -51,7 +52,6 @@ use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
 };
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
 use rustc_session::parse::feature_err;
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 7dd7b3ff055..f0f32ae07a9 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -40,7 +40,7 @@ use rustc_infer::infer::InferOk;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
-use rustc_middle::ty::error::{ExpectedFound, TypeError::Sorts};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
@@ -682,7 +682,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             self.suggest_mismatched_types_on_tail(
                                 &mut err, expr, ty, e_ty, target_id,
                             );
-                            let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty }));
+                            let error =
+                                Some(TypeError::Sorts(ExpectedFound { expected: ty, found: e_ty }));
                             self.annotate_loop_expected_due_to_inference(err, expr, error);
                             if let Some(val) =
                                 self.err_ctxt().ty_kind_suggestion(self.param_env, ty)
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index c1565a7d40f..5136ab79a0f 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -18,6 +18,7 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
+rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 17e6d6250ad..046d908d148 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -27,8 +27,8 @@
 
 use super::*;
 
+use crate::infer::relate::{Relate, StructurallyRelateAliases, TypeRelation};
 use rustc_middle::bug;
-use rustc_middle::ty::relate::{Relate, TypeRelation};
 use rustc_middle::ty::{Const, ImplSubject};
 
 /// Whether we should define opaque types or just treat them opaquely.
@@ -90,7 +90,7 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 }
 
-pub trait ToTrace<'tcx>: Relate<'tcx> + Copy {
+pub trait ToTrace<'tcx>: Relate<TyCtxt<'tcx>> + Copy {
     fn to_trace(
         cause: &ObligationCause<'tcx>,
         a_is_expected: bool,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index fe0a246abbc..ed483c6cbeb 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -58,6 +58,7 @@ use crate::traits::{
     PredicateObligation,
 };
 
+use crate::infer::relate::{self, RelateResult, TypeRelation};
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{
     codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString,
@@ -71,8 +72,8 @@ use rustc_hir::lang_items::LangItem;
 use rustc_macros::extension;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::DepContext;
+use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _};
-use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
 use rustc_middle::ty::Upcast;
 use rustc_middle::ty::{
     self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable,
@@ -2686,7 +2687,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
     /// FloatVar inference type are compatible with themselves or their concrete types (Int and
     /// Float types, respectively). When comparing two ADTs, these rules apply recursively.
-    pub fn same_type_modulo_infer<T: relate::Relate<'tcx>>(&self, a: T, b: T) -> bool {
+    pub fn same_type_modulo_infer<T: relate::Relate<TyCtxt<'tcx>>>(&self, a: T, b: T) -> bool {
         let (a, b) = self.resolve_vars_if_possible((a, b));
         SameTypeModuloInfer(self).relate(a, b).is_ok()
     }
@@ -2694,7 +2695,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
 struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>);
 
-impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for SameTypeModuloInfer<'_, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.0.tcx
     }
@@ -2703,10 +2704,10 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
         "SameTypeModuloInfer"
     }
 
-    fn relate_with_variance<T: relate::Relate<'tcx>>(
+    fn relate_with_variance<T: relate::Relate<TyCtxt<'tcx>>>(
         &mut self,
         _variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> relate::RelateResult<'tcx, T> {
@@ -2754,7 +2755,7 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> relate::RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: relate::Relate<'tcx>,
+        T: relate::Relate<TyCtxt<'tcx>>,
     {
         Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
     }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index 19ef2d61fca..b88677b3a4e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -21,13 +21,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         sp: Span,
         body_owner_def_id: DefId,
     ) {
-        use ty::error::TypeError::*;
         debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
 
         let tcx = self.tcx;
 
         match err {
-            ArgumentSorts(values, _) | Sorts(values) => {
+            TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => {
                 match (*values.expected.kind(), *values.found.kind()) {
                     (ty::Closure(..), ty::Closure(..)) => {
                         diag.note("no two closures, even if identical, have the same type");
@@ -483,7 +482,7 @@ impl<T> Trait<T> for X {
                     values.found.kind(),
                 );
             }
-            CyclicTy(ty) => {
+            TypeError::CyclicTy(ty) => {
                 // Watch out for various cases of cyclic types and try to explain.
                 if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
                     diag.note(
@@ -494,7 +493,7 @@ impl<T> Trait<T> for X {
                     );
                 }
             }
-            TargetFeatureCast(def_id) => {
+            TypeError::TargetFeatureCast(def_id) => {
                 let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
                 diag.note(
                     "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 4476611d9c8..c606ab808ef 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1,9 +1,6 @@
 pub use at::DefineOpaqueTypes;
 pub use freshen::TypeFreshener;
 pub use lexical_region_resolve::RegionResolutionError;
-pub use relate::combine::CombineFields;
-pub use relate::combine::ObligationEmittingRelation;
-pub use relate::StructurallyRelateAliases;
 pub use rustc_macros::{TypeFoldable, TypeVisitable};
 pub use rustc_middle::ty::IntVarValue;
 pub use BoundRegionConversionTime::*;
@@ -11,6 +8,7 @@ pub use RegionVariableOrigin::*;
 pub use SubregionOrigin::*;
 pub use ValuePairs::*;
 
+use crate::infer::relate::{CombineFields, RelateResult};
 use crate::traits::{
     self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine,
 };
@@ -39,7 +37,6 @@ use rustc_middle::traits::select;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::BoundVarReplacerDelegate;
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
-use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt};
 use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid};
@@ -62,7 +59,7 @@ pub mod opaque_types;
 pub mod outlives;
 mod projection;
 pub mod region_constraints;
-mod relate;
+pub mod relate;
 pub mod resolve;
 pub(crate) mod snapshot;
 pub mod type_variable;
diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
index 29c11d4247d..978b92fd898 100644
--- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
+++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
@@ -1,15 +1,12 @@
 use std::collections::hash_map::Entry;
 
 use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::TypeVisitableExt;
-use rustc_middle::ty::{
-    self,
-    error::TypeError,
-    relate::{self, Relate, RelateResult, TypeRelation},
-    Ty, TyCtxt,
-};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 
 use crate::infer::region_constraints::VerifyIfEq;
+use crate::infer::relate::{self as relate, Relate, RelateResult, TypeRelation};
 
 /// Given a "verify-if-eq" type test like:
 ///
@@ -135,7 +132,7 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstHigherRankedOutlives<'tcx> {
     fn tag(&self) -> &'static str {
         "MatchAgainstHigherRankedOutlives"
     }
@@ -145,10 +142,10 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> {
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _: ty::VarianceDiagInfo<'tcx>,
+        _: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -208,7 +205,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> {
         value: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         self.pattern_depth.shift_in(1);
         let result = Ok(pattern.rebind(self.relate(pattern.skip_binder(), value.skip_binder())?));
diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
index 255ca52d3e9..5b159d62731 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
@@ -1,11 +1,11 @@
 use super::*;
+use crate::infer::relate::RelateResult;
 use crate::infer::snapshot::CombinedSnapshot;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::graph::{scc::Sccs, vec_graph::VecGraph};
 use rustc_index::Idx;
 use rustc_middle::span_bug;
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::relate::RelateResult;
 
 impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
     /// Searches new universes created during `snapshot`, looking for
@@ -276,7 +276,7 @@ impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> {
         other_region: ty::Region<'tcx>,
     ) -> TypeError<'tcx> {
         debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region);
-        TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound.kind, other_region)
+        TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound, other_region)
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_infer/src/infer/relate/_match.rs
index f30270abd5c..30a066a265a 100644
--- a/compiler/rustc_middle/src/ty/_match.rs
+++ b/compiler/rustc_infer/src/infer/relate/_match.rs
@@ -1,8 +1,10 @@
-use crate::ty::error::TypeError;
-use crate::ty::relate::{self, Relate, RelateResult, TypeRelation};
-use crate::ty::{self, InferConst, Ty, TyCtxt};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
 use tracing::{debug, instrument};
 
+use super::{structurally_relate_tys, Relate, RelateResult, TypeRelation};
+use crate::infer::relate;
+
 /// A type "A" *matches* "B" if the fresh types in B could be
 /// instantiated with values so as to make it equal to A. Matching is
 /// intended to be used only on freshened types, and it basically
@@ -29,7 +31,7 @@ impl<'tcx> MatchAgainstFreshVars<'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> {
     fn tag(&self) -> &'static str {
         "MatchAgainstFreshVars"
     }
@@ -38,10 +40,10 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
         self.tcx
     }
 
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         _: ty::Variance,
-        _: ty::VarianceDiagInfo<'tcx>,
+        _: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -72,12 +74,12 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
             ) => Ok(a),
 
             (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
-                Err(TypeError::Sorts(relate::expected_found(a, b)))
+                Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
             }
 
             (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.tcx(), guar)),
 
-            _ => relate::structurally_relate_tys(self, a, b),
+            _ => structurally_relate_tys(self, a, b),
         }
     }
 
@@ -97,7 +99,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
             }
 
             (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
-                return Err(TypeError::ConstMismatch(relate::expected_found(a, b)));
+                return Err(TypeError::ConstMismatch(ExpectedFound::new(true, a, b)));
             }
 
             _ => {}
@@ -112,7 +114,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
     }
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
index b193f4bcede..30cb2bab900 100644
--- a/compiler/rustc_infer/src/infer/relate/combine.rs
+++ b/compiler/rustc_infer/src/infer/relate/combine.rs
@@ -22,12 +22,13 @@ use super::glb::Glb;
 use super::lub::Lub;
 use super::type_relating::TypeRelating;
 use super::StructurallyRelateAliases;
+use super::{RelateResult, TypeRelation};
+use crate::infer::relate;
 use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
 use crate::traits::{Obligation, PredicateObligations};
 use rustc_middle::bug;
 use rustc_middle::infer::unify_key::EffectVarValue;
-use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::relate::{RelateResult, TypeRelation};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitableExt, Upcast};
 use rustc_middle::ty::{IntType, UintType};
 use rustc_span::Span;
@@ -121,7 +122,7 @@ impl<'tcx> InferCtxt<'tcx> {
             (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => {
                 match relation.structurally_relate_aliases() {
                     StructurallyRelateAliases::Yes => {
-                        ty::relate::structurally_relate_tys(relation, a, b)
+                        relate::structurally_relate_tys(relation, a, b)
                     }
                     StructurallyRelateAliases::No => {
                         relation.register_type_relate_obligation(a, b);
@@ -132,7 +133,7 @@ impl<'tcx> InferCtxt<'tcx> {
 
             // All other cases of inference are errors
             (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
-                Err(TypeError::Sorts(ty::relate::expected_found(a, b)))
+                Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
             }
 
             // During coherence, opaque types should be treated as *possibly*
@@ -144,7 +145,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 Ok(a)
             }
 
-            _ => ty::relate::structurally_relate_tys(relation, a, b),
+            _ => relate::structurally_relate_tys(relation, a, b),
         }
     }
 
@@ -234,11 +235,11 @@ impl<'tcx> InferCtxt<'tcx> {
                         Ok(b)
                     }
                     StructurallyRelateAliases::Yes => {
-                        ty::relate::structurally_relate_consts(relation, a, b)
+                        relate::structurally_relate_consts(relation, a, b)
                     }
                 }
             }
-            _ => ty::relate::structurally_relate_consts(relation, a, b),
+            _ => relate::structurally_relate_consts(relation, a, b),
         }
     }
 
@@ -303,7 +304,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
     }
 }
 
-pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
+pub trait ObligationEmittingRelation<'tcx>: TypeRelation<TyCtxt<'tcx>> {
     fn span(&self) -> Span;
 
     fn param_env(&self) -> ty::ParamEnv<'tcx>;
diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index 225c126fcf8..5478afda455 100644
--- a/compiler/rustc_infer/src/infer/relate/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -1,15 +1,16 @@
 use std::mem;
 
 use super::StructurallyRelateAliases;
+use super::{ObligationEmittingRelation, Relate, RelateResult, TypeRelation};
+use crate::infer::relate;
 use crate::infer::type_variable::TypeVariableValue;
-use crate::infer::{InferCtxt, ObligationEmittingRelation, RegionVariableOrigin};
+use crate::infer::{InferCtxt, RegionVariableOrigin};
 use rustc_data_structures::sso::SsoHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir::def_id::DefId;
 use rustc_middle::bug;
 use rustc_middle::infer::unify_key::ConstVariableValue;
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::visit::MaxUniverse;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{AliasRelationDirection, InferConst, Term, TypeVisitable, TypeVisitableExt};
@@ -228,7 +229,7 @@ impl<'tcx> InferCtxt<'tcx> {
 
     /// Attempts to generalize `source_term` for the type variable `target_vid`.
     /// This checks for cycles -- that is, whether `source_term` references `target_vid`.
-    fn generalize<T: Into<Term<'tcx>> + Relate<'tcx>>(
+    fn generalize<T: Into<Term<'tcx>> + Relate<TyCtxt<'tcx>>>(
         &self,
         span: Span,
         structurally_relate_aliases: StructurallyRelateAliases,
@@ -395,7 +396,7 @@ impl<'tcx> Generalizer<'_, 'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.infcx.tcx
     }
@@ -430,10 +431,10 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
     }
 
     #[instrument(level = "debug", skip(self, variance, b), ret)]
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -695,7 +696,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
         _: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         let result = self.relate(a.skip_binder(), a.skip_binder())?;
         Ok(a.rebind(result))
diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs
index a224a86492a..98e8f07c7a2 100644
--- a/compiler/rustc_infer/src/infer/relate/glb.rs
+++ b/compiler/rustc_infer/src/infer/relate/glb.rs
@@ -1,6 +1,6 @@
 //! Greatest lower bound. See [`lattice`].
 
-use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
+use super::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::Span;
 
@@ -21,7 +21,7 @@ impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Glb<'_, '_, 'tcx> {
     fn tag(&self) -> &'static str {
         "Glb"
     }
@@ -30,10 +30,10 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
         self.fields.tcx()
     }
 
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -81,7 +81,7 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         // GLB of a binder and itself is just itself
         if a == b {
diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
index d3001eb5838..cfce28aca5d 100644
--- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
+++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
@@ -1,10 +1,10 @@
 //! Helper routines for higher-ranked things. See the `doc` module at
 //! the end of the file for details.
 
+use super::RelateResult;
 use crate::infer::snapshot::CombinedSnapshot;
 use crate::infer::InferCtxt;
 use rustc_middle::ty::fold::FnMutDelegate;
-use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 
 impl<'tcx> InferCtxt<'tcx> {
diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs
index c0c51a2820b..f05b984142a 100644
--- a/compiler/rustc_infer/src/infer/relate/lattice.rs
+++ b/compiler/rustc_infer/src/infer/relate/lattice.rs
@@ -21,7 +21,7 @@ use super::combine::ObligationEmittingRelation;
 use crate::infer::{DefineOpaqueTypes, InferCtxt};
 use crate::traits::ObligationCause;
 
-use rustc_middle::ty::relate::RelateResult;
+use super::RelateResult;
 use rustc_middle::ty::TyVar;
 use rustc_middle::ty::{self, Ty};
 
diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs
index 83ab7770770..28dbaa94f95 100644
--- a/compiler/rustc_infer/src/infer/relate/lub.rs
+++ b/compiler/rustc_infer/src/infer/relate/lub.rs
@@ -6,7 +6,7 @@ use super::StructurallyRelateAliases;
 use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
 use crate::traits::{ObligationCause, PredicateObligations};
 
-use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
+use super::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::Span;
 
@@ -21,7 +21,7 @@ impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Lub<'_, '_, 'tcx> {
     fn tag(&self) -> &'static str {
         "Lub"
     }
@@ -30,10 +30,10 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
         self.fields.tcx()
     }
 
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -81,7 +81,7 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         // LUB of a binder and itself is just itself
         if a == b {
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
index 86a01130167..627c527cba1 100644
--- a/compiler/rustc_infer/src/infer/relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -1,6 +1,14 @@
 //! This module contains the definitions of most `TypeRelation`s in the type system
 //! (except for some relations used for diagnostics and heuristics in the compiler).
+//! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc).
 
+pub use rustc_middle::ty::relate::*;
+
+pub use self::_match::MatchAgainstFreshVars;
+pub use self::combine::CombineFields;
+pub use self::combine::ObligationEmittingRelation;
+
+pub mod _match;
 pub(super) mod combine;
 mod generalize;
 mod glb;
@@ -8,15 +16,3 @@ mod higher_ranked;
 mod lattice;
 mod lub;
 mod type_relating;
-
-/// Whether aliases should be related structurally or not. Used
-/// to adjust the behavior of generalization and combine.
-///
-/// This should always be `No` unless in a few special-cases when
-/// instantiating canonical responses and in the new solver. Each
-/// such case should have a comment explaining why it is used.
-#[derive(Debug, Copy, Clone)]
-pub enum StructurallyRelateAliases {
-    Yes,
-    No,
-}
diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs
index e55a5878821..fd0bc9f44f7 100644
--- a/compiler/rustc_infer/src/infer/relate/type_relating.rs
+++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs
@@ -1,12 +1,11 @@
 use super::combine::CombineFields;
 use crate::infer::BoundRegionConversionTime::HigherRankedType;
-use crate::infer::{
-    DefineOpaqueTypes, ObligationEmittingRelation, StructurallyRelateAliases, SubregionOrigin,
-};
+use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
 use crate::traits::{Obligation, PredicateObligations};
 
-use rustc_middle::ty::relate::{
-    relate_args_invariantly, relate_args_with_variances, Relate, RelateResult, TypeRelation,
+use super::{
+    relate_args_invariantly, relate_args_with_variances, ObligationEmittingRelation, Relate,
+    RelateResult, StructurallyRelateAliases, TypeRelation,
 };
 use rustc_middle::ty::TyVar;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -29,7 +28,7 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
     fn tag(&self) -> &'static str {
         "TypeRelating"
     }
@@ -56,10 +55,10 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
         }
     }
 
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -226,7 +225,7 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         if a == b {
             // Do nothing
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 87c433a5dc0..8c9abeafacf 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -51,7 +51,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_hir::intravisit::FnKind as HirFnKind;
-use rustc_hir::{Body, FnDecl, GenericParamKind, Node, PatKind, PredicateOrigin};
+use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin};
 use rustc_middle::bug;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::layout::LayoutOf;
@@ -1423,11 +1423,20 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
         self.perform_lint(cx, "item", foreign_item.owner_id.def_id, foreign_item.vis_span, true);
     }
 
-    fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
-        if matches!(cx.tcx.parent_hir_node(field.hir_id), Node::Variant(_)) {
-            return;
-        }
-        self.perform_lint(cx, "field", field.def_id, field.vis_span, false);
+    fn check_field_def(&mut self, _cx: &LateContext<'_>, _field: &hir::FieldDef<'_>) {
+        // - If an ADT definition is reported then we don't need to check fields
+        //   (as it would add unnecessary complexity to the source code, the struct
+        //   definition is in the immediate proximity to give the "real" visibility).
+        // - If an ADT is not reported because it's not `pub` - we don't need to
+        //   check fields.
+        // - If an ADT is not reported because it's reachable - we also don't need
+        //   to check fields because then they are reachable by construction if they
+        //   are pub.
+        //
+        // Therefore in no case we check the fields.
+        //
+        // cf. https://github.com/rust-lang/rust/pull/126013#issuecomment-2152839205
+        // cf. https://github.com/rust-lang/rust/pull/126040#issuecomment-2152944506
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 44a3e9760e1..ad283117d7e 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -581,7 +581,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
                 self.tcx.crate_types().iter().all(|c| *c == CrateType::Rlib),
                 hash,
                 extra_filename,
-                false, // is_host
                 path_kind,
             );
 
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index 73443de3553..90fe52a3438 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -222,7 +222,6 @@ use rustc_data_structures::owned_slice::slice_owned;
 use rustc_data_structures::svh::Svh;
 use rustc_errors::{DiagArgValue, IntoDiagArg};
 use rustc_fs_util::try_canonicalize;
-use rustc_session::config;
 use rustc_session::cstore::CrateSource;
 use rustc_session::filesearch::FileSearch;
 use rustc_session::search_paths::PathKind;
@@ -309,7 +308,6 @@ impl<'a> CrateLocator<'a> {
         is_rlib: bool,
         hash: Option<Svh>,
         extra_filename: Option<&'a str>,
-        is_host: bool,
         path_kind: PathKind,
     ) -> CrateLocator<'a> {
         let needs_object_code = sess.opts.output_types.should_codegen();
@@ -340,17 +338,9 @@ impl<'a> CrateLocator<'a> {
             },
             hash,
             extra_filename,
-            target: if is_host { &sess.host } else { &sess.target },
-            triple: if is_host {
-                TargetTriple::from_triple(config::host_triple())
-            } else {
-                sess.opts.target_triple.clone()
-            },
-            filesearch: if is_host {
-                sess.host_filesearch(path_kind)
-            } else {
-                sess.target_filesearch(path_kind)
-            },
+            target: &sess.target,
+            triple: sess.opts.target_triple.clone(),
+            filesearch: sess.target_filesearch(path_kind),
             is_proc_macro: false,
             crate_rejections: CrateRejections::default(),
         }
@@ -424,12 +414,18 @@ impl<'a> CrateLocator<'a> {
                 debug!("testing {}", spf.path.display());
 
                 let f = &spf.file_name_str;
-                let (hash, kind) = if f.starts_with(rlib_prefix) && f.ends_with(rlib_suffix) {
-                    (&f[rlib_prefix.len()..(f.len() - rlib_suffix.len())], CrateFlavor::Rlib)
-                } else if f.starts_with(rmeta_prefix) && f.ends_with(rmeta_suffix) {
-                    (&f[rmeta_prefix.len()..(f.len() - rmeta_suffix.len())], CrateFlavor::Rmeta)
-                } else if f.starts_with(dylib_prefix) && f.ends_with(dylib_suffix.as_ref()) {
-                    (&f[dylib_prefix.len()..(f.len() - dylib_suffix.len())], CrateFlavor::Dylib)
+                let (hash, kind) = if let Some(f) = f.strip_prefix(rlib_prefix)
+                    && let Some(f) = f.strip_suffix(rlib_suffix)
+                {
+                    (f, CrateFlavor::Rlib)
+                } else if let Some(f) = f.strip_prefix(rmeta_prefix)
+                    && let Some(f) = f.strip_suffix(rmeta_suffix)
+                {
+                    (f, CrateFlavor::Rmeta)
+                } else if let Some(f) = f.strip_prefix(dylib_prefix)
+                    && let Some(f) = f.strip_suffix(dylib_suffix.as_ref())
+                {
+                    (f, CrateFlavor::Dylib)
                 } else {
                     if f.starts_with(staticlib_prefix) && f.ends_with(staticlib_suffix.as_ref()) {
                         self.crate_rejections.via_kind.push(CrateMismatch {
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index ab0c598ea0c..d1cdabc293d 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -28,7 +28,6 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_hir_pretty = { path = "../rustc_hir_pretty" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
-rustc_next_trait_solver = { path = "../rustc_next_trait_solver" }
 rustc_query_system = { path = "../rustc_query_system" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index bf6ab800064..de786c38326 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -62,7 +62,7 @@ macro_rules! arena_types {
             [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>,
             [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>,
             [] canonical_goal_evaluation:
-                rustc_next_trait_solver::solve::inspect::CanonicalGoalEvaluationStep<
+                rustc_type_ir::solve::inspect::CanonicalGoalEvaluationStep<
                     rustc_middle::ty::TyCtxt<'tcx>
                 >,
             [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>,
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 63678ab659d..202d587f0ad 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -32,7 +32,7 @@ use std::hash::{Hash, Hasher};
 
 pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
 // FIXME: Remove this import and import via `solve::`
-pub use rustc_next_trait_solver::solve::BuiltinImplSource;
+pub use rustc_type_ir::solve::BuiltinImplSource;
 
 /// Depending on the stage of compilation, we want projection to be
 /// more or less conservative.
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index 66e50307733..50b6c77e1b2 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -7,13 +7,12 @@
 
 use crate::error::DropCheckOverflow;
 use crate::infer::canonical::{Canonical, QueryResponse};
-use crate::ty::error::TypeError;
 use crate::ty::GenericArg;
 use crate::ty::{self, Ty, TyCtxt};
 use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
 use rustc_span::Span;
 // FIXME: Remove this import and import via `traits::solve`.
-pub use rustc_next_trait_solver::solve::NoSolution;
+pub use rustc_type_ir::solve::NoSolution;
 
 pub mod type_op {
     use crate::ty::fold::TypeFoldable;
@@ -91,12 +90,6 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> =
 pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
     Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
 
-impl<'tcx> From<TypeError<'tcx>> for NoSolution {
-    fn from(_: TypeError<'tcx>) -> NoSolution {
-        NoSolution
-    }
-}
-
 #[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable)]
 pub struct DropckOutlivesResult<'tcx> {
     pub kinds: Vec<GenericArg<'tcx>>,
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index c8c16ec1e2c..0d9ce402c64 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -1,8 +1,8 @@
 use rustc_ast_ir::try_visit;
 use rustc_data_structures::intern::Interned;
 use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
-use rustc_next_trait_solver as ir;
-pub use rustc_next_trait_solver::solve::*;
+use rustc_type_ir as ir;
+pub use rustc_type_ir::solve::*;
 
 use crate::infer::canonical::QueryRegionConstraints;
 use crate::ty::{
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 5f9b870331c..886dbd317af 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -200,6 +200,12 @@ impl<'tcx> AdtDef<'tcx> {
     }
 }
 
+impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
+    fn def_id(self) -> DefId {
+        self.did()
+    }
+}
+
 #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)]
 pub enum AdtKind {
     Struct,
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index dc13cc5437d..cc1daeb6419 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -149,6 +149,10 @@ impl<'tcx> Const<'tcx> {
 }
 
 impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
+    fn try_to_target_usize(self, interner: TyCtxt<'tcx>) -> Option<u64> {
+        self.try_to_target_usize(interner)
+    }
+
     fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst) -> Self {
         Const::new_infer(tcx, infer)
     }
@@ -168,6 +172,10 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
     fn new_unevaluated(interner: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Self {
         Const::new_unevaluated(interner, uv)
     }
+
+    fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self {
+        Const::new_expr(interner, expr)
+    }
 }
 
 impl<'tcx> Const<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 6b35b1f2d13..54daeffcb52 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -69,6 +69,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx};
 use rustc_target::spec::abi;
+use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::WithCachedTypeInfo;
 use rustc_type_ir::{CollectAndApply, Interner, TypeFlags};
@@ -135,9 +136,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type ParamEnv = ty::ParamEnv<'tcx>;
     type Predicate = Predicate<'tcx>;
     type Clause = Clause<'tcx>;
-
     type Clauses = ty::Clauses<'tcx>;
 
+    fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {
+        self.expand_abstract_consts(t)
+    }
+
     fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
         self.mk_canonical_var_infos(infos)
     }
@@ -148,6 +152,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.generics_of(def_id)
     }
 
+    type VariancesOf = &'tcx [ty::Variance];
+
+    fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf {
+        self.variances_of(def_id)
+    }
+
     fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
         self.type_of(def_id)
     }
@@ -205,7 +215,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.mk_args(args)
     }
 
-    fn mk_args_from_iter(self, args: impl Iterator<Item = Self::GenericArg>) -> Self::GenericArgs {
+    fn mk_args_from_iter<I, T>(self, args: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<Self::GenericArg, Self::GenericArgs>,
+    {
         self.mk_args_from_iter(args)
     }
 
@@ -224,6 +238,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.arena.alloc(step)
     }
 
+    fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<Self::Ty, Self::Tys>,
+    {
+        self.mk_type_list_from_iter(args)
+    }
+
     fn parent(self, def_id: Self::DefId) -> Self::DefId {
         self.parent(def_id)
     }
@@ -231,6 +253,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     fn recursion_limit(self) -> usize {
         self.recursion_limit().0
     }
+
+    type Features = &'tcx rustc_feature::Features;
+
+    fn features(self) -> Self::Features {
+        self.features()
+    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::Abi<TyCtxt<'tcx>> for abi::Abi {
@@ -249,6 +277,12 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
     }
 }
 
+impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_feature::Features {
+    fn generic_const_exprs(self) -> bool {
+        self.generic_const_exprs
+    }
+}
+
 type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
 
 pub struct CtxtInterners<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 9e2c626478a..32dc9fa5fc6 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -1,89 +1,26 @@
 use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, PrettyPrinter};
-use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
+use crate::ty::{self, Ty, TyCtxt};
+
 use rustc_errors::pluralize;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind};
-use rustc_hir::def_id::DefId;
-use rustc_macros::{TypeFoldable, TypeVisitable};
-use rustc_span::symbol::Symbol;
-use rustc_target::spec::abi;
+use rustc_macros::extension;
+pub use rustc_type_ir::error::ExpectedFound;
+
 use std::borrow::Cow;
 use std::hash::{DefaultHasher, Hash, Hasher};
 use std::path::PathBuf;
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)]
-pub struct ExpectedFound<T> {
-    pub expected: T,
-    pub found: T,
-}
-
-impl<T> ExpectedFound<T> {
-    pub fn new(a_is_expected: bool, a: T, b: T) -> Self {
-        if a_is_expected {
-            ExpectedFound { expected: a, found: b }
-        } else {
-            ExpectedFound { expected: b, found: a }
-        }
-    }
-}
-
-// Data structures used in type unification
-#[derive(Copy, Clone, Debug, TypeVisitable, PartialEq, Eq)]
-#[rustc_pass_by_value]
-pub enum TypeError<'tcx> {
-    Mismatch,
-    ConstnessMismatch(ExpectedFound<ty::BoundConstness>),
-    PolarityMismatch(ExpectedFound<ty::PredicatePolarity>),
-    SafetyMismatch(ExpectedFound<hir::Safety>),
-    AbiMismatch(ExpectedFound<abi::Abi>),
-    Mutability,
-    ArgumentMutability(usize),
-    TupleSize(ExpectedFound<usize>),
-    FixedArraySize(ExpectedFound<u64>),
-    ArgCount,
-    FieldMisMatch(Symbol, Symbol),
-
-    RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
-    RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
-    RegionsPlaceholderMismatch,
-
-    Sorts(ExpectedFound<Ty<'tcx>>),
-    ArgumentSorts(ExpectedFound<Ty<'tcx>>, usize),
-    Traits(ExpectedFound<DefId>),
-    VariadicMismatch(ExpectedFound<bool>),
-
-    /// Instantiating a type variable with the given type would have
-    /// created a cycle (because it appears somewhere within that
-    /// type).
-    CyclicTy(Ty<'tcx>),
-    CyclicConst(ty::Const<'tcx>),
-    ProjectionMismatched(ExpectedFound<DefId>),
-    ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>),
-    ConstMismatch(ExpectedFound<ty::Const<'tcx>>),
-
-    IntrinsicCast,
-    /// Safe `#[target_feature]` functions are not assignable to safe function pointers.
-    TargetFeatureCast(DefId),
-}
-
-impl TypeError<'_> {
-    pub fn involves_regions(self) -> bool {
-        match self {
-            TypeError::RegionsDoesNotOutlive(_, _)
-            | TypeError::RegionsInsufficientlyPolymorphic(_, _)
-            | TypeError::RegionsPlaceholderMismatch => true,
-            _ => false,
-        }
-    }
-}
+pub type TypeError<'tcx> = rustc_type_ir::error::TypeError<TyCtxt<'tcx>>;
 
-/// Explains the source of a type err in a short, human readable way. This is meant to be placed
-/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()`
-/// afterwards to present additional details, particularly when it comes to lifetime-related
-/// errors.
+/// Explains the source of a type err in a short, human readable way.
+/// This is meant to be placed in parentheses after some larger message.
+/// You should also invoke `note_and_explain_type_err()` afterwards
+/// to present additional details, particularly when it comes to lifetime-
+/// related errors.
+#[extension(pub trait TypeErrorToStringExt<'tcx>)]
 impl<'tcx> TypeError<'tcx> {
-    pub fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
-        use self::TypeError::*;
+    fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
         fn report_maybe_different(expected: &str, found: &str) -> String {
             // A naive approach to making sure that we're not reporting silly errors such as:
             // (expected closure, found closure).
@@ -95,24 +32,26 @@ impl<'tcx> TypeError<'tcx> {
         }
 
         match self {
-            CyclicTy(_) => "cyclic type of infinite size".into(),
-            CyclicConst(_) => "encountered a self-referencing constant".into(),
-            Mismatch => "types differ".into(),
-            ConstnessMismatch(values) => {
+            TypeError::CyclicTy(_) => "cyclic type of infinite size".into(),
+            TypeError::CyclicConst(_) => "encountered a self-referencing constant".into(),
+            TypeError::Mismatch => "types differ".into(),
+            TypeError::ConstnessMismatch(values) => {
                 format!("expected {} bound, found {} bound", values.expected, values.found).into()
             }
-            PolarityMismatch(values) => {
+            TypeError::PolarityMismatch(values) => {
                 format!("expected {} polarity, found {} polarity", values.expected, values.found)
                     .into()
             }
-            SafetyMismatch(values) => {
+            TypeError::SafetyMismatch(values) => {
                 format!("expected {} fn, found {} fn", values.expected, values.found).into()
             }
-            AbiMismatch(values) => {
+            TypeError::AbiMismatch(values) => {
                 format!("expected {} fn, found {} fn", values.expected, values.found).into()
             }
-            ArgumentMutability(_) | Mutability => "types differ in mutability".into(),
-            TupleSize(values) => format!(
+            TypeError::ArgumentMutability(_) | TypeError::Mutability => {
+                "types differ in mutability".into()
+            }
+            TypeError::TupleSize(values) => format!(
                 "expected a tuple with {} element{}, found one with {} element{}",
                 values.expected,
                 pluralize!(values.expected),
@@ -120,7 +59,7 @@ impl<'tcx> TypeError<'tcx> {
                 pluralize!(values.found)
             )
             .into(),
-            FixedArraySize(values) => format!(
+            TypeError::FixedArraySize(values) => format!(
                 "expected an array with a fixed size of {} element{}, found one with {} element{}",
                 values.expected,
                 pluralize!(values.expected),
@@ -128,20 +67,21 @@ impl<'tcx> TypeError<'tcx> {
                 pluralize!(values.found)
             )
             .into(),
-            ArgCount => "incorrect number of function parameters".into(),
-            FieldMisMatch(adt, field) => format!("field type mismatch: {adt}.{field}").into(),
-            RegionsDoesNotOutlive(..) => "lifetime mismatch".into(),
+            TypeError::ArgCount => "incorrect number of function parameters".into(),
+            TypeError::RegionsDoesNotOutlive(..) => "lifetime mismatch".into(),
             // Actually naming the region here is a bit confusing because context is lacking
-            RegionsInsufficientlyPolymorphic(..) => {
+            TypeError::RegionsInsufficientlyPolymorphic(..) => {
+                "one type is more general than the other".into()
+            }
+            TypeError::RegionsPlaceholderMismatch => {
                 "one type is more general than the other".into()
             }
-            RegionsPlaceholderMismatch => "one type is more general than the other".into(),
-            ArgumentSorts(values, _) | Sorts(values) => {
+            TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => {
                 let expected = values.expected.sort_string(tcx);
                 let found = values.found.sort_string(tcx);
                 report_maybe_different(&expected, &found).into()
             }
-            Traits(values) => {
+            TypeError::Traits(values) => {
                 let (mut expected, mut found) = with_forced_trimmed_paths!((
                     tcx.def_path_str(values.expected),
                     tcx.def_path_str(values.found),
@@ -153,59 +93,34 @@ impl<'tcx> TypeError<'tcx> {
                 report_maybe_different(&format!("trait `{expected}`"), &format!("trait `{found}`"))
                     .into()
             }
-            VariadicMismatch(ref values) => format!(
+            TypeError::VariadicMismatch(ref values) => format!(
                 "expected {} fn, found {} function",
                 if values.expected { "variadic" } else { "non-variadic" },
                 if values.found { "variadic" } else { "non-variadic" }
             )
             .into(),
-            ProjectionMismatched(ref values) => format!(
+            TypeError::ProjectionMismatched(ref values) => format!(
                 "expected `{}`, found `{}`",
                 tcx.def_path_str(values.expected),
                 tcx.def_path_str(values.found)
             )
             .into(),
-            ExistentialMismatch(ref values) => report_maybe_different(
+            TypeError::ExistentialMismatch(ref values) => report_maybe_different(
                 &format!("trait `{}`", values.expected),
                 &format!("trait `{}`", values.found),
             )
             .into(),
-            ConstMismatch(ref values) => {
+            TypeError::ConstMismatch(ref values) => {
                 format!("expected `{}`, found `{}`", values.expected, values.found).into()
             }
-            IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
-            TargetFeatureCast(_) => {
+            TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
+            TypeError::TargetFeatureCast(_) => {
                 "cannot coerce functions with `#[target_feature]` to safe function pointers".into()
             }
         }
     }
 }
 
-impl<'tcx> TypeError<'tcx> {
-    pub fn must_include_note(self) -> bool {
-        use self::TypeError::*;
-        match self {
-            CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_)
-            | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
-            | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false,
-
-            Mutability
-            | ArgumentMutability(_)
-            | TupleSize(_)
-            | ArgCount
-            | FieldMisMatch(..)
-            | RegionsDoesNotOutlive(..)
-            | RegionsInsufficientlyPolymorphic(..)
-            | RegionsPlaceholderMismatch
-            | Traits(_)
-            | ProjectionMismatched(_)
-            | ExistentialMismatch(_)
-            | ConstMismatch(_)
-            | IntrinsicCast => true,
-        }
-    }
-}
-
 impl<'tcx> Ty<'tcx> {
     pub fn sort_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
         match *self.kind() {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index e0fbf127e70..7ff1b799822 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -60,6 +60,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{ExpnId, ExpnKind, Span};
 use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx};
 pub use rustc_target::abi::{ReprFlags, ReprOptions};
+pub use rustc_type_ir::relate::VarianceDiagInfo;
 pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx};
 use tracing::{debug, instrument};
 pub use vtable::*;
@@ -114,7 +115,7 @@ pub use self::rvalue_scopes::RvalueScopes;
 pub use self::sty::{
     AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig,
     CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst,
-    ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo,
+    ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs,
 };
 pub use self::trait_def::TraitDef;
 pub use self::typeck_results::{
@@ -122,7 +123,6 @@ pub use self::typeck_results::{
     TypeckResults, UserType, UserTypeAnnotationIndex,
 };
 
-pub mod _match;
 pub mod abstract_const;
 pub mod adjustment;
 pub mod cast;
@@ -313,38 +313,6 @@ impl Visibility {
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)]
-pub enum BoundConstness {
-    /// `Type: Trait`
-    NotConst,
-    /// `Type: const Trait`
-    Const,
-    /// `Type: ~const Trait`
-    ///
-    /// Requires resolving to const only when we are in a const context.
-    ConstIfConst,
-}
-
-impl BoundConstness {
-    pub fn as_str(self) -> &'static str {
-        match self {
-            Self::NotConst => "",
-            Self::Const => "const",
-            Self::ConstIfConst => "~const",
-        }
-    }
-}
-
-impl fmt::Display for BoundConstness {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::NotConst => f.write_str("normal"),
-            Self::Const => f.write_str("const"),
-            Self::ConstIfConst => f.write_str("~const"),
-        }
-    }
-}
-
 #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)]
 #[derive(TypeFoldable, TypeVisitable)]
 pub struct ClosureSizeProfileData<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index e24e64b2301..b169d672a84 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -1,383 +1,54 @@
-//! Generalized type relating mechanism.
-//!
-//! A type relation `R` relates a pair of values `(A, B)`. `A and B` are usually
-//! types or regions but can be other things. Examples of type relations are
-//! subtyping, type equality, etc.
+use std::iter;
 
-use crate::ty::error::{ExpectedFound, TypeError};
-use crate::ty::{
-    self, ExistentialPredicate, ExistentialPredicateStableCmpExt as _, GenericArg, GenericArgKind,
-    GenericArgsRef, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable,
-};
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_macros::TypeVisitable;
 use rustc_target::spec::abi;
-use std::iter;
-use tracing::{debug, instrument};
-
-use super::Pattern;
-
-pub type RelateResult<'tcx, T> = Result<T, TypeError<'tcx>>;
-
-pub trait TypeRelation<'tcx>: Sized {
-    fn tcx(&self) -> TyCtxt<'tcx>;
-
-    /// Returns a static string we can use for printouts.
-    fn tag(&self) -> &'static str;
-
-    /// Generic relation routine suitable for most anything.
-    fn relate<T: Relate<'tcx>>(&mut self, a: T, b: T) -> RelateResult<'tcx, T> {
-        Relate::relate(self, a, b)
-    }
-
-    /// Relate the two args for the given item. The default
-    /// is to look up the variance for the item and proceed
-    /// accordingly.
-    fn relate_item_args(
-        &mut self,
-        item_def_id: DefId,
-        a_arg: GenericArgsRef<'tcx>,
-        b_arg: GenericArgsRef<'tcx>,
-    ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
-        debug!(
-            "relate_item_args(item_def_id={:?}, a_arg={:?}, b_arg={:?})",
-            item_def_id, a_arg, b_arg
-        );
-
-        let tcx = self.tcx();
-        let opt_variances = tcx.variances_of(item_def_id);
-        relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, true)
-    }
-
-    /// Switch variance for the purpose of relating `a` and `b`.
-    fn relate_with_variance<T: Relate<'tcx>>(
-        &mut self,
-        variance: ty::Variance,
-        info: ty::VarianceDiagInfo<'tcx>,
-        a: T,
-        b: T,
-    ) -> RelateResult<'tcx, T>;
-
-    // Overridable relations. You shouldn't typically call these
-    // directly, instead call `relate()`, which in turn calls
-    // these. This is both more uniform but also allows us to add
-    // additional hooks for other types in the future if needed
-    // without making older code, which called `relate`, obsolete.
-
-    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>;
-
-    fn regions(
-        &mut self,
-        a: ty::Region<'tcx>,
-        b: ty::Region<'tcx>,
-    ) -> RelateResult<'tcx, ty::Region<'tcx>>;
-
-    fn consts(
-        &mut self,
-        a: ty::Const<'tcx>,
-        b: ty::Const<'tcx>,
-    ) -> RelateResult<'tcx, ty::Const<'tcx>>;
-
-    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>;
-}
-
-pub trait Relate<'tcx>: TypeFoldable<TyCtxt<'tcx>> + PartialEq + Copy {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: Self,
-        b: Self,
-    ) -> RelateResult<'tcx, Self>;
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Relate impls
-
-#[inline]
-pub fn relate_args_invariantly<'tcx, R: TypeRelation<'tcx>>(
-    relation: &mut R,
-    a_arg: GenericArgsRef<'tcx>,
-    b_arg: GenericArgsRef<'tcx>,
-) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
-    relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| {
-        relation.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)
-    }))
-}
-
-pub fn relate_args_with_variances<'tcx, R: TypeRelation<'tcx>>(
-    relation: &mut R,
-    ty_def_id: DefId,
-    variances: &[ty::Variance],
-    a_arg: GenericArgsRef<'tcx>,
-    b_arg: GenericArgsRef<'tcx>,
-    fetch_ty_for_diag: bool,
-) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
-    let tcx = relation.tcx();
-
-    let mut cached_ty = None;
-    let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| {
-        let variance = variances[i];
-        let variance_info = if variance == ty::Invariant && fetch_ty_for_diag {
-            let ty =
-                *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, a_arg));
-            ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() }
-        } else {
-            ty::VarianceDiagInfo::default()
-        };
-        relation.relate_with_variance(variance, variance_info, a, b)
-    });
-
-    tcx.mk_args_from_iter(params)
-}
-
-impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::FnSig<'tcx>,
-        b: ty::FnSig<'tcx>,
-    ) -> RelateResult<'tcx, ty::FnSig<'tcx>> {
-        let tcx = relation.tcx();
-
-        if a.c_variadic != b.c_variadic {
-            return Err(TypeError::VariadicMismatch(expected_found(a.c_variadic, b.c_variadic)));
-        }
-        let safety = relation.relate(a.safety, b.safety)?;
-        let abi = relation.relate(a.abi, b.abi)?;
-
-        if a.inputs().len() != b.inputs().len() {
-            return Err(TypeError::ArgCount);
-        }
+pub use rustc_type_ir::relate::*;
 
-        let inputs_and_output = iter::zip(a.inputs(), b.inputs())
-            .map(|(&a, &b)| ((a, b), false))
-            .chain(iter::once(((a.output(), b.output()), true)))
-            .map(|((a, b), is_output)| {
-                if is_output {
-                    relation.relate(a, b)
-                } else {
-                    relation.relate_with_variance(
-                        ty::Contravariant,
-                        ty::VarianceDiagInfo::default(),
-                        a,
-                        b,
-                    )
-                }
-            })
-            .enumerate()
-            .map(|(i, r)| match r {
-                Err(TypeError::Sorts(exp_found) | TypeError::ArgumentSorts(exp_found, _)) => {
-                    Err(TypeError::ArgumentSorts(exp_found, i))
-                }
-                Err(TypeError::Mutability | TypeError::ArgumentMutability(_)) => {
-                    Err(TypeError::ArgumentMutability(i))
-                }
-                r => r,
-            });
-        Ok(ty::FnSig {
-            inputs_and_output: tcx.mk_type_list_from_iter(inputs_and_output)?,
-            c_variadic: a.c_variadic,
-            safety,
-            abi,
-        })
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::BoundConstness {
-    fn relate<R: TypeRelation<'tcx>>(
-        _relation: &mut R,
-        a: ty::BoundConstness,
-        b: ty::BoundConstness,
-    ) -> RelateResult<'tcx, ty::BoundConstness> {
-        if a != b { Err(TypeError::ConstnessMismatch(expected_found(a, b))) } else { Ok(a) }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for hir::Safety {
-    fn relate<R: TypeRelation<'tcx>>(
-        _relation: &mut R,
-        a: hir::Safety,
-        b: hir::Safety,
-    ) -> RelateResult<'tcx, hir::Safety> {
-        if a != b { Err(TypeError::SafetyMismatch(expected_found(a, b))) } else { Ok(a) }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for abi::Abi {
-    fn relate<R: TypeRelation<'tcx>>(
-        _relation: &mut R,
-        a: abi::Abi,
-        b: abi::Abi,
-    ) -> RelateResult<'tcx, abi::Abi> {
-        if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(expected_found(a, b))) }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::AliasTy<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::AliasTy<'tcx>,
-        b: ty::AliasTy<'tcx>,
-    ) -> RelateResult<'tcx, ty::AliasTy<'tcx>> {
-        if a.def_id != b.def_id {
-            Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id)))
-        } else {
-            let args = match a.kind(relation.tcx()) {
-                ty::Opaque => relate_args_with_variances(
-                    relation,
-                    a.def_id,
-                    relation.tcx().variances_of(a.def_id),
-                    a.args,
-                    b.args,
-                    false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
-                )?,
-                ty::Projection | ty::Weak | ty::Inherent => {
-                    relate_args_invariantly(relation, a.args, b.args)?
-                }
-            };
-            Ok(ty::AliasTy::new(relation.tcx(), a.def_id, args))
-        }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::AliasTerm<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::AliasTerm<'tcx>,
-        b: ty::AliasTerm<'tcx>,
-    ) -> RelateResult<'tcx, ty::AliasTerm<'tcx>> {
-        if a.def_id != b.def_id {
-            Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id)))
-        } else {
-            let args = match a.kind(relation.tcx()) {
-                ty::AliasTermKind::OpaqueTy => relate_args_with_variances(
-                    relation,
-                    a.def_id,
-                    relation.tcx().variances_of(a.def_id),
-                    a.args,
-                    b.args,
-                    false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
-                )?,
-                ty::AliasTermKind::ProjectionTy
-                | ty::AliasTermKind::WeakTy
-                | ty::AliasTermKind::InherentTy
-                | ty::AliasTermKind::UnevaluatedConst
-                | ty::AliasTermKind::ProjectionConst => {
-                    relate_args_invariantly(relation, a.args, b.args)?
-                }
-            };
-            Ok(ty::AliasTerm::new(relation.tcx(), a.def_id, args))
-        }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::ExistentialProjection<'tcx>,
-        b: ty::ExistentialProjection<'tcx>,
-    ) -> RelateResult<'tcx, ty::ExistentialProjection<'tcx>> {
-        if a.def_id != b.def_id {
-            Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id)))
-        } else {
-            let term = relation.relate_with_variance(
-                ty::Invariant,
-                ty::VarianceDiagInfo::default(),
-                a.term,
-                b.term,
-            )?;
-            let args = relation.relate_with_variance(
-                ty::Invariant,
-                ty::VarianceDiagInfo::default(),
-                a.args,
-                b.args,
-            )?;
-            Ok(ty::ExistentialProjection { def_id: a.def_id, args, term })
-        }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::TraitRef<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::TraitRef<'tcx>,
-        b: ty::TraitRef<'tcx>,
-    ) -> RelateResult<'tcx, ty::TraitRef<'tcx>> {
-        // Different traits cannot be related.
-        if a.def_id != b.def_id {
-            Err(TypeError::Traits(expected_found(a.def_id, b.def_id)))
-        } else {
-            let args = relate_args_invariantly(relation, a.args, b.args)?;
-            Ok(ty::TraitRef::new(relation.tcx(), a.def_id, args))
-        }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::ExistentialTraitRef<'tcx>,
-        b: ty::ExistentialTraitRef<'tcx>,
-    ) -> RelateResult<'tcx, ty::ExistentialTraitRef<'tcx>> {
-        // Different traits cannot be related.
-        if a.def_id != b.def_id {
-            Err(TypeError::Traits(expected_found(a.def_id, b.def_id)))
-        } else {
-            let args = relate_args_invariantly(relation, a.args, b.args)?;
-            Ok(ty::ExistentialTraitRef { def_id: a.def_id, args })
-        }
-    }
-}
+use crate::ty::error::{ExpectedFound, TypeError};
+use crate::ty::predicate::ExistentialPredicateStableCmpExt as _;
+use crate::ty::{self as ty, Ty, TyCtxt};
 
-#[derive(PartialEq, Copy, Debug, Clone, TypeFoldable, TypeVisitable)]
-struct CoroutineWitness<'tcx>(&'tcx ty::List<Ty<'tcx>>);
+pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult<TyCtxt<'tcx>, T>;
 
-impl<'tcx> Relate<'tcx> for CoroutineWitness<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: CoroutineWitness<'tcx>,
-        b: CoroutineWitness<'tcx>,
-    ) -> RelateResult<'tcx, CoroutineWitness<'tcx>> {
-        assert_eq!(a.0.len(), b.0.len());
-        let tcx = relation.tcx();
-        let types =
-            tcx.mk_type_list_from_iter(iter::zip(a.0, b.0).map(|(a, b)| relation.relate(a, b)))?;
-        Ok(CoroutineWitness(types))
-    }
+/// Whether aliases should be related structurally or not. Used
+/// to adjust the behavior of generalization and combine.
+///
+/// This should always be `No` unless in a few special-cases when
+/// instantiating canonical responses and in the new solver. Each
+/// such case should have a comment explaining why it is used.
+#[derive(Debug, Copy, Clone)]
+pub enum StructurallyRelateAliases {
+    Yes,
+    No,
 }
 
-impl<'tcx> Relate<'tcx> for ImplSubject<'tcx> {
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::ImplSubject<'tcx> {
     #[inline]
-    fn relate<R: TypeRelation<'tcx>>(
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
-        a: ImplSubject<'tcx>,
-        b: ImplSubject<'tcx>,
-    ) -> RelateResult<'tcx, ImplSubject<'tcx>> {
+        a: ty::ImplSubject<'tcx>,
+        b: ty::ImplSubject<'tcx>,
+    ) -> RelateResult<'tcx, ty::ImplSubject<'tcx>> {
         match (a, b) {
-            (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => {
+            (ty::ImplSubject::Trait(trait_ref_a), ty::ImplSubject::Trait(trait_ref_b)) => {
                 let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?;
-                Ok(ImplSubject::Trait(trait_ref))
+                Ok(ty::ImplSubject::Trait(trait_ref))
             }
-            (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => {
+            (ty::ImplSubject::Inherent(ty_a), ty::ImplSubject::Inherent(ty_b)) => {
                 let ty = Ty::relate(relation, ty_a, ty_b)?;
-                Ok(ImplSubject::Inherent(ty))
+                Ok(ty::ImplSubject::Inherent(ty))
             }
-            (ImplSubject::Trait(_), ImplSubject::Inherent(_))
-            | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => {
+            (ty::ImplSubject::Trait(_), ty::ImplSubject::Inherent(_))
+            | (ty::ImplSubject::Inherent(_), ty::ImplSubject::Trait(_)) => {
                 bug!("can not relate TraitRef and Ty");
             }
         }
     }
 }
 
-impl<'tcx> Relate<'tcx> for Ty<'tcx> {
+impl<'tcx> Relate<TyCtxt<'tcx>> for Ty<'tcx> {
     #[inline]
-    fn relate<R: TypeRelation<'tcx>>(
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: Ty<'tcx>,
         b: Ty<'tcx>,
@@ -386,9 +57,9 @@ impl<'tcx> Relate<'tcx> for Ty<'tcx> {
     }
 }
 
-impl<'tcx> Relate<'tcx> for Pattern<'tcx> {
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Pattern<'tcx> {
     #[inline]
-    fn relate<R: TypeRelation<'tcx>>(
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: Self,
         b: Self,
@@ -416,276 +87,8 @@ impl<'tcx> Relate<'tcx> for Pattern<'tcx> {
     }
 }
 
-/// Relates `a` and `b` structurally, calling the relation for all nested values.
-/// Any semantic equality, e.g. of projections, and inference variables have to be
-/// handled by the caller.
-#[instrument(level = "trace", skip(relation), ret)]
-pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
-    relation: &mut R,
-    a: Ty<'tcx>,
-    b: Ty<'tcx>,
-) -> RelateResult<'tcx, Ty<'tcx>> {
-    let tcx = relation.tcx();
-    match (a.kind(), b.kind()) {
-        (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
-            // The caller should handle these cases!
-            bug!("var types encountered in structurally_relate_tys")
-        }
-
-        (ty::Bound(..), _) | (_, ty::Bound(..)) => {
-            bug!("bound types encountered in structurally_relate_tys")
-        }
-
-        (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)),
-
-        (&ty::Never, _)
-        | (&ty::Char, _)
-        | (&ty::Bool, _)
-        | (&ty::Int(_), _)
-        | (&ty::Uint(_), _)
-        | (&ty::Float(_), _)
-        | (&ty::Str, _)
-            if a == b =>
-        {
-            Ok(a)
-        }
-
-        (ty::Param(a_p), ty::Param(b_p)) if a_p.index == b_p.index => {
-            debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name");
-            Ok(a)
-        }
-
-        (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a),
-
-        (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) if a_def == b_def => {
-            let args = relation.relate_item_args(a_def.did(), a_args, b_args)?;
-            Ok(Ty::new_adt(tcx, a_def, args))
-        }
-
-        (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)),
-
-        (&ty::Dynamic(a_obj, a_region, a_repr), &ty::Dynamic(b_obj, b_region, b_repr))
-            if a_repr == b_repr =>
-        {
-            Ok(Ty::new_dynamic(
-                tcx,
-                relation.relate(a_obj, b_obj)?,
-                relation.relate(a_region, b_region)?,
-                a_repr,
-            ))
-        }
-
-        (&ty::Coroutine(a_id, a_args), &ty::Coroutine(b_id, b_args)) if a_id == b_id => {
-            // All Coroutine types with the same id represent
-            // the (anonymous) type of the same coroutine expression. So
-            // all of their regions should be equated.
-            let args = relate_args_invariantly(relation, a_args, b_args)?;
-            Ok(Ty::new_coroutine(tcx, a_id, args))
-        }
-
-        (&ty::CoroutineWitness(a_id, a_args), &ty::CoroutineWitness(b_id, b_args))
-            if a_id == b_id =>
-        {
-            // All CoroutineWitness types with the same id represent
-            // the (anonymous) type of the same coroutine expression. So
-            // all of their regions should be equated.
-            let args = relate_args_invariantly(relation, a_args, b_args)?;
-            Ok(Ty::new_coroutine_witness(tcx, a_id, args))
-        }
-
-        (&ty::Closure(a_id, a_args), &ty::Closure(b_id, b_args)) if a_id == b_id => {
-            // All Closure types with the same id represent
-            // the (anonymous) type of the same closure expression. So
-            // all of their regions should be equated.
-            let args = relate_args_invariantly(relation, a_args, b_args)?;
-            Ok(Ty::new_closure(tcx, a_id, args))
-        }
-
-        (&ty::CoroutineClosure(a_id, a_args), &ty::CoroutineClosure(b_id, b_args))
-            if a_id == b_id =>
-        {
-            let args = relate_args_invariantly(relation, a_args, b_args)?;
-            Ok(Ty::new_coroutine_closure(tcx, a_id, args))
-        }
-
-        (&ty::RawPtr(a_ty, a_mutbl), &ty::RawPtr(b_ty, b_mutbl)) => {
-            if a_mutbl != b_mutbl {
-                return Err(TypeError::Mutability);
-            }
-
-            let (variance, info) = match a_mutbl {
-                hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None),
-                hir::Mutability::Mut => {
-                    (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
-                }
-            };
-
-            let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?;
-
-            Ok(Ty::new_ptr(tcx, ty, a_mutbl))
-        }
-
-        (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => {
-            if a_mutbl != b_mutbl {
-                return Err(TypeError::Mutability);
-            }
-
-            let (variance, info) = match a_mutbl {
-                hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None),
-                hir::Mutability::Mut => {
-                    (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
-                }
-            };
-
-            let r = relation.relate(a_r, b_r)?;
-            let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?;
-
-            Ok(Ty::new_ref(tcx, r, ty, a_mutbl))
-        }
-
-        (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => {
-            let t = relation.relate(a_t, b_t)?;
-            match relation.relate(sz_a, sz_b) {
-                Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)),
-                Err(err) => {
-                    // Check whether the lengths are both concrete/known values,
-                    // but are unequal, for better diagnostics.
-                    let sz_a = sz_a.try_to_target_usize(tcx);
-                    let sz_b = sz_b.try_to_target_usize(tcx);
-
-                    match (sz_a, sz_b) {
-                        (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => {
-                            Err(TypeError::FixedArraySize(expected_found(sz_a_val, sz_b_val)))
-                        }
-                        _ => Err(err),
-                    }
-                }
-            }
-        }
-
-        (&ty::Slice(a_t), &ty::Slice(b_t)) => {
-            let t = relation.relate(a_t, b_t)?;
-            Ok(Ty::new_slice(tcx, t))
-        }
-
-        (&ty::Tuple(as_), &ty::Tuple(bs)) => {
-            if as_.len() == bs.len() {
-                Ok(Ty::new_tup_from_iter(
-                    tcx,
-                    iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)),
-                )?)
-            } else if !(as_.is_empty() || bs.is_empty()) {
-                Err(TypeError::TupleSize(expected_found(as_.len(), bs.len())))
-            } else {
-                Err(TypeError::Sorts(expected_found(a, b)))
-            }
-        }
-
-        (&ty::FnDef(a_def_id, a_args), &ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => {
-            let args = relation.relate_item_args(a_def_id, a_args, b_args)?;
-            Ok(Ty::new_fn_def(tcx, a_def_id, args))
-        }
-
-        (&ty::FnPtr(a_fty), &ty::FnPtr(b_fty)) => {
-            let fty = relation.relate(a_fty, b_fty)?;
-            Ok(Ty::new_fn_ptr(tcx, fty))
-        }
-
-        // Alias tend to mostly already be handled downstream due to normalization.
-        (&ty::Alias(a_kind, a_data), &ty::Alias(b_kind, b_data)) => {
-            let alias_ty = relation.relate(a_data, b_data)?;
-            assert_eq!(a_kind, b_kind);
-            Ok(Ty::new_alias(tcx, a_kind, alias_ty))
-        }
-
-        (&ty::Pat(a_ty, a_pat), &ty::Pat(b_ty, b_pat)) => {
-            let ty = relation.relate(a_ty, b_ty)?;
-            let pat = relation.relate(a_pat, b_pat)?;
-            Ok(Ty::new_pat(tcx, ty, pat))
-        }
-
-        _ => Err(TypeError::Sorts(expected_found(a, b))),
-    }
-}
-
-/// Relates `a` and `b` structurally, calling the relation for all nested values.
-/// Any semantic equality, e.g. of unevaluated consts, and inference variables have
-/// to be handled by the caller.
-///
-/// FIXME: This is not totally structual, which probably should be fixed.
-/// See the HACKs below.
-pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
-    relation: &mut R,
-    mut a: ty::Const<'tcx>,
-    mut b: ty::Const<'tcx>,
-) -> RelateResult<'tcx, ty::Const<'tcx>> {
-    debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
-    let tcx = relation.tcx();
-
-    if tcx.features().generic_const_exprs {
-        a = tcx.expand_abstract_consts(a);
-        b = tcx.expand_abstract_consts(b);
-    }
-
-    debug!("{}.structurally_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b);
-
-    // Currently, the values that can be unified are primitive types,
-    // and those that derive both `PartialEq` and `Eq`, corresponding
-    // to structural-match types.
-    let is_match = match (a.kind(), b.kind()) {
-        (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
-            // The caller should handle these cases!
-            bug!("var types encountered in structurally_relate_consts: {:?} {:?}", a, b)
-        }
-
-        (ty::ConstKind::Error(_), _) => return Ok(a),
-        (_, ty::ConstKind::Error(_)) => return Ok(b),
-
-        (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => {
-            debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name");
-            true
-        }
-        (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
-        (ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val,
-
-        // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
-        // and is the better alternative to waiting until `generic_const_exprs` can
-        // be stabilized.
-        (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => {
-            if cfg!(debug_assertions) {
-                let a_ty = tcx.type_of(au.def).instantiate(tcx, au.args);
-                let b_ty = tcx.type_of(bu.def).instantiate(tcx, bu.args);
-                assert_eq!(a_ty, b_ty);
-            }
-
-            let args = relation.relate_with_variance(
-                ty::Variance::Invariant,
-                ty::VarianceDiagInfo::default(),
-                au.args,
-                bu.args,
-            )?;
-            return Ok(ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst { def: au.def, args }));
-        }
-        (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => {
-            match (ae.kind, be.kind) {
-                (ty::ExprKind::Binop(a_binop), ty::ExprKind::Binop(b_binop))
-                    if a_binop == b_binop => {}
-                (ty::ExprKind::UnOp(a_unop), ty::ExprKind::UnOp(b_unop)) if a_unop == b_unop => {}
-                (ty::ExprKind::FunctionCall, ty::ExprKind::FunctionCall) => {}
-                (ty::ExprKind::Cast(a_kind), ty::ExprKind::Cast(b_kind)) if a_kind == b_kind => {}
-                _ => return Err(TypeError::ConstMismatch(expected_found(a, b))),
-            }
-
-            let args = relation.relate(ae.args(), be.args())?;
-            return Ok(ty::Const::new_expr(tcx, ty::Expr::new(ae.kind, args)));
-        }
-        _ => false,
-    };
-    if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(a, b))) }
-}
-
-impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: Self,
         b: Self,
@@ -703,44 +106,65 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
         b_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
         b_v.dedup();
         if a_v.len() != b_v.len() {
-            return Err(TypeError::ExistentialMismatch(expected_found(a, b)));
+            return Err(TypeError::ExistentialMismatch(ExpectedFound::new(true, a, b)));
         }
 
         let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| {
             match (ep_a.skip_binder(), ep_b.skip_binder()) {
-                (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(
+                (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => {
+                    Ok(ep_a.rebind(ty::ExistentialPredicate::Trait(
                         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))),
+                (
+                    ty::ExistentialPredicate::Projection(a),
+                    ty::ExistentialPredicate::Projection(b),
+                ) => Ok(ep_a.rebind(ty::ExistentialPredicate::Projection(
+                    relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
+                ))),
+                (
+                    ty::ExistentialPredicate::AutoTrait(a),
+                    ty::ExistentialPredicate::AutoTrait(b),
+                ) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))),
+                _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(true, a, b))),
             }
         });
         tcx.mk_poly_existential_predicates_from_iter(v)
     }
 }
 
-impl<'tcx> Relate<'tcx> for GenericArgsRef<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for hir::Safety {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
+        _relation: &mut R,
+        a: hir::Safety,
+        b: hir::Safety,
+    ) -> RelateResult<'tcx, hir::Safety> {
+        if a != b { Err(TypeError::SafetyMismatch(ExpectedFound::new(true, a, b))) } else { Ok(a) }
+    }
+}
+
+impl<'tcx> Relate<TyCtxt<'tcx>> for abi::Abi {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
+        _relation: &mut R,
+        a: abi::Abi,
+        b: abi::Abi,
+    ) -> RelateResult<'tcx, abi::Abi> {
+        if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(ExpectedFound::new(true, a, b))) }
+    }
+}
+
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::GenericArgsRef<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
-        a: GenericArgsRef<'tcx>,
-        b: GenericArgsRef<'tcx>,
-    ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
+        a: ty::GenericArgsRef<'tcx>,
+        b: ty::GenericArgsRef<'tcx>,
+    ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> {
         relate_args_invariantly(relation, a, b)
     }
 }
 
-impl<'tcx> Relate<'tcx> for ty::Region<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Region<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: ty::Region<'tcx>,
         b: ty::Region<'tcx>,
@@ -749,8 +173,8 @@ impl<'tcx> Relate<'tcx> for ty::Region<'tcx> {
     }
 }
 
-impl<'tcx> Relate<'tcx> for ty::Const<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Const<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: ty::Const<'tcx>,
         b: ty::Const<'tcx>,
@@ -759,85 +183,70 @@ impl<'tcx> Relate<'tcx> for ty::Const<'tcx> {
     }
 }
 
-impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for ty::Binder<'tcx, T> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::Binder<'tcx, T>,
-        b: ty::Binder<'tcx, T>,
-    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> {
-        relation.binders(a, b)
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Expr<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
+        relation: &mut R,
+        ae: ty::Expr<'tcx>,
+        be: ty::Expr<'tcx>,
+    ) -> RelateResult<'tcx, ty::Expr<'tcx>> {
+        // FIXME(generic_const_exprs): is it possible to relate two consts which are not identical
+        // exprs? Should we care about that?
+        // FIXME(generic_const_exprs): relating the `ty()`s is a little weird since it is supposed to
+        // ICE If they mismatch. Unfortunately `ConstKind::Expr` is a little special and can be thought
+        // of as being generic over the argument types, however this is implicit so these types don't get
+        // related when we relate the args of the item this const arg is for.
+        match (ae.kind, be.kind) {
+            (ty::ExprKind::Binop(a_binop), ty::ExprKind::Binop(b_binop)) if a_binop == b_binop => {}
+            (ty::ExprKind::UnOp(a_unop), ty::ExprKind::UnOp(b_unop)) if a_unop == b_unop => {}
+            (ty::ExprKind::FunctionCall, ty::ExprKind::FunctionCall) => {}
+            (ty::ExprKind::Cast(a_kind), ty::ExprKind::Cast(b_kind)) if a_kind == b_kind => {}
+            _ => return Err(TypeError::Mismatch),
+        }
+
+        let args = relation.relate(ae.args(), be.args())?;
+        Ok(ty::Expr::new(ae.kind, args))
     }
 }
 
-impl<'tcx> Relate<'tcx> for GenericArg<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::GenericArg<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
-        a: GenericArg<'tcx>,
-        b: GenericArg<'tcx>,
-    ) -> RelateResult<'tcx, GenericArg<'tcx>> {
+        a: ty::GenericArg<'tcx>,
+        b: ty::GenericArg<'tcx>,
+    ) -> RelateResult<'tcx, ty::GenericArg<'tcx>> {
         match (a.unpack(), b.unpack()) {
-            (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => {
+            (ty::GenericArgKind::Lifetime(a_lt), ty::GenericArgKind::Lifetime(b_lt)) => {
                 Ok(relation.relate(a_lt, b_lt)?.into())
             }
-            (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => {
+            (ty::GenericArgKind::Type(a_ty), ty::GenericArgKind::Type(b_ty)) => {
                 Ok(relation.relate(a_ty, b_ty)?.into())
             }
-            (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => {
+            (ty::GenericArgKind::Const(a_ct), ty::GenericArgKind::Const(b_ct)) => {
                 Ok(relation.relate(a_ct, b_ct)?.into())
             }
-            (GenericArgKind::Lifetime(unpacked), x) => {
+            (ty::GenericArgKind::Lifetime(unpacked), x) => {
                 bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
             }
-            (GenericArgKind::Type(unpacked), x) => {
+            (ty::GenericArgKind::Type(unpacked), x) => {
                 bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
             }
-            (GenericArgKind::Const(unpacked), x) => {
+            (ty::GenericArgKind::Const(unpacked), x) => {
                 bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
             }
         }
     }
 }
 
-impl<'tcx> Relate<'tcx> for ty::PredicatePolarity {
-    fn relate<R: TypeRelation<'tcx>>(
-        _relation: &mut R,
-        a: ty::PredicatePolarity,
-        b: ty::PredicatePolarity,
-    ) -> RelateResult<'tcx, ty::PredicatePolarity> {
-        if a != b { Err(TypeError::PolarityMismatch(expected_found(a, b))) } else { Ok(a) }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::TraitPredicate<'tcx>,
-        b: ty::TraitPredicate<'tcx>,
-    ) -> RelateResult<'tcx, ty::TraitPredicate<'tcx>> {
-        Ok(ty::TraitPredicate {
-            trait_ref: relation.relate(a.trait_ref, b.trait_ref)?,
-            polarity: relation.relate(a.polarity, b.polarity)?,
-        })
-    }
-}
-
-impl<'tcx> Relate<'tcx> for Term<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Term<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: Self,
         b: Self,
     ) -> RelateResult<'tcx, Self> {
         Ok(match (a.unpack(), b.unpack()) {
-            (TermKind::Ty(a), TermKind::Ty(b)) => relation.relate(a, b)?.into(),
-            (TermKind::Const(a), TermKind::Const(b)) => relation.relate(a, b)?.into(),
+            (ty::TermKind::Ty(a), ty::TermKind::Ty(b)) => relation.relate(a, b)?.into(),
+            (ty::TermKind::Const(a), ty::TermKind::Const(b)) => relation.relate(a, b)?.into(),
             _ => return Err(TypeError::Mismatch),
         })
     }
 }
-
-///////////////////////////////////////////////////////////////////////////
-// Error handling
-
-pub fn expected_found<T>(a: T, b: T) -> ExpectedFound<T> {
-    ExpectedFound::new(true, a, b)
-}
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index cf4854d1364..cc6b1d57f87 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -296,7 +296,6 @@ TrivialTypeTraversalImpls! {
     ::rustc_target::abi::FieldIdx,
     ::rustc_target::abi::VariantIdx,
     crate::middle::region::Scope,
-    crate::ty::FloatTy,
     ::rustc_ast::InlineAsmOptions,
     ::rustc_ast::InlineAsmTemplatePiece,
     ::rustc_ast::NodeId,
@@ -316,7 +315,7 @@ TrivialTypeTraversalImpls! {
     crate::traits::Reveal,
     crate::ty::adjustment::AutoBorrowMutability,
     crate::ty::AdtKind,
-    crate::ty::BoundConstness,
+    crate::ty::BoundRegion,
     // Including `BoundRegionKind` is a *bit* dubious, but direct
     // references to bound region appear in `ty::Error`, and aren't
     // really meant to be folded. In general, we can only fold a fully
@@ -324,16 +323,11 @@ TrivialTypeTraversalImpls! {
     crate::ty::BoundRegionKind,
     crate::ty::AssocItem,
     crate::ty::AssocKind,
-    crate::ty::AliasTyKind,
     crate::ty::Placeholder<crate::ty::BoundRegion>,
     crate::ty::Placeholder<crate::ty::BoundTy>,
     crate::ty::Placeholder<ty::BoundVar>,
     crate::ty::LateParamRegion,
-    crate::ty::InferTy,
-    crate::ty::IntVarValue,
     crate::ty::adjustment::PointerCoercion,
-    crate::ty::RegionVid,
-    crate::ty::Variance,
     ::rustc_span::Span,
     ::rustc_span::symbol::Ident,
     ::rustc_errors::ErrorGuaranteed,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index c83f6b0b9ec..ba9ed0d5b70 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -810,6 +810,31 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
         Ty::new_alias(interner, kind, alias_ty)
     }
 
+    fn new_error(interner: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
+        Ty::new_error(interner, guar)
+    }
+
+    fn new_adt(
+        interner: TyCtxt<'tcx>,
+        adt_def: ty::AdtDef<'tcx>,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> Self {
+        Ty::new_adt(interner, adt_def, args)
+    }
+
+    fn new_foreign(interner: TyCtxt<'tcx>, def_id: DefId) -> Self {
+        Ty::new_foreign(interner, def_id)
+    }
+
+    fn new_dynamic(
+        interner: TyCtxt<'tcx>,
+        preds: &'tcx List<ty::PolyExistentialPredicate<'tcx>>,
+        region: ty::Region<'tcx>,
+        kind: ty::DynKind,
+    ) -> Self {
+        Ty::new_dynamic(interner, preds, region, kind)
+    }
+
     fn new_coroutine(
         interner: TyCtxt<'tcx>,
         def_id: DefId,
@@ -818,6 +843,51 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
         Ty::new_coroutine(interner, def_id, args)
     }
 
+    fn new_coroutine_closure(
+        interner: TyCtxt<'tcx>,
+        def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> Self {
+        Ty::new_coroutine_closure(interner, def_id, args)
+    }
+
+    fn new_closure(interner: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Self {
+        Ty::new_closure(interner, def_id, args)
+    }
+
+    fn new_coroutine_witness(
+        interner: TyCtxt<'tcx>,
+        def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> Self {
+        Ty::new_coroutine_witness(interner, def_id, args)
+    }
+
+    fn new_ptr(interner: TyCtxt<'tcx>, ty: Self, mutbl: hir::Mutability) -> Self {
+        Ty::new_ptr(interner, ty, mutbl)
+    }
+
+    fn new_ref(
+        interner: TyCtxt<'tcx>,
+        region: ty::Region<'tcx>,
+        ty: Self,
+        mutbl: hir::Mutability,
+    ) -> Self {
+        Ty::new_ref(interner, region, ty, mutbl)
+    }
+
+    fn new_array_with_const_len(interner: TyCtxt<'tcx>, ty: Self, len: ty::Const<'tcx>) -> Self {
+        Ty::new_array_with_const_len(interner, ty, len)
+    }
+
+    fn new_slice(interner: TyCtxt<'tcx>, ty: Self) -> Self {
+        Ty::new_slice(interner, ty)
+    }
+
+    fn new_tup(interner: TyCtxt<'tcx>, tys: &[Ty<'tcx>]) -> Self {
+        Ty::new_tup(interner, tys)
+    }
+
     fn new_tup_from_iter<It, T>(interner: TyCtxt<'tcx>, iter: It) -> T::Output
     where
         It: Iterator<Item = T>,
@@ -844,6 +914,18 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
     ) -> Self {
         Ty::from_coroutine_closure_kind(interner, kind)
     }
+
+    fn new_fn_def(interner: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Self {
+        Ty::new_fn_def(interner, def_id, args)
+    }
+
+    fn new_fn_ptr(interner: TyCtxt<'tcx>, sig: ty::Binder<'tcx, ty::FnSig<'tcx>>) -> Self {
+        Ty::new_fn_ptr(interner, sig)
+    }
+
+    fn new_pat(interner: TyCtxt<'tcx>, ty: Self, pat: ty::Pattern<'tcx>) -> Self {
+        Ty::new_pat(interner, ty, pat)
+    }
 }
 
 /// Type utilities
@@ -1812,43 +1894,6 @@ impl<'tcx> rustc_type_ir::inherent::Tys<TyCtxt<'tcx>> for &'tcx ty::List<Ty<'tcx
     }
 }
 
-/// Extra information about why we ended up with a particular variance.
-/// This is only used to add more information to error messages, and
-/// has no effect on soundness. While choosing the 'wrong' `VarianceDiagInfo`
-/// may lead to confusing notes in error messages, it will never cause
-/// a miscompilation or unsoundness.
-///
-/// When in doubt, use `VarianceDiagInfo::default()`
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
-pub enum VarianceDiagInfo<'tcx> {
-    /// No additional information - this is the default.
-    /// We will not add any additional information to error messages.
-    #[default]
-    None,
-    /// We switched our variance because a generic argument occurs inside
-    /// the invariant generic argument of another type.
-    Invariant {
-        /// The generic type containing the generic parameter
-        /// that changes the variance (e.g. `*mut T`, `MyStruct<T>`)
-        ty: Ty<'tcx>,
-        /// The index of the generic parameter being used
-        /// (e.g. `0` for `*mut T`, `1` for `MyStruct<'CovariantParam, 'InvariantParam>`)
-        param_index: u32,
-    },
-}
-
-impl<'tcx> VarianceDiagInfo<'tcx> {
-    /// Mirrors `Variance::xform` - used to 'combine' the existing
-    /// and new `VarianceDiagInfo`s when our variance changes.
-    pub fn xform(self, other: VarianceDiagInfo<'tcx>) -> VarianceDiagInfo<'tcx> {
-        // For now, just use the first `VarianceDiagInfo::Invariant` that we see
-        match self {
-            VarianceDiagInfo::None => other,
-            VarianceDiagInfo::Invariant { .. } => self,
-        }
-    }
-}
-
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml
index 8bcc21d82f8..c30d21fd784 100644
--- a/compiler/rustc_next_trait_solver/Cargo.toml
+++ b/compiler/rustc_next_trait_solver/Cargo.toml
@@ -4,13 +4,16 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
-rustc_type_ir = { path = "../rustc_type_ir", default-features = false }
+# tidy-alphabetical-start
 derivative = "2.2.0"
+rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false }
+rustc_data_structures = { path = "../rustc_data_structures", optional = true }
 rustc_macros = { path = "../rustc_macros", optional = true }
-rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
 rustc_serialize = { path = "../rustc_serialize", optional = true }
-rustc_data_structures = { path = "../rustc_data_structures", optional = true }
-rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false }
+rustc_type_ir = { path = "../rustc_type_ir", default-features = false }
+rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
+tracing = "0.1"
+# tidy-alphabetical-end
 
 [features]
 default = ["nightly"]
diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs
index 92e05cc4901..5c00b6978d6 100644
--- a/compiler/rustc_next_trait_solver/src/resolve.rs
+++ b/compiler/rustc_next_trait_solver/src/resolve.rs
@@ -7,11 +7,11 @@ use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
 // EAGER RESOLUTION
 
 /// Resolves ty, region, and const vars to their inferred values or their root vars.
-pub struct EagerResolver<
-    'a,
+pub struct EagerResolver<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner>
+where
     Infcx: InferCtxtLike<Interner = I>,
-    I: Interner = <Infcx as InferCtxtLike>::Interner,
-> {
+    I: Interner,
+{
     infcx: &'a Infcx,
 }
 
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 04f855e4f55..f678d11213c 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -622,8 +622,6 @@ parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allo
 parse_out_of_range_hex_escape = out of range hex escape
     .label = must be a character in the range [\x00-\x7f]
 
-parse_outer_attr_ambiguous = ambiguous outer attributes
-
 parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them
 
 parse_outer_attribute_not_allowed_on_if_else = outer attributes are not allowed on `if` and `else` branches
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 3f08a830b0c..6c1fcbe06fc 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -496,15 +496,6 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse {
 }
 
 #[derive(Diagnostic)]
-#[diag(parse_outer_attr_ambiguous)]
-pub(crate) struct AmbiguousOuterAttributes {
-    #[primary_span]
-    pub span: Span,
-    #[subdiagnostic]
-    pub sugg: WrapInParentheses,
-}
-
-#[derive(Diagnostic)]
 #[diag(parse_missing_in_in_for_loop)]
 pub(crate) struct MissingInInForLoop {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 1b99bc015b6..285bac3f4b8 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -328,9 +328,7 @@ impl<'a> Parser<'a> {
                 this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed)
             })?;
 
-            self.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span);
-            let span = lhs_span.to(rhs.span);
-
+            let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
             lhs = match op {
                 AssocOp::Add
                 | AssocOp::Subtract
@@ -429,18 +427,6 @@ impl<'a> Parser<'a> {
         });
     }
 
-    fn error_ambiguous_outer_attrs(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) {
-        if let Some(attr) = lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer) {
-            self.dcx().emit_err(errors::AmbiguousOuterAttributes {
-                span: attr.span.to(rhs_span),
-                sugg: errors::WrapInParentheses::Expression {
-                    left: attr.span.shrink_to_lo(),
-                    right: lhs_span.shrink_to_hi(),
-                },
-            });
-        }
-    }
-
     /// Possibly translate the current token to an associative operator.
     /// The method does not advance the current token.
     ///
@@ -520,8 +506,7 @@ impl<'a> Parser<'a> {
             None
         };
         let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span);
-        self.error_ambiguous_outer_attrs(&lhs, lhs.span, rhs_span);
-        let span = lhs.span.to(rhs_span);
+        let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span);
         let limits =
             if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
         let range = self.mk_range(Some(lhs), rhs, limits);
@@ -739,8 +724,7 @@ impl<'a> Parser<'a> {
         expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind,
     ) -> PResult<'a, P<Expr>> {
         let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| {
-            this.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span);
-            this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs))
+            this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs))
         };
 
         // Save the state of the parser before parsing type normally, in case there is a
@@ -3858,6 +3842,16 @@ impl<'a> Parser<'a> {
         self.mk_expr(span, ExprKind::Err(guar))
     }
 
+    /// Create expression span ensuring the span of the parent node
+    /// is larger than the span of lhs and rhs, including the attributes.
+    fn mk_expr_sp(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) -> Span {
+        lhs.attrs
+            .iter()
+            .find(|a| a.style == AttrStyle::Outer)
+            .map_or(lhs_span, |a| a.span)
+            .to(rhs_span)
+    }
+
     fn collect_tokens_for_expr(
         &mut self,
         attrs: AttrWrapper,
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
index 9cb8cd836e6..6f63776bedc 100644
--- a/compiler/rustc_session/src/filesearch.rs
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -12,7 +12,7 @@ use tracing::debug;
 pub struct FileSearch<'a> {
     sysroot: &'a Path,
     triple: &'a str,
-    search_paths: &'a [SearchPath],
+    cli_search_paths: &'a [SearchPath],
     tlib_path: &'a SearchPath,
     kind: PathKind,
 }
@@ -20,7 +20,7 @@ pub struct FileSearch<'a> {
 impl<'a> FileSearch<'a> {
     pub fn search_paths(&self) -> impl Iterator<Item = &'a SearchPath> {
         let kind = self.kind;
-        self.search_paths
+        self.cli_search_paths
             .iter()
             .filter(move |sp| sp.kind.matches(kind))
             .chain(std::iter::once(self.tlib_path))
@@ -37,26 +37,26 @@ impl<'a> FileSearch<'a> {
     pub fn new(
         sysroot: &'a Path,
         triple: &'a str,
-        search_paths: &'a [SearchPath],
+        cli_search_paths: &'a [SearchPath],
         tlib_path: &'a SearchPath,
         kind: PathKind,
     ) -> FileSearch<'a> {
         debug!("using sysroot = {}, triple = {}", sysroot.display(), triple);
-        FileSearch { sysroot, triple, search_paths, tlib_path, kind }
+        FileSearch { sysroot, triple, cli_search_paths, tlib_path, kind }
     }
 }
 
 pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple);
-    PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")])
+    let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple);
+    sysroot.join(rustlib_path).join("lib")
 }
 
 /// Returns a path to the target's `bin` folder within its `rustlib` path in the sysroot. This is
 /// where binaries are usually installed, e.g. the self-contained linkers, lld-wrappers, LLVM tools,
 /// etc.
 pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple);
-    PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("bin")])
+    let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple);
+    sysroot.join(rustlib_path).join("bin")
 }
 
 #[cfg(unix)]
@@ -275,7 +275,7 @@ pub fn get_or_default_sysroot() -> Result<PathBuf, String> {
                 p.pop();
                 p.pop();
                 // Look for the target rustlib directory in the suspected sysroot.
-                let mut rustlib_path = rustc_target::target_rustlib_path(&p, "dummy");
+                let mut rustlib_path = rustc_target::relative_target_rustlib_path(&p, "dummy");
                 rustlib_path.pop(); // pop off the dummy target.
                 rustlib_path.exists().then_some(p)
             }
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index 46c83be9d95..ecc91ab9a31 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -41,17 +41,13 @@ const RUST_LIB_DIR: &str = "rustlib";
 ///
 /// For example: `target_sysroot_path("/usr", "x86_64-unknown-linux-gnu")` =>
 /// `"lib*/rustlib/x86_64-unknown-linux-gnu"`.
-pub fn target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    let libdir = find_libdir(sysroot);
-    PathBuf::from_iter([
-        Path::new(libdir.as_ref()),
-        Path::new(RUST_LIB_DIR),
-        Path::new(target_triple),
-    ])
+pub fn relative_target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
+    let libdir = find_relative_libdir(sysroot);
+    Path::new(libdir.as_ref()).join(RUST_LIB_DIR).join(target_triple)
 }
 
 /// The name of the directory rustc expects libraries to be located.
-fn find_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> {
+fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> {
     // FIXME: This is a quick hack to make the rustc binary able to locate
     // Rust libraries in Linux environments where libraries might be installed
     // to lib64/lib32. This would be more foolproof by basing the sysroot off
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 83ee63e2cf2..fe07d116726 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -3367,7 +3367,7 @@ impl Target {
 
                 // Additionally look in the sysroot under `lib/rustlib/<triple>/target.json`
                 // as a fallback.
-                let rustlib_path = crate::target_rustlib_path(sysroot, target_triple);
+                let rustlib_path = crate::relative_target_rustlib_path(sysroot, target_triple);
                 let p = PathBuf::from_iter([
                     Path::new(sysroot),
                     Path::new(&rustlib_path),
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
index f90e4711037..b522022c206 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -37,11 +37,11 @@ pub(super) mod canonical;
 mod probe;
 mod select;
 
-pub struct EvalCtxt<
-    'a,
+pub struct EvalCtxt<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner>
+where
     Infcx: InferCtxtLike<Interner = I>,
-    I: Interner = <Infcx as InferCtxtLike>::Interner,
-> {
+    I: Interner,
+{
     /// The inference context that backs (mostly) inference and placeholder terms
     /// instantiated while solving goals.
     ///
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 6a96a03e047..c7da85bd1cc 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -28,7 +28,7 @@ use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
 use rustc_macros::extension;
 use rustc_middle::hir::map;
 use rustc_middle::traits::IsConstable;
-use rustc_middle::ty::error::TypeError::{self, Sorts};
+use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::print::PrintPolyTraitRefExt;
 use rustc_middle::ty::{
     self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs,
@@ -3842,7 +3842,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     && let Some(failed_pred) = failed_pred.as_projection_clause()
                     && let Some(found) = failed_pred.skip_binder().term.as_type()
                 {
-                    type_diffs = vec![Sorts(ty::error::ExpectedFound {
+                    type_diffs = vec![TypeError::Sorts(ty::error::ExpectedFound {
                         expected: where_pred
                             .skip_binder()
                             .projection_term
@@ -3985,7 +3985,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 continue;
             };
             for diff in type_diffs {
-                let Sorts(expected_found) = diff else {
+                let TypeError::Sorts(expected_found) = diff else {
                     continue;
                 };
                 if tcx.is_diagnostic_item(sym::IteratorItem, *def_id)
@@ -4165,7 +4165,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     };
                     if primary_spans.is_empty()
                         || type_diffs.iter().any(|diff| {
-                            let Sorts(expected_found) = diff else {
+                            let TypeError::Sorts(expected_found) = diff else {
                                 return false;
                             };
                             self.can_eq(param_env, expected_found.found, ty)
@@ -4198,7 +4198,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         let assoc = with_forced_trimmed_paths!(self.tcx.def_path_str(assoc));
                         if !self.can_eq(param_env, ty, *prev_ty) {
                             if type_diffs.iter().any(|diff| {
-                                let Sorts(expected_found) = diff else {
+                                let TypeError::Sorts(expected_found) = diff else {
                                     return false;
                                 };
                                 self.can_eq(param_env, expected_found.found, ty)
@@ -4248,7 +4248,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let ocx = ObligationCtxt::new(self.infcx);
         let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
         for diff in type_diffs {
-            let Sorts(expected_found) = diff else {
+            let TypeError::Sorts(expected_found) = diff else {
                 continue;
             };
             let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else {
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index ce4fa5fa47c..4a935f4a64a 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -32,6 +32,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_infer::infer::relate::MatchAgainstFreshVars;
+use rustc_infer::infer::relate::TypeRelation;
 use rustc_infer::infer::BoundRegionConversionTime;
 use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType;
 use rustc_infer::infer::DefineOpaqueTypes;
@@ -40,10 +42,9 @@ use rustc_middle::bug;
 use rustc_middle::dep_graph::dep_kinds;
 use rustc_middle::dep_graph::DepNodeIndex;
 use rustc_middle::mir::interpret::ErrorHandled;
-use rustc_middle::ty::_match::MatchAgainstFreshVars;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
+use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
-use rustc_middle::ty::relate::TypeRelation;
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, PolyProjectionPredicate, Upcast};
 use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs
new file mode 100644
index 00000000000..27623ea9cac
--- /dev/null
+++ b/compiler/rustc_type_ir/src/error.rs
@@ -0,0 +1,106 @@
+use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
+
+use crate::solve::NoSolution;
+use crate::{self as ty, Interner};
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[derive(TypeFoldable_Generic, TypeVisitable_Generic)]
+pub struct ExpectedFound<T> {
+    pub expected: T,
+    pub found: T,
+}
+
+impl<T> ExpectedFound<T> {
+    pub fn new(a_is_expected: bool, a: T, b: T) -> Self {
+        if a_is_expected {
+            ExpectedFound { expected: a, found: b }
+        } else {
+            ExpectedFound { expected: b, found: a }
+        }
+    }
+}
+
+// Data structures used in type unification
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic)]
+#[rustc_pass_by_value]
+pub enum TypeError<I: Interner> {
+    Mismatch,
+    ConstnessMismatch(ExpectedFound<ty::BoundConstness>),
+    PolarityMismatch(ExpectedFound<ty::PredicatePolarity>),
+    SafetyMismatch(ExpectedFound<I::Safety>),
+    AbiMismatch(ExpectedFound<I::Abi>),
+    Mutability,
+    ArgumentMutability(usize),
+    TupleSize(ExpectedFound<usize>),
+    FixedArraySize(ExpectedFound<u64>),
+    ArgCount,
+
+    RegionsDoesNotOutlive(I::Region, I::Region),
+    RegionsInsufficientlyPolymorphic(I::BoundRegion, I::Region),
+    RegionsPlaceholderMismatch,
+
+    Sorts(ExpectedFound<I::Ty>),
+    ArgumentSorts(ExpectedFound<I::Ty>, usize),
+    Traits(ExpectedFound<I::DefId>),
+    VariadicMismatch(ExpectedFound<bool>),
+
+    /// Instantiating a type variable with the given type would have
+    /// created a cycle (because it appears somewhere within that
+    /// type).
+    CyclicTy(I::Ty),
+    CyclicConst(I::Const),
+    ProjectionMismatched(ExpectedFound<I::DefId>),
+    ExistentialMismatch(ExpectedFound<I::BoundExistentialPredicates>),
+    ConstMismatch(ExpectedFound<I::Const>),
+
+    IntrinsicCast,
+    /// Safe `#[target_feature]` functions are not assignable to safe function pointers.
+    TargetFeatureCast(I::DefId),
+}
+
+impl<I: Interner> TypeError<I> {
+    pub fn involves_regions(self) -> bool {
+        match self {
+            TypeError::RegionsDoesNotOutlive(_, _)
+            | TypeError::RegionsInsufficientlyPolymorphic(_, _)
+            | TypeError::RegionsPlaceholderMismatch => true,
+            _ => false,
+        }
+    }
+
+    pub fn must_include_note(self) -> bool {
+        use self::TypeError::*;
+        match self {
+            CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_)
+            | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
+            | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false,
+
+            Mutability
+            | ArgumentMutability(_)
+            | TupleSize(_)
+            | ArgCount
+            | RegionsDoesNotOutlive(..)
+            | RegionsInsufficientlyPolymorphic(..)
+            | RegionsPlaceholderMismatch
+            | Traits(_)
+            | ProjectionMismatched(_)
+            | ExistentialMismatch(_)
+            | ConstMismatch(_)
+            | IntrinsicCast => true,
+        }
+    }
+}
+
+impl<I: Interner> From<TypeError<I>> for NoSolution {
+    fn from(_: TypeError<I>) -> NoSolution {
+        NoSolution
+    }
+}
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 94874a6acfc..205a1e5f100 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -7,7 +7,10 @@ use std::fmt::Debug;
 use std::hash::Hash;
 use std::ops::Deref;
 
+use rustc_ast_ir::Mutability;
+
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
+use crate::relate::Relate;
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty, CollectAndApply, DebugWithInfcx, Interner, UpcastFrom};
 
@@ -21,6 +24,7 @@ pub trait Ty<I: Interner<Ty = Self>>:
     + IntoKind<Kind = ty::TyKind<I>>
     + TypeSuperVisitable<I>
     + TypeSuperFoldable<I>
+    + Relate<I>
     + Flags
 {
     fn new_bool(interner: I) -> Self;
@@ -35,8 +39,37 @@ pub trait Ty<I: Interner<Ty = Self>>:
 
     fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy<I>) -> Self;
 
+    fn new_error(interner: I, guar: I::ErrorGuaranteed) -> Self;
+
+    fn new_adt(interner: I, adt_def: I::AdtDef, args: I::GenericArgs) -> Self;
+
+    fn new_foreign(interner: I, def_id: I::DefId) -> Self;
+
+    fn new_dynamic(
+        interner: I,
+        preds: I::BoundExistentialPredicates,
+        region: I::Region,
+        kind: ty::DynKind,
+    ) -> Self;
+
     fn new_coroutine(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
 
+    fn new_coroutine_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_coroutine_witness(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_ptr(interner: I, ty: Self, mutbl: Mutability) -> Self;
+
+    fn new_ref(interner: I, region: I::Region, ty: Self, mutbl: Mutability) -> Self;
+
+    fn new_array_with_const_len(interner: I, ty: Self, len: I::Const) -> Self;
+
+    fn new_slice(interner: I, ty: Self) -> Self;
+
+    fn new_tup(interner: I, tys: &[I::Ty]) -> Self;
+
     fn new_tup_from_iter<It, T>(interner: I, iter: It) -> T::Output
     where
         It: Iterator<Item = T>,
@@ -49,6 +82,12 @@ pub trait Ty<I: Interner<Ty = Self>>:
     fn from_closure_kind(interner: I, kind: ty::ClosureKind) -> Self;
 
     fn from_coroutine_closure_kind(interner: I, kind: ty::ClosureKind) -> Self;
+
+    fn new_fn_def(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_fn_ptr(interner: I, sig: ty::Binder<I, ty::FnSig<I>>) -> Self;
+
+    fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self;
 }
 
 pub trait Tys<I: Interner<Tys = Self>>:
@@ -84,6 +123,7 @@ pub trait Region<I: Interner<Region = Self>>:
     + IntoKind<Kind = ty::RegionKind<I>>
     + Flags
     + TypeVisitable<I>
+    + Relate<I>
 {
     fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundRegion) -> Self;
 
@@ -102,8 +142,11 @@ pub trait Const<I: Interner<Const = Self>>:
     + IntoKind<Kind = ty::ConstKind<I>>
     + TypeSuperVisitable<I>
     + TypeSuperFoldable<I>
+    + Relate<I>
     + Flags
 {
+    fn try_to_target_usize(self, interner: I) -> Option<u64>;
+
     fn new_infer(interner: I, var: ty::InferConst) -> Self;
 
     fn new_var(interner: I, var: ty::ConstVid) -> Self;
@@ -113,6 +156,8 @@ pub trait Const<I: Interner<Const = Self>>:
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
     fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst<I>) -> Self;
+
+    fn new_expr(interner: I, expr: I::ExprConst) -> Self;
 }
 
 pub trait GenericsOf<I: Interner<GenericsOf = Self>> {
@@ -128,13 +173,14 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
     + Deref<Target: Deref<Target = [I::GenericArg]>>
     + Default
     + TypeFoldable<I>
+    + Relate<I>
 {
     fn type_at(self, i: usize) -> I::Ty;
 
     fn identity_for_item(interner: I, def_id: I::DefId) -> I::GenericArgs;
 
     fn extend_with_error(
-        tcx: I,
+        interner: I,
         def_id: I::DefId,
         original_args: &[I::GenericArg],
     ) -> I::GenericArgs;
@@ -193,3 +239,11 @@ pub trait BoundVarLike<I: Interner> {
 pub trait ParamLike {
     fn index(self) -> u32;
 }
+
+pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
+    fn def_id(self) -> I::DefId;
+}
+
+pub trait Features<I: Interner>: Copy {
+    fn generic_const_exprs(self) -> bool;
+}
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 6ebb434299b..ad1d2753b28 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -6,6 +6,7 @@ use std::ops::Deref;
 use crate::fold::TypeFoldable;
 use crate::inherent::*;
 use crate::ir_print::IrPrint;
+use crate::relate::Relate;
 use crate::solve::inspect::CanonicalGoalEvaluationStep;
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty, DebugWithInfcx};
@@ -25,8 +26,8 @@ pub trait Interner:
     + IrPrint<ty::CoercePredicate<Self>>
     + IrPrint<ty::FnSig<Self>>
 {
-    type DefId: Copy + Debug + Hash + Eq + TypeVisitable<Self>;
-    type AdtDef: Copy + Debug + Hash + Eq;
+    type DefId: Copy + Debug + Hash + Eq + TypeFoldable<Self>;
+    type AdtDef: AdtDef<Self>;
 
     type GenericArgs: GenericArgs<Self>;
     type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref<Target = [Self::GenericArg]>;
@@ -35,8 +36,15 @@ pub trait Interner:
         + Hash
         + Eq
         + IntoKind<Kind = ty::GenericArgKind<Self>>
-        + TypeVisitable<Self>;
-    type Term: Copy + Debug + Hash + Eq + IntoKind<Kind = ty::TermKind<Self>> + TypeVisitable<Self>;
+        + TypeVisitable<Self>
+        + Relate<Self>;
+    type Term: Copy
+        + Debug
+        + Hash
+        + Eq
+        + IntoKind<Kind = ty::TermKind<Self>>
+        + TypeFoldable<Self>
+        + Relate<Self>;
 
     type BoundVarKinds: Copy
         + Debug
@@ -66,11 +74,11 @@ pub trait Interner:
 
     // Things stored inside of tys
     type ErrorGuaranteed: Copy + Debug + Hash + Eq;
-    type BoundExistentialPredicates: Copy + DebugWithInfcx<Self> + Hash + Eq;
+    type BoundExistentialPredicates: Copy + DebugWithInfcx<Self> + Hash + Eq + Relate<Self>;
     type AllocId: Copy + Debug + Hash + Eq;
-    type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx<Self>;
-    type Safety: Safety<Self>;
-    type Abi: Abi<Self>;
+    type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx<Self> + Relate<Self>;
+    type Safety: Safety<Self> + TypeFoldable<Self> + Relate<Self>;
+    type Abi: Abi<Self> + TypeFoldable<Self> + Relate<Self>;
 
     // Kinds of consts
     type Const: Const<Self>;
@@ -78,7 +86,7 @@ pub trait Interner:
     type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
     type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
     type ValueConst: Copy + Debug + Hash + Eq;
-    type ExprConst: Copy + DebugWithInfcx<Self> + Hash + Eq;
+    type ExprConst: Copy + DebugWithInfcx<Self> + Hash + Eq + Relate<Self>;
 
     // Kinds of regions
     type Region: Region<Self>;
@@ -93,11 +101,16 @@ pub trait Interner:
     type Clause: Clause<Self>;
     type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags;
 
+    fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;
+
     fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
 
     type GenericsOf: GenericsOf<Self>;
     fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf;
 
+    type VariancesOf: Copy + Debug + Deref<Target = [ty::Variance]>;
+    fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf;
+
     // FIXME: Remove after uplifting `EarlyBinder`
     fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
 
@@ -112,7 +125,12 @@ pub trait Interner:
     ) -> (ty::TraitRef<Self>, Self::GenericArgsSlice);
 
     fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs;
-    fn mk_args_from_iter(self, args: impl Iterator<Item = Self::GenericArg>) -> Self::GenericArgs;
+
+    fn mk_args_from_iter<I, T>(self, args: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<Self::GenericArg, Self::GenericArgs>;
+
     fn check_and_mk_args(
         self,
         def_id: Self::DefId,
@@ -124,9 +142,17 @@ pub trait Interner:
         step: CanonicalGoalEvaluationStep<Self>,
     ) -> Self::CanonicalGoalEvaluationStepRef;
 
+    fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<Self::Ty, Self::Tys>;
+
     fn parent(self, def_id: Self::DefId) -> Self::DefId;
 
     fn recursion_limit(self) -> usize;
+
+    type Features: Features<Self>;
+    fn features(self) -> Self::Features;
 }
 
 /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 217c056d0ba..73716468930 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -21,18 +21,20 @@ use std::hash::Hash;
 #[cfg(not(feature = "nightly"))]
 use std::sync::Arc as Lrc;
 
+// These modules are `pub` since they are not glob-imported.
 #[macro_use]
 pub mod visit;
 #[cfg(feature = "nightly")]
 pub mod codec;
+pub mod error;
 pub mod fold;
 pub mod inherent;
 pub mod ir_print;
 pub mod lift;
+pub mod relate;
 pub mod solve;
-pub mod ty_info;
-pub mod ty_kind;
 
+// These modules are not `pub` since they are glob-imported.
 #[macro_use]
 mod macros;
 mod binder;
@@ -46,6 +48,8 @@ mod interner;
 mod predicate;
 mod predicate_kind;
 mod region_kind;
+mod ty_info;
+mod ty_kind;
 mod upcast;
 
 pub use binder::*;
diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs
index f2f7b165de5..aae5aeb5fb3 100644
--- a/compiler/rustc_type_ir/src/macros.rs
+++ b/compiler/rustc_type_ir/src/macros.rs
@@ -48,10 +48,22 @@ TrivialTypeTraversalImpls! {
     u32,
     u64,
     String,
-    crate::DebruijnIndex,
     crate::AliasRelationDirection,
+    crate::AliasTyKind,
+    crate::BoundConstness,
+    crate::DebruijnIndex,
+    crate::FloatTy,
+    crate::InferTy,
+    crate::IntVarValue,
+    crate::PredicatePolarity,
+    crate::RegionVid,
+    crate::solve::BuiltinImplSource,
+    crate::solve::Certainty,
+    crate::solve::GoalSource,
+    crate::solve::MaybeCause,
+    crate::solve::NoSolution,
     crate::UniverseIndex,
-    rustc_ast_ir::Mutability,
+    crate::Variance,
     rustc_ast_ir::Movability,
-    crate::PredicatePolarity,
+    rustc_ast_ir::Mutability,
 }
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index 9e0e52cfb4b..63a4c2e9d1f 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -793,3 +793,36 @@ pub struct CoercePredicate<I: Interner> {
     pub a: I::Ty,
     pub b: I::Ty,
 }
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))]
+pub enum BoundConstness {
+    /// `Type: Trait`
+    NotConst,
+    /// `Type: const Trait`
+    Const,
+    /// `Type: ~const Trait`
+    ///
+    /// Requires resolving to const only when we are in a const context.
+    ConstIfConst,
+}
+
+impl BoundConstness {
+    pub fn as_str(self) -> &'static str {
+        match self {
+            Self::NotConst => "",
+            Self::Const => "const",
+            Self::ConstIfConst => "~const",
+        }
+    }
+}
+
+impl fmt::Display for BoundConstness {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::NotConst => f.write_str("normal"),
+            Self::Const => f.write_str("const"),
+            Self::ConstIfConst => f.write_str("~const"),
+        }
+    }
+}
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
new file mode 100644
index 00000000000..cae1d13020d
--- /dev/null
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -0,0 +1,666 @@
+use std::iter;
+
+use rustc_ast_ir::Mutability;
+use rustc_type_ir::error::{ExpectedFound, TypeError};
+use rustc_type_ir::fold::TypeFoldable;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::{self as ty, Interner};
+use tracing::{debug, instrument};
+
+pub type RelateResult<I, T> = Result<T, TypeError<I>>;
+
+/// Extra information about why we ended up with a particular variance.
+/// This is only used to add more information to error messages, and
+/// has no effect on soundness. While choosing the 'wrong' `VarianceDiagInfo`
+/// may lead to confusing notes in error messages, it will never cause
+/// a miscompilation or unsoundness.
+///
+/// When in doubt, use `VarianceDiagInfo::default()`
+#[derive(derivative::Derivative)]
+#[derivative(
+    Copy(bound = ""),
+    Clone(bound = ""),
+    Debug(bound = ""),
+    Default(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = "")
+)]
+pub enum VarianceDiagInfo<I: Interner> {
+    /// No additional information - this is the default.
+    /// We will not add any additional information to error messages.
+    #[derivative(Default)]
+    None,
+    /// We switched our variance because a generic argument occurs inside
+    /// the invariant generic argument of another type.
+    Invariant {
+        /// The generic type containing the generic parameter
+        /// that changes the variance (e.g. `*mut T`, `MyStruct<T>`)
+        ty: I::Ty,
+        /// The index of the generic parameter being used
+        /// (e.g. `0` for `*mut T`, `1` for `MyStruct<'CovariantParam, 'InvariantParam>`)
+        param_index: u32,
+    },
+}
+
+impl<I: Interner> VarianceDiagInfo<I> {
+    /// Mirrors `Variance::xform` - used to 'combine' the existing
+    /// and new `VarianceDiagInfo`s when our variance changes.
+    pub fn xform(self, other: VarianceDiagInfo<I>) -> VarianceDiagInfo<I> {
+        // For now, just use the first `VarianceDiagInfo::Invariant` that we see
+        match self {
+            VarianceDiagInfo::None => other,
+            VarianceDiagInfo::Invariant { .. } => self,
+        }
+    }
+}
+
+pub trait TypeRelation<I: Interner>: Sized {
+    fn tcx(&self) -> I;
+
+    /// Returns a static string we can use for printouts.
+    fn tag(&self) -> &'static str;
+
+    /// Generic relation routine suitable for most anything.
+    fn relate<T: Relate<I>>(&mut self, a: T, b: T) -> RelateResult<I, T> {
+        Relate::relate(self, a, b)
+    }
+
+    /// Relate the two args for the given item. The default
+    /// is to look up the variance for the item and proceed
+    /// accordingly.
+    fn relate_item_args(
+        &mut self,
+        item_def_id: I::DefId,
+        a_arg: I::GenericArgs,
+        b_arg: I::GenericArgs,
+    ) -> RelateResult<I, I::GenericArgs> {
+        debug!(
+            "relate_item_args(item_def_id={:?}, a_arg={:?}, b_arg={:?})",
+            item_def_id, a_arg, b_arg
+        );
+
+        let tcx = self.tcx();
+        let opt_variances = tcx.variances_of(item_def_id);
+        relate_args_with_variances(self, item_def_id, &opt_variances, a_arg, b_arg, true)
+    }
+
+    /// Switch variance for the purpose of relating `a` and `b`.
+    fn relate_with_variance<T: Relate<I>>(
+        &mut self,
+        variance: ty::Variance,
+        info: VarianceDiagInfo<I>,
+        a: T,
+        b: T,
+    ) -> RelateResult<I, T>;
+
+    // Overridable relations. You shouldn't typically call these
+    // directly, instead call `relate()`, which in turn calls
+    // these. This is both more uniform but also allows us to add
+    // additional hooks for other types in the future if needed
+    // without making older code, which called `relate`, obsolete.
+
+    fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult<I, I::Ty>;
+
+    fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult<I, I::Region>;
+
+    fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult<I, I::Const>;
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<I, T>,
+        b: ty::Binder<I, T>,
+    ) -> RelateResult<I, ty::Binder<I, T>>
+    where
+        T: Relate<I>;
+}
+
+pub trait Relate<I: Interner>: TypeFoldable<I> + PartialEq + Copy {
+    fn relate<R: TypeRelation<I>>(relation: &mut R, a: Self, b: Self) -> RelateResult<I, Self>;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Relate impls
+
+#[inline]
+pub fn relate_args_invariantly<I: Interner, R: TypeRelation<I>>(
+    relation: &mut R,
+    a_arg: I::GenericArgs,
+    b_arg: I::GenericArgs,
+) -> RelateResult<I, I::GenericArgs> {
+    relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| {
+        relation.relate_with_variance(ty::Variance::Invariant, VarianceDiagInfo::default(), a, b)
+    }))
+}
+
+pub fn relate_args_with_variances<I: Interner, R: TypeRelation<I>>(
+    relation: &mut R,
+    ty_def_id: I::DefId,
+    variances: &[ty::Variance],
+    a_arg: I::GenericArgs,
+    b_arg: I::GenericArgs,
+    fetch_ty_for_diag: bool,
+) -> RelateResult<I, I::GenericArgs> {
+    let tcx = relation.tcx();
+
+    let mut cached_ty = None;
+    let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| {
+        let variance = variances[i];
+        let variance_info = if variance == ty::Variance::Invariant && fetch_ty_for_diag {
+            let ty =
+                *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, &a_arg));
+            VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() }
+        } else {
+            VarianceDiagInfo::default()
+        };
+        relation.relate_with_variance(variance, variance_info, a, b)
+    });
+
+    tcx.mk_args_from_iter(params)
+}
+
+impl<I: Interner> Relate<I> for ty::FnSig<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::FnSig<I>,
+        b: ty::FnSig<I>,
+    ) -> RelateResult<I, ty::FnSig<I>> {
+        let tcx = relation.tcx();
+
+        if a.c_variadic != b.c_variadic {
+            return Err(TypeError::VariadicMismatch({
+                let a = a.c_variadic;
+                let b = b.c_variadic;
+                ExpectedFound::new(true, a, b)
+            }));
+        }
+        let safety = relation.relate(a.safety, b.safety)?;
+        let abi = relation.relate(a.abi, b.abi)?;
+
+        let a_inputs = a.inputs();
+        let b_inputs = b.inputs();
+
+        if a_inputs.len() != b_inputs.len() {
+            return Err(TypeError::ArgCount);
+        }
+
+        let inputs_and_output = iter::zip(a_inputs.iter(), b_inputs.iter())
+            .map(|(&a, &b)| ((a, b), false))
+            .chain(iter::once(((a.output(), b.output()), true)))
+            .map(|((a, b), is_output)| {
+                if is_output {
+                    relation.relate(a, b)
+                } else {
+                    relation.relate_with_variance(
+                        ty::Variance::Contravariant,
+                        VarianceDiagInfo::default(),
+                        a,
+                        b,
+                    )
+                }
+            })
+            .enumerate()
+            .map(|(i, r)| match r {
+                Err(TypeError::Sorts(exp_found) | TypeError::ArgumentSorts(exp_found, _)) => {
+                    Err(TypeError::ArgumentSorts(exp_found, i))
+                }
+                Err(TypeError::Mutability | TypeError::ArgumentMutability(_)) => {
+                    Err(TypeError::ArgumentMutability(i))
+                }
+                r => r,
+            });
+        Ok(ty::FnSig {
+            inputs_and_output: tcx.mk_type_list_from_iter(inputs_and_output)?,
+            c_variadic: a.c_variadic,
+            safety,
+            abi,
+        })
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::BoundConstness {
+    fn relate<R: TypeRelation<I>>(
+        _relation: &mut R,
+        a: ty::BoundConstness,
+        b: ty::BoundConstness,
+    ) -> RelateResult<I, ty::BoundConstness> {
+        if a != b {
+            Err(TypeError::ConstnessMismatch(ExpectedFound::new(true, a, b)))
+        } else {
+            Ok(a)
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::AliasTy<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::AliasTy<I>,
+        b: ty::AliasTy<I>,
+    ) -> RelateResult<I, ty::AliasTy<I>> {
+        if a.def_id != b.def_id {
+            Err(TypeError::ProjectionMismatched({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let args = match a.kind(relation.tcx()) {
+                ty::Opaque => relate_args_with_variances(
+                    relation,
+                    a.def_id,
+                    &relation.tcx().variances_of(a.def_id),
+                    a.args,
+                    b.args,
+                    false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
+                )?,
+                ty::Projection | ty::Weak | ty::Inherent => {
+                    relate_args_invariantly(relation, a.args, b.args)?
+                }
+            };
+            Ok(ty::AliasTy::new(relation.tcx(), a.def_id, args))
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::AliasTerm<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::AliasTerm<I>,
+        b: ty::AliasTerm<I>,
+    ) -> RelateResult<I, ty::AliasTerm<I>> {
+        if a.def_id != b.def_id {
+            Err(TypeError::ProjectionMismatched({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let args = match a.kind(relation.tcx()) {
+                ty::AliasTermKind::OpaqueTy => relate_args_with_variances(
+                    relation,
+                    a.def_id,
+                    &relation.tcx().variances_of(a.def_id),
+                    a.args,
+                    b.args,
+                    false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
+                )?,
+                ty::AliasTermKind::ProjectionTy
+                | ty::AliasTermKind::WeakTy
+                | ty::AliasTermKind::InherentTy
+                | ty::AliasTermKind::UnevaluatedConst
+                | ty::AliasTermKind::ProjectionConst => {
+                    relate_args_invariantly(relation, a.args, b.args)?
+                }
+            };
+            Ok(ty::AliasTerm::new(relation.tcx(), a.def_id, args))
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::ExistentialProjection<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::ExistentialProjection<I>,
+        b: ty::ExistentialProjection<I>,
+    ) -> RelateResult<I, ty::ExistentialProjection<I>> {
+        if a.def_id != b.def_id {
+            Err(TypeError::ProjectionMismatched({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let term = relation.relate_with_variance(
+                ty::Variance::Invariant,
+                VarianceDiagInfo::default(),
+                a.term,
+                b.term,
+            )?;
+            let args = relation.relate_with_variance(
+                ty::Variance::Invariant,
+                VarianceDiagInfo::default(),
+                a.args,
+                b.args,
+            )?;
+            Ok(ty::ExistentialProjection { def_id: a.def_id, args, term })
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::TraitRef<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::TraitRef<I>,
+        b: ty::TraitRef<I>,
+    ) -> RelateResult<I, ty::TraitRef<I>> {
+        // Different traits cannot be related.
+        if a.def_id != b.def_id {
+            Err(TypeError::Traits({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let args = relate_args_invariantly(relation, a.args, b.args)?;
+            Ok(ty::TraitRef::new(relation.tcx(), a.def_id, args))
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::ExistentialTraitRef<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::ExistentialTraitRef<I>,
+        b: ty::ExistentialTraitRef<I>,
+    ) -> RelateResult<I, ty::ExistentialTraitRef<I>> {
+        // Different traits cannot be related.
+        if a.def_id != b.def_id {
+            Err(TypeError::Traits({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let args = relate_args_invariantly(relation, a.args, b.args)?;
+            Ok(ty::ExistentialTraitRef { def_id: a.def_id, args })
+        }
+    }
+}
+
+/// Relates `a` and `b` structurally, calling the relation for all nested values.
+/// Any semantic equality, e.g. of projections, and inference variables have to be
+/// handled by the caller.
+#[instrument(level = "trace", skip(relation), ret)]
+pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>(
+    relation: &mut R,
+    a: I::Ty,
+    b: I::Ty,
+) -> RelateResult<I, I::Ty> {
+    let tcx = relation.tcx();
+    match (a.kind(), b.kind()) {
+        (ty::Infer(_), _) | (_, ty::Infer(_)) => {
+            // The caller should handle these cases!
+            panic!("var types encountered in structurally_relate_tys")
+        }
+
+        (ty::Bound(..), _) | (_, ty::Bound(..)) => {
+            panic!("bound types encountered in structurally_relate_tys")
+        }
+
+        (ty::Error(guar), _) | (_, ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)),
+
+        (ty::Never, _)
+        | (ty::Char, _)
+        | (ty::Bool, _)
+        | (ty::Int(_), _)
+        | (ty::Uint(_), _)
+        | (ty::Float(_), _)
+        | (ty::Str, _)
+            if a == b =>
+        {
+            Ok(a)
+        }
+
+        (ty::Param(a_p), ty::Param(b_p)) if a_p.index() == b_p.index() => {
+            // FIXME: Put this back
+            //debug_assert_eq!(a_p.name(), b_p.name(), "param types with same index differ in name");
+            Ok(a)
+        }
+
+        (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a),
+
+        (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args)) if a_def == b_def => {
+            let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?;
+            Ok(Ty::new_adt(tcx, a_def, args))
+        }
+
+        (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)),
+
+        (ty::Dynamic(a_obj, a_region, a_repr), ty::Dynamic(b_obj, b_region, b_repr))
+            if a_repr == b_repr =>
+        {
+            Ok(Ty::new_dynamic(
+                tcx,
+                relation.relate(a_obj, b_obj)?,
+                relation.relate(a_region, b_region)?,
+                a_repr,
+            ))
+        }
+
+        (ty::Coroutine(a_id, a_args), ty::Coroutine(b_id, b_args)) if a_id == b_id => {
+            // All Coroutine types with the same id represent
+            // the (anonymous) type of the same coroutine expression. So
+            // all of their regions should be equated.
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
+            Ok(Ty::new_coroutine(tcx, a_id, args))
+        }
+
+        (ty::CoroutineWitness(a_id, a_args), ty::CoroutineWitness(b_id, b_args))
+            if a_id == b_id =>
+        {
+            // All CoroutineWitness types with the same id represent
+            // the (anonymous) type of the same coroutine expression. So
+            // all of their regions should be equated.
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
+            Ok(Ty::new_coroutine_witness(tcx, a_id, args))
+        }
+
+        (ty::Closure(a_id, a_args), ty::Closure(b_id, b_args)) if a_id == b_id => {
+            // All Closure types with the same id represent
+            // the (anonymous) type of the same closure expression. So
+            // all of their regions should be equated.
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
+            Ok(Ty::new_closure(tcx, a_id, args))
+        }
+
+        (ty::CoroutineClosure(a_id, a_args), ty::CoroutineClosure(b_id, b_args))
+            if a_id == b_id =>
+        {
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
+            Ok(Ty::new_coroutine_closure(tcx, a_id, args))
+        }
+
+        (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => {
+            if a_mutbl != b_mutbl {
+                return Err(TypeError::Mutability);
+            }
+
+            let (variance, info) = match a_mutbl {
+                Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None),
+                Mutability::Mut => {
+                    (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
+                }
+            };
+
+            let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?;
+
+            Ok(Ty::new_ptr(tcx, ty, a_mutbl))
+        }
+
+        (ty::Ref(a_r, a_ty, a_mutbl), ty::Ref(b_r, b_ty, b_mutbl)) => {
+            if a_mutbl != b_mutbl {
+                return Err(TypeError::Mutability);
+            }
+
+            let (variance, info) = match a_mutbl {
+                Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None),
+                Mutability::Mut => {
+                    (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
+                }
+            };
+
+            let r = relation.relate(a_r, b_r)?;
+            let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?;
+
+            Ok(Ty::new_ref(tcx, r, ty, a_mutbl))
+        }
+
+        (ty::Array(a_t, sz_a), ty::Array(b_t, sz_b)) => {
+            let t = relation.relate(a_t, b_t)?;
+            match relation.relate(sz_a, sz_b) {
+                Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)),
+                Err(err) => {
+                    // Check whether the lengths are both concrete/known values,
+                    // but are unequal, for better diagnostics.
+                    let sz_a = sz_a.try_to_target_usize(tcx);
+                    let sz_b = sz_b.try_to_target_usize(tcx);
+
+                    match (sz_a, sz_b) {
+                        (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => Err(
+                            TypeError::FixedArraySize(ExpectedFound::new(true, sz_a_val, sz_b_val)),
+                        ),
+                        _ => Err(err),
+                    }
+                }
+            }
+        }
+
+        (ty::Slice(a_t), ty::Slice(b_t)) => {
+            let t = relation.relate(a_t, b_t)?;
+            Ok(Ty::new_slice(tcx, t))
+        }
+
+        (ty::Tuple(as_), ty::Tuple(bs)) => {
+            if as_.len() == bs.len() {
+                Ok(Ty::new_tup_from_iter(
+                    tcx,
+                    iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)),
+                )?)
+            } else if !(as_.is_empty() || bs.is_empty()) {
+                Err(TypeError::TupleSize(ExpectedFound::new(true, as_.len(), bs.len())))
+            } else {
+                Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
+            }
+        }
+
+        (ty::FnDef(a_def_id, a_args), ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => {
+            let args = relation.relate_item_args(a_def_id, a_args, b_args)?;
+            Ok(Ty::new_fn_def(tcx, a_def_id, args))
+        }
+
+        (ty::FnPtr(a_fty), ty::FnPtr(b_fty)) => {
+            let fty = relation.relate(a_fty, b_fty)?;
+            Ok(Ty::new_fn_ptr(tcx, fty))
+        }
+
+        // Alias tend to mostly already be handled downstream due to normalization.
+        (ty::Alias(a_kind, a_data), ty::Alias(b_kind, b_data)) => {
+            let alias_ty = relation.relate(a_data, b_data)?;
+            assert_eq!(a_kind, b_kind);
+            Ok(Ty::new_alias(tcx, a_kind, alias_ty))
+        }
+
+        (ty::Pat(a_ty, a_pat), ty::Pat(b_ty, b_pat)) => {
+            let ty = relation.relate(a_ty, b_ty)?;
+            let pat = relation.relate(a_pat, b_pat)?;
+            Ok(Ty::new_pat(tcx, ty, pat))
+        }
+
+        _ => Err(TypeError::Sorts(ExpectedFound::new(true, a, b))),
+    }
+}
+
+/// Relates `a` and `b` structurally, calling the relation for all nested values.
+/// Any semantic equality, e.g. of unevaluated consts, and inference variables have
+/// to be handled by the caller.
+///
+/// FIXME: This is not totally structual, which probably should be fixed.
+/// See the HACKs below.
+pub fn structurally_relate_consts<I: Interner, R: TypeRelation<I>>(
+    relation: &mut R,
+    mut a: I::Const,
+    mut b: I::Const,
+) -> RelateResult<I, I::Const> {
+    debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
+    let tcx = relation.tcx();
+
+    if tcx.features().generic_const_exprs() {
+        a = tcx.expand_abstract_consts(a);
+        b = tcx.expand_abstract_consts(b);
+    }
+
+    debug!("{}.structurally_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b);
+
+    // Currently, the values that can be unified are primitive types,
+    // and those that derive both `PartialEq` and `Eq`, corresponding
+    // to structural-match types.
+    let is_match = match (a.kind(), b.kind()) {
+        (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
+            // The caller should handle these cases!
+            panic!("var types encountered in structurally_relate_consts: {:?} {:?}", a, b)
+        }
+
+        (ty::ConstKind::Error(_), _) => return Ok(a),
+        (_, ty::ConstKind::Error(_)) => return Ok(b),
+
+        (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index() == b_p.index() => {
+            // FIXME: Put this back
+            // debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name");
+            true
+        }
+        (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
+        (ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val,
+
+        // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
+        // and is the better alternative to waiting until `generic_const_exprs` can
+        // be stabilized.
+        (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => {
+            if cfg!(debug_assertions) {
+                let a_ty = tcx.type_of(au.def).instantiate(tcx, &au.args);
+                let b_ty = tcx.type_of(bu.def).instantiate(tcx, &bu.args);
+                assert_eq!(a_ty, b_ty);
+            }
+
+            let args = relation.relate_with_variance(
+                ty::Variance::Invariant,
+                VarianceDiagInfo::default(),
+                au.args,
+                bu.args,
+            )?;
+            return Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst { def: au.def, args }));
+        }
+        (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => {
+            let expr = relation.relate(ae, be)?;
+            return Ok(Const::new_expr(tcx, expr));
+        }
+        _ => false,
+    };
+    if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(ExpectedFound::new(true, a, b))) }
+}
+
+impl<I: Interner, T: Relate<I>> Relate<I> for ty::Binder<I, T> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::Binder<I, T>,
+        b: ty::Binder<I, T>,
+    ) -> RelateResult<I, ty::Binder<I, T>> {
+        relation.binders(a, b)
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::PredicatePolarity {
+    fn relate<R: TypeRelation<I>>(
+        _relation: &mut R,
+        a: ty::PredicatePolarity,
+        b: ty::PredicatePolarity,
+    ) -> RelateResult<I, ty::PredicatePolarity> {
+        if a != b {
+            Err(TypeError::PolarityMismatch(ExpectedFound::new(true, a, b)))
+        } else {
+            Ok(a)
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::TraitPredicate<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::TraitPredicate<I>,
+        b: ty::TraitPredicate<I>,
+    ) -> RelateResult<I, ty::TraitPredicate<I>> {
+        Ok(ty::TraitPredicate {
+            trait_ref: relation.relate(a.trait_ref, b.trait_ref)?,
+            polarity: relation.relate(a.polarity, b.polarity)?,
+        })
+    }
+}
diff --git a/compiler/rustc_type_ir/src/solve.rs b/compiler/rustc_type_ir/src/solve.rs
index 3c24e851d7b..45125fe6191 100644
--- a/compiler/rustc_type_ir/src/solve.rs
+++ b/compiler/rustc_type_ir/src/solve.rs
@@ -20,7 +20,6 @@ pub type CanonicalResponse<I> = Canonical<I, Response<I>>;
 pub type QueryResult<I> = Result<CanonicalResponse<I>, NoSolution>;
 
 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-#[derive(TypeFoldable_Generic, TypeVisitable_Generic)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
 pub struct NoSolution;
 
@@ -60,7 +59,7 @@ impl<I: Interner, P> Goal<I, P> {
 ///
 /// This is necessary as we treat nested goals different depending on
 /// their source.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeVisitable_Generic, TypeFoldable_Generic)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
 pub enum GoalSource {
     Misc,
@@ -170,7 +169,6 @@ pub enum CandidateSource<I: Interner> {
 }
 
 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
-#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))]
 pub enum BuiltinImplSource {
     /// Some builtin impl we don't need to differentiate. This should be used
@@ -214,7 +212,6 @@ pub struct Response<I: Interner> {
 }
 
 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
-#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
 pub enum Certainty {
     Yes,
@@ -252,7 +249,6 @@ impl Certainty {
 
 /// Why we failed to evaluate a goal.
 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
-#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
 pub enum MaybeCause {
     /// We failed due to ambiguity. This ambiguity can either
diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs
index ab2158394bf..3a5a5af8bf5 100644
--- a/library/core/src/fmt/num.rs
+++ b/library/core/src/fmt/num.rs
@@ -212,6 +212,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\
 
 macro_rules! impl_Display {
     ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
+        #[cfg(not(feature = "optimize_for_size"))]
         fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             // 2^128 is about 3*10^38, so 39 gives an extra byte of space
             let mut buf = [MaybeUninit::<u8>::uninit(); 39];
@@ -277,6 +278,38 @@ macro_rules! impl_Display {
             f.pad_integral(is_nonnegative, "", buf_slice)
         }
 
+        #[cfg(feature = "optimize_for_size")]
+        fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            // 2^128 is about 3*10^38, so 39 gives an extra byte of space
+            let mut buf = [MaybeUninit::<u8>::uninit(); 39];
+            let mut curr = buf.len();
+            let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
+
+            // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
+            // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
+            // each step this is kept the same as `n` is divided. Since `n` is always
+            // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
+            // is safe to access.
+            unsafe {
+                loop {
+                    curr -= 1;
+                    buf_ptr.add(curr).write((n % 10) as u8 + b'0');
+                    n /= 10;
+
+                    if n == 0 {
+                        break;
+                    }
+                }
+            }
+
+            // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
+            let buf_slice = unsafe {
+                str::from_utf8_unchecked(
+                    slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
+            };
+            f.pad_integral(is_nonnegative, "", buf_slice)
+        }
+
         $(#[stable(feature = "rust1", since = "1.0.0")]
         impl fmt::Display for $t {
             #[allow(unused_comparisons)]
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
index 84dac431169..05d5b3d10ea 100644
--- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
@@ -16,7 +16,7 @@ fn foo(
 
 fn skip_on_statements() {
     #[rustfmt::skip]
-    { 5+3; }
+    5+3;
 }
 
 #[rustfmt::skip]
@@ -33,11 +33,11 @@ mod foo {
 #[clippy::msrv = "1.29"]
 fn msrv_1_29() {
     #[cfg_attr(rustfmt, rustfmt::skip)]
-    { 1+29; }
+    1+29;
 }
 
 #[clippy::msrv = "1.30"]
 fn msrv_1_30() {
     #[rustfmt::skip]
-    { 1+30; }
+    1+30;
 }
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
index 4ab5c70e13b..bc29e20210e 100644
--- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
@@ -16,7 +16,7 @@ fn foo(
 
 fn skip_on_statements() {
     #[cfg_attr(rustfmt, rustfmt::skip)]
-    { 5+3; }
+    5+3;
 }
 
 #[cfg_attr(rustfmt, rustfmt_skip)]
@@ -33,11 +33,11 @@ mod foo {
 #[clippy::msrv = "1.29"]
 fn msrv_1_29() {
     #[cfg_attr(rustfmt, rustfmt::skip)]
-    { 1+29; }
+    1+29;
 }
 
 #[clippy::msrv = "1.30"]
 fn msrv_1_30() {
     #[cfg_attr(rustfmt, rustfmt::skip)]
-    { 1+30; }
+    1+30;
 }
diff --git a/src/tools/rustfmt/tests/source/attrib.rs b/src/tools/rustfmt/tests/source/attrib.rs
index fc13cd02b03..d45fba55224 100644
--- a/src/tools/rustfmt/tests/source/attrib.rs
+++ b/src/tools/rustfmt/tests/source/attrib.rs
@@ -214,8 +214,8 @@ type Os = NoSource;
 // #3313
 fn stmt_expr_attributes() {
     let foo ;
-    (#[must_use]
-   foo) = false ;
+    #[must_use]
+   foo = false ;
 }
 
 // #3509
diff --git a/src/tools/rustfmt/tests/target/attrib.rs b/src/tools/rustfmt/tests/target/attrib.rs
index 7b3309676de..7e61f68d76a 100644
--- a/src/tools/rustfmt/tests/target/attrib.rs
+++ b/src/tools/rustfmt/tests/target/attrib.rs
@@ -248,8 +248,8 @@ type Os = NoSource;
 // #3313
 fn stmt_expr_attributes() {
     let foo;
-    (#[must_use]
-    foo) = false;
+    #[must_use]
+    foo = false;
 }
 
 // #3509
diff --git a/tests/pretty/ast-stmt-expr-attr.rs b/tests/pretty/ast-stmt-expr-attr.rs
index 899f7173f70..fd7272a1b1f 100644
--- a/tests/pretty/ast-stmt-expr-attr.rs
+++ b/tests/pretty/ast-stmt-expr-attr.rs
@@ -13,17 +13,17 @@ fn syntax() {
     let _ = #[attr] ();
     let _ = #[attr] (#[attr] 0,);
     let _ = #[attr] (#[attr] 0, 0);
-    let _ = (#[attr] 0) + #[attr] 0;
-    let _ = (#[attr] 0) / #[attr] 0;
-    let _ = (#[attr] 0) & #[attr] 0;
-    let _ = (#[attr] 0) % #[attr] 0;
+    let _ = #[attr] 0 + #[attr] 0;
+    let _ = #[attr] 0 / #[attr] 0;
+    let _ = #[attr] 0 & #[attr] 0;
+    let _ = #[attr] 0 % #[attr] 0;
     let _ = #[attr] (0 + 0);
     let _ = #[attr] !0;
     let _ = #[attr] -0;
     let _ = #[attr] false;
     let _ = #[attr] 0;
     let _ = #[attr] 'c';
-    let _ = (#[attr] x) as Y;
+    let _ = #[attr] x as Y;
     let _ = #[attr] (x as Y);
     let _ =
         #[attr] while true {
@@ -88,9 +88,9 @@ fn syntax() {
             let _ = ();
             foo
         };
-    let _ = (#[attr] x) = y;
+    let _ = #[attr] x = y;
     let _ = #[attr] (x = y);
-    let _ = (#[attr] x) += y;
+    let _ = #[attr] x += y;
     let _ = #[attr] (x += y);
     let _ = #[attr] foo.bar;
     let _ = (#[attr] foo).bar;
@@ -98,8 +98,8 @@ fn syntax() {
     let _ = (#[attr] foo).0;
     let _ = #[attr] foo[bar];
     let _ = (#[attr] foo)[bar];
-    let _ = (#[attr] 0)..#[attr] 0;
-    let _ = (#[attr] 0)..;
+    let _ = #[attr] 0..#[attr] 0;
+    let _ = #[attr] 0..;
     let _ = #[attr] (0..0);
     let _ = #[attr] (0..);
     let _ = #[attr] (..0);
diff --git a/tests/pretty/stmt_expr_attributes.rs b/tests/pretty/stmt_expr_attributes.rs
index f9041268211..e7fd4642724 100644
--- a/tests/pretty/stmt_expr_attributes.rs
+++ b/tests/pretty/stmt_expr_attributes.rs
@@ -147,13 +147,13 @@ fn _11() {
     let _ = #[rustc_dummy] (0);
     let _ = #[rustc_dummy] (0,);
     let _ = #[rustc_dummy] (0, 0);
-    let _ = (#[rustc_dummy] 0) + #[rustc_dummy] 0;
+    let _ = #[rustc_dummy] 0 + #[rustc_dummy] 0;
     let _ = #[rustc_dummy] !0;
     let _ = #[rustc_dummy] -0i32;
     let _ = #[rustc_dummy] false;
     let _ = #[rustc_dummy] 'c';
     let _ = #[rustc_dummy] 0;
-    let _ = (#[rustc_dummy] 0) as usize;
+    let _ = #[rustc_dummy] 0 as usize;
     let _ =
         #[rustc_dummy] while false {
             #![rustc_dummy]
@@ -208,8 +208,8 @@ fn _11() {
         };
     let const {} = #[rustc_dummy] const {};
     let mut x = 0;
-    let _ = (#[rustc_dummy] x) = 15;
-    let _ = (#[rustc_dummy] x) += 15;
+    let _ = #[rustc_dummy] x = 15;
+    let _ = #[rustc_dummy] x += 15;
     let s = Foo { data: () };
     let _ = #[rustc_dummy] s.data;
     let _ = (#[rustc_dummy] s).data;
@@ -219,8 +219,8 @@ fn _11() {
     let v = vec!(0);
     let _ = #[rustc_dummy] v[0];
     let _ = (#[rustc_dummy] v)[0];
-    let _ = (#[rustc_dummy] 0)..#[rustc_dummy] 0;
-    let _ = (#[rustc_dummy] 0)..;
+    let _ = #[rustc_dummy] 0..#[rustc_dummy] 0;
+    let _ = #[rustc_dummy] 0..;
     let _ = #[rustc_dummy] (0..0);
     let _ = #[rustc_dummy] (0..);
     let _ = #[rustc_dummy] (..0);
diff --git a/tests/run-make/same-lib-two-locations-no-panic/bar.rs b/tests/run-make/same-lib-two-locations-no-panic/bar.rs
deleted file mode 100644
index bb7b36c496e..00000000000
--- a/tests/run-make/same-lib-two-locations-no-panic/bar.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-extern crate foo;
-
-fn main() {}
diff --git a/tests/run-make/same-lib-two-locations-no-panic/foo.rs b/tests/run-make/same-lib-two-locations-no-panic/foo.rs
deleted file mode 100644
index 82b2dfe9fdb..00000000000
--- a/tests/run-make/same-lib-two-locations-no-panic/foo.rs
+++ /dev/null
@@ -1 +0,0 @@
-#![crate_name = "foo"]
diff --git a/tests/run-make/same-lib-two-locations-no-panic/rmake.rs b/tests/run-make/same-lib-two-locations-no-panic/rmake.rs
deleted file mode 100644
index 2900c3c8b74..00000000000
--- a/tests/run-make/same-lib-two-locations-no-panic/rmake.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-// A path which contains the same rlib or dylib in two locations
-// should not cause an assertion panic in the compiler.
-// This test tries to replicate the linked issue and checks
-// if the bugged error makes a resurgence.
-
-// See https://github.com/rust-lang/rust/issues/11908
-
-//@ ignore-cross-compile
-
-use run_make_support::{dynamic_lib, rust_lib, rustc, tmp_dir};
-use std::fs;
-
-fn main() {
-    let tmp_dir_other = tmp_dir().join("other");
-
-    fs::create_dir(&tmp_dir_other);
-    rustc().input("foo.rs").crate_type("dylib").arg("-Cprefer-dynamic").run();
-    fs::rename(dynamic_lib("foo"), &tmp_dir_other);
-    rustc().input("foo.rs").crate_type("dylib").arg("-Cprefer-dynamic").run();
-    rustc().input("bar.rs").library_search_path(&tmp_dir_other).run();
-    fs::remove_dir_all(tmp_dir());
-
-    fs::create_dir_all(&tmp_dir_other);
-    rustc().input("foo.rs").crate_type("rlib").run();
-    fs::rename(rust_lib("foo"), &tmp_dir_other);
-    rustc().input("foo.rs").crate_type("rlib").run();
-    rustc().input("bar.rs").library_search_path(tmp_dir_other).run();
-}
diff --git a/tests/ui/lint/unreachable_pub.rs b/tests/ui/lint/unreachable_pub.rs
index 22c091e112b..f21f6640342 100644
--- a/tests/ui/lint/unreachable_pub.rs
+++ b/tests/ui/lint/unreachable_pub.rs
@@ -9,12 +9,16 @@ mod private_mod {
     pub use std::env::{Args}; // braced-use has different item spans than unbraced
     //~^ WARNING unreachable_pub
 
+    // we lint on struct definition
     pub struct Hydrogen { //~ WARNING unreachable_pub
-        // `pub` struct fields, too
-        pub neutrons: usize, //~ WARNING unreachable_pub
-        // (... but not more-restricted fields)
+        // but not on fields, even if they are `pub` as putting `pub(crate)`
+        // it would clutter the source code for little value
+        pub neutrons: usize,
         pub(crate) electrons: usize
     }
+    pub(crate) struct Calcium {
+        pub neutrons: usize,
+    }
     impl Hydrogen {
         // impls, too
         pub fn count_neutrons(&self) -> usize { self.neutrons } //~ WARNING unreachable_pub
diff --git a/tests/ui/lint/unreachable_pub.stderr b/tests/ui/lint/unreachable_pub.stderr
index 762834b97b9..705a537a3f1 100644
--- a/tests/ui/lint/unreachable_pub.stderr
+++ b/tests/ui/lint/unreachable_pub.stderr
@@ -24,7 +24,7 @@ LL |     pub use std::env::{Args}; // braced-use has different item spans than u
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:12:5
+  --> $DIR/unreachable_pub.rs:13:5
    |
 LL |     pub struct Hydrogen {
    |     ---^^^^^^^^^^^^^^^^
@@ -33,16 +33,8 @@ LL |     pub struct Hydrogen {
    |
    = help: or consider exporting it for use by other crates
 
-warning: unreachable `pub` field
-  --> $DIR/unreachable_pub.rs:14:9
-   |
-LL |         pub neutrons: usize,
-   |         ---^^^^^^^^^^^^^^^^
-   |         |
-   |         help: consider restricting its visibility: `pub(crate)`
-
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:20:9
+  --> $DIR/unreachable_pub.rs:24:9
    |
 LL |         pub fn count_neutrons(&self) -> usize { self.neutrons }
    |         ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -50,7 +42,7 @@ LL |         pub fn count_neutrons(&self) -> usize { self.neutrons }
    |         help: consider restricting its visibility: `pub(crate)`
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:29:5
+  --> $DIR/unreachable_pub.rs:33:5
    |
 LL |     pub enum Helium {}
    |     ---^^^^^^^^^^^^
@@ -60,7 +52,7 @@ LL |     pub enum Helium {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:30:5
+  --> $DIR/unreachable_pub.rs:34:5
    |
 LL |     pub union Lithium { c1: usize, c2: u8 }
    |     ---^^^^^^^^^^^^^^
@@ -70,7 +62,7 @@ LL |     pub union Lithium { c1: usize, c2: u8 }
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:31:5
+  --> $DIR/unreachable_pub.rs:35:5
    |
 LL |     pub fn beryllium() {}
    |     ---^^^^^^^^^^^^^^^
@@ -80,7 +72,7 @@ LL |     pub fn beryllium() {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:32:5
+  --> $DIR/unreachable_pub.rs:36:5
    |
 LL |     pub trait Boron {}
    |     ---^^^^^^^^^^^^
@@ -90,7 +82,7 @@ LL |     pub trait Boron {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:33:5
+  --> $DIR/unreachable_pub.rs:37:5
    |
 LL |     pub const CARBON: usize = 1;
    |     ---^^^^^^^^^^^^^^^^^^^^
@@ -100,7 +92,7 @@ LL |     pub const CARBON: usize = 1;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:34:5
+  --> $DIR/unreachable_pub.rs:38:5
    |
 LL |     pub static NITROGEN: usize = 2;
    |     ---^^^^^^^^^^^^^^^^^^^^^^^
@@ -110,7 +102,7 @@ LL |     pub static NITROGEN: usize = 2;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:35:5
+  --> $DIR/unreachable_pub.rs:39:5
    |
 LL |     pub type Oxygen = bool;
    |     ---^^^^^^^^^^^^
@@ -120,7 +112,7 @@ LL |     pub type Oxygen = bool;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:38:47
+  --> $DIR/unreachable_pub.rs:42:47
    |
 LL |         ($visibility: vis, $name: ident) => { $visibility struct $name {} }
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -135,7 +127,7 @@ LL |     define_empty_struct_with_visibility!(pub, Fluorine);
    = note: this warning originates in the macro `define_empty_struct_with_visibility` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:44:9
+  --> $DIR/unreachable_pub.rs:48:9
    |
 LL |         pub fn catalyze() -> bool;
    |         ---^^^^^^^^^^^^^^^^^^^^^^
@@ -144,5 +136,5 @@ LL |         pub fn catalyze() -> bool;
    |
    = help: or consider exporting it for use by other crates
 
-warning: 14 warnings emitted
+warning: 13 warnings emitted
 
diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs
index 0f9eac93930..ba32fb566e8 100644
--- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs
+++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs
@@ -20,10 +20,10 @@ fn doc_comment_between_if_else(num: u8) -> bool {
 }
 
 fn doc_comment_on_expr(num: u8) -> bool {
-    (/// useless doc comment
+    /// useless doc comment
     //~^ ERROR: attributes on expressions are experimental
     //~| ERROR: unused doc comment
-    num) == 3
+    num == 3
 }
 
 fn doc_comment_on_expr_field() -> bool {
diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr
index 1ff5c8d8825..c0741174554 100644
--- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr
+++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr
@@ -5,10 +5,10 @@ LL |     else {
    |     ^^^^ expected expression
 
 error[E0658]: attributes on expressions are experimental
-  --> $DIR/unused-doc-comments-edge-cases.rs:23:6
+  --> $DIR/unused-doc-comments-edge-cases.rs:23:5
    |
-LL |     (/// useless doc comment
-   |      ^^^^^^^^^^^^^^^^^^^^^^^
+LL |     /// useless doc comment
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
    = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
@@ -32,12 +32,12 @@ LL | #![deny(unused_doc_comments)]
    |         ^^^^^^^^^^^^^^^^^^^
 
 error: unused doc comment
-  --> $DIR/unused-doc-comments-edge-cases.rs:23:6
+  --> $DIR/unused-doc-comments-edge-cases.rs:23:5
    |
-LL |     (/// useless doc comment
-   |      ^^^^^^^^^^^^^^^^^^^^^^^
+LL |     /// useless doc comment
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
 ...
-LL |     num) == 3
+LL |     num == 3
    |     --- rustdoc does not generate documentation for expressions
    |
    = help: use `//` for a plain comment
diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed b/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed
deleted file mode 100644
index aae71ede771..00000000000
--- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ run-rustfix
-#![feature(stmt_expr_attributes)]
-#![allow(unused_assignments, unused_attributes)]
-
-fn main() {
-    let mut x = (#[deprecated] 1) + 2; //~ ERROR ambiguous
-
-    (#[deprecated] x) = 4; //~ ERROR ambiguous
-
-    x = (#[deprecated] 5) as i32; //~ ERROR ambiguous
-
-    let _r = (#[deprecated] 1)..6; //~ ERROR ambiguous
-
-    println!("{}", x);
-}
diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs b/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs
deleted file mode 100644
index 613e01d743b..00000000000
--- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ run-rustfix
-#![feature(stmt_expr_attributes)]
-#![allow(unused_assignments, unused_attributes)]
-
-fn main() {
-    let mut x = #[deprecated] 1 + 2; //~ ERROR ambiguous
-
-    #[deprecated] x = 4; //~ ERROR ambiguous
-
-    x = #[deprecated] 5 as i32; //~ ERROR ambiguous
-
-    let _r = #[deprecated] 1..6; //~ ERROR ambiguous
-
-    println!("{}", x);
-}
diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr b/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr
deleted file mode 100644
index 0430570fd49..00000000000
--- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr
+++ /dev/null
@@ -1,46 +0,0 @@
-error: ambiguous outer attributes
-  --> $DIR/attr-binary-expr-ambigous.rs:6:17
-   |
-LL |     let mut x = #[deprecated] 1 + 2;
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-help: wrap the expression in parentheses
-   |
-LL |     let mut x = (#[deprecated] 1) + 2;
-   |                 +               +
-
-error: ambiguous outer attributes
-  --> $DIR/attr-binary-expr-ambigous.rs:8:5
-   |
-LL |     #[deprecated] x = 4;
-   |     ^^^^^^^^^^^^^^^^^^^
-   |
-help: wrap the expression in parentheses
-   |
-LL |     (#[deprecated] x) = 4;
-   |     +               +
-
-error: ambiguous outer attributes
-  --> $DIR/attr-binary-expr-ambigous.rs:10:9
-   |
-LL |     x = #[deprecated] 5 as i32;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: wrap the expression in parentheses
-   |
-LL |     x = (#[deprecated] 5) as i32;
-   |         +               +
-
-error: ambiguous outer attributes
-  --> $DIR/attr-binary-expr-ambigous.rs:12:14
-   |
-LL |     let _r = #[deprecated] 1..6;
-   |              ^^^^^^^^^^^^^^^^^^
-   |
-help: wrap the expression in parentheses
-   |
-LL |     let _r = (#[deprecated] 1)..6;
-   |              +               +
-
-error: aborting due to 4 previous errors
-
diff --git a/tests/ui/proc-macro/issue-81555.rs b/tests/ui/proc-macro/issue-81555.rs
index b337ab7ce37..7a61a31952f 100644
--- a/tests/ui/proc-macro/issue-81555.rs
+++ b/tests/ui/proc-macro/issue-81555.rs
@@ -10,5 +10,6 @@ use test_macros::identity_attr;
 fn main() {
     let _x;
     let y = ();
-    (#[identity_attr] _x) = y;
+    #[identity_attr]
+    _x = y;
 }