about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-02-22 20:45:24 +0000
committerbors <bors@rust-lang.org>2024-02-22 20:45:24 +0000
commit397937d812852f9bbeb671005cb399dbcb357cde (patch)
tree4af90ca24da0be119ccfadea6502ac0df79fa793
parentd9ae43c7879c83f08ea2e886b55ea0f6839c9ead (diff)
parentc71484eefd38d6d60a54981376f3b157789cc570 (diff)
downloadrust-397937d812852f9bbeb671005cb399dbcb357cde.tar.gz
rust-397937d812852f9bbeb671005cb399dbcb357cde.zip
Auto merge of #119989 - lcnr:sub_relations-bye-bye, r=compiler-errors
remove `sub_relations` from the `InferCtxt`

While doing so, I tried to remove the `delay_span_bug` in `rematch_impl` again, which lead me to discover another `freshen` bug, fixing that one in the second commit. See commit descriptions for the reasoning behind each change.

r? `@compiler-errors`
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs7
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs3
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs43
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/sub_relations.rs81
-rw-r--r--compiler/rustc_infer/src/infer/freshen.rs136
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs16
-rw-r--r--compiler/rustc_infer/src/infer/type_variable.rs106
-rw-r--r--compiler/rustc_infer/src/infer/undo_log.rs8
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs106
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs25
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs11
-rw-r--r--tests/ui/const-generics/occurs-check/unused-substs-2.rs5
-rw-r--r--tests/ui/const-generics/occurs-check/unused-substs-2.stderr6
-rw-r--r--tests/ui/const-generics/occurs-check/unused-substs-5.stderr6
-rw-r--r--tests/ui/impl-trait/issues/issue-62742.rs1
-rw-r--r--tests/ui/impl-trait/issues/issue-62742.stderr29
-rw-r--r--tests/ui/impl-trait/issues/issue-84073.rs2
-rw-r--r--tests/ui/impl-trait/issues/issue-84073.stderr13
-rw-r--r--tests/ui/infinite/infinite-autoderef.rs4
-rw-r--r--tests/ui/infinite/infinite-autoderef.stderr55
-rw-r--r--tests/ui/infinite/infinite-type-alias-mutual-recursion.feature.stderr6
-rw-r--r--tests/ui/infinite/infinite-type-alias-mutual-recursion.rs6
-rw-r--r--tests/ui/infinite/infinite-vec-type-recursion.feature.stderr2
-rw-r--r--tests/ui/infinite/infinite-vec-type-recursion.rs2
-rw-r--r--tests/ui/issues/issue-59494.rs2
-rw-r--r--tests/ui/issues/issue-59494.stderr19
-rw-r--r--tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr16
-rw-r--r--tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr6
-rw-r--r--tests/ui/lazy-type-alias/inherent-impls-overflow.rs14
-rw-r--r--tests/ui/occurs-check-2.rs3
-rw-r--r--tests/ui/occurs-check-2.stderr11
-rw-r--r--tests/ui/occurs-check-3.rs10
-rw-r--r--tests/ui/occurs-check-3.stderr10
-rw-r--r--tests/ui/occurs-check.rs5
-rw-r--r--tests/ui/occurs-check.stderr13
-rw-r--r--tests/ui/span/coerce-suggestions.rs4
-rw-r--r--tests/ui/span/coerce-suggestions.stderr13
-rw-r--r--tests/ui/traits/subtype-recursion-limit.rs2
-rw-r--r--tests/ui/traits/subtype-recursion-limit.stderr2
-rw-r--r--tests/ui/traits/well-formed-recursion-limit.rs4
-rw-r--r--tests/ui/traits/well-formed-recursion-limit.stderr2
47 files changed, 409 insertions, 455 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 986af2f5c9e..9303e437a96 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1522,10 +1522,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if self.next_trait_solver()
             && let ty::Alias(..) = ty.kind()
         {
-            match self
+            // We need to use a separate variable here as otherwise the temporary for
+            // `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting
+            // in a reentrant borrow, causing an ICE.
+            let result = self
                 .at(&self.misc(sp), self.param_env)
-                .structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut())
-            {
+                .structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut());
+            match result {
                 Ok(normalized_ty) => normalized_ty,
                 Err(errors) => {
                     let guar = self.err_ctxt().report_fulfillment_errors(errors);
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index ad4546c09b5..685b1af931e 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -11,6 +11,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir_analysis::astconv::AstConv;
 use rustc_infer::infer;
+use rustc_infer::infer::error_reporting::sub_relations::SubRelations;
 use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
@@ -155,8 +156,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt
     pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
+        let mut sub_relations = SubRelations::default();
+        sub_relations.add_constraints(
+            self,
+            self.fulfillment_cx.borrow_mut().pending_obligations().iter().map(|o| o.predicate),
+        );
         TypeErrCtxt {
             infcx: &self.infcx,
+            sub_relations: RefCell::new(sub_relations),
             typeck_results: Some(self.typeck_results.borrow()),
             fallback_has_occurred: self.fallback_has_occurred.get(),
             normalize_fn_sig: Box::new(|fn_sig| {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 505d56cf491..d40f3f501f5 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -88,6 +88,7 @@ mod note_and_explain;
 mod suggest;
 
 pub(crate) mod need_type_info;
+pub mod sub_relations;
 pub use need_type_info::TypeAnnotationNeeded;
 
 pub mod nice_region_error;
@@ -123,6 +124,8 @@ fn escape_literal(s: &str) -> String {
 /// methods which should not be used during the happy path.
 pub struct TypeErrCtxt<'a, 'tcx> {
     pub infcx: &'a InferCtxt<'tcx>,
+    pub sub_relations: std::cell::RefCell<sub_relations::SubRelations>,
+
     pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
     pub fallback_has_occurred: bool,
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index af722b20626..896d1747850 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -502,7 +502,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     parent_name,
                 });
 
-                let args = if self.infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn)
+                let args = if self.tcx.get_diagnostic_item(sym::iterator_collect_fn)
                     == Some(generics_def_id)
                 {
                     "Vec<_>".to_string()
@@ -710,7 +710,7 @@ struct InsertableGenericArgs<'tcx> {
 /// While doing so, the currently best spot is stored in `infer_source`.
 /// For details on how we rank spots, see [Self::source_cost]
 struct FindInferSourceVisitor<'a, 'tcx> {
-    infcx: &'a InferCtxt<'tcx>,
+    tecx: &'a TypeErrCtxt<'a, 'tcx>,
     typeck_results: &'a TypeckResults<'tcx>,
 
     target: GenericArg<'tcx>,
@@ -722,12 +722,12 @@ struct FindInferSourceVisitor<'a, 'tcx> {
 
 impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
     fn new(
-        infcx: &'a InferCtxt<'tcx>,
+        tecx: &'a TypeErrCtxt<'a, 'tcx>,
         typeck_results: &'a TypeckResults<'tcx>,
         target: GenericArg<'tcx>,
     ) -> Self {
         FindInferSourceVisitor {
-            infcx,
+            tecx,
             typeck_results,
 
             target,
@@ -778,7 +778,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
         }
 
         // The sources are listed in order of preference here.
-        let tcx = self.infcx.tcx;
+        let tcx = self.tecx.tcx;
         let ctx = CostCtxt { tcx };
         match source.kind {
             InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty),
@@ -829,12 +829,12 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
 
     fn node_args_opt(&self, hir_id: HirId) -> Option<GenericArgsRef<'tcx>> {
         let args = self.typeck_results.node_args_opt(hir_id);
-        self.infcx.resolve_vars_if_possible(args)
+        self.tecx.resolve_vars_if_possible(args)
     }
 
     fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
         let ty = self.typeck_results.node_type_opt(hir_id);
-        self.infcx.resolve_vars_if_possible(ty)
+        self.tecx.resolve_vars_if_possible(ty)
     }
 
     // Check whether this generic argument is the inference variable we
@@ -849,7 +849,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
                 use ty::{Infer, TyVar};
                 match (inner_ty.kind(), target_ty.kind()) {
                     (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
-                        self.infcx.inner.borrow_mut().type_variables().sub_unified(a_vid, b_vid)
+                        self.tecx.sub_relations.borrow_mut().unified(self.tecx, a_vid, b_vid)
                     }
                     _ => false,
                 }
@@ -857,12 +857,9 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
             (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => {
                 use ty::InferConst::*;
                 match (inner_ct.kind(), target_ct.kind()) {
-                    (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => self
-                        .infcx
-                        .inner
-                        .borrow_mut()
-                        .const_unification_table()
-                        .unioned(a_vid, b_vid),
+                    (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => {
+                        self.tecx.inner.borrow_mut().const_unification_table().unioned(a_vid, b_vid)
+                    }
                     _ => false,
                 }
             }
@@ -917,7 +914,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
         &self,
         expr: &'tcx hir::Expr<'tcx>,
     ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
-        let tcx = self.infcx.tcx;
+        let tcx = self.tecx.tcx;
         match expr.kind {
             hir::ExprKind::Path(ref path) => {
                 if let Some(args) = self.node_args_opt(expr.hir_id) {
@@ -980,7 +977,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
         path: &'tcx hir::Path<'tcx>,
         args: GenericArgsRef<'tcx>,
     ) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a {
-        let tcx = self.infcx.tcx;
+        let tcx = self.tecx.tcx;
         let have_turbofish = path.segments.iter().any(|segment| {
             segment.args.is_some_and(|args| args.args.iter().any(|arg| arg.is_ty_or_const()))
         });
@@ -1034,7 +1031,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
         args: GenericArgsRef<'tcx>,
         qpath: &'tcx hir::QPath<'tcx>,
     ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
-        let tcx = self.infcx.tcx;
+        let tcx = self.tecx.tcx;
         match qpath {
             hir::QPath::Resolved(_self_ty, path) => {
                 Box::new(self.resolved_path_inferred_arg_iter(path, args))
@@ -1107,7 +1104,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
     type NestedFilter = nested_filter::OnlyBodies;
 
     fn nested_visit_map(&mut self) -> Self::Map {
-        self.infcx.tcx.hir()
+        self.tecx.tcx.hir()
     }
 
     fn visit_local(&mut self, local: &'tcx Local<'tcx>) {
@@ -1163,7 +1160,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
 
     #[instrument(level = "debug", skip(self))]
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-        let tcx = self.infcx.tcx;
+        let tcx = self.tecx.tcx;
         match expr.kind {
             // When encountering `func(arg)` first look into `arg` and then `func`,
             // as `arg` is "more specific".
@@ -1194,7 +1191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
                 if generics.parent.is_none() && generics.has_self {
                     argument_index += 1;
                 }
-                let args = self.infcx.resolve_vars_if_possible(args);
+                let args = self.tecx.resolve_vars_if_possible(args);
                 let generic_args =
                     &generics.own_args_no_defaults(tcx, args)[generics.own_counts().lifetimes..];
                 let span = match expr.kind {
@@ -1224,7 +1221,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
             {
                 let output = args.as_closure().sig().output().skip_binder();
                 if self.generic_arg_contains_target(output.into()) {
-                    let body = self.infcx.tcx.hir().body(body);
+                    let body = self.tecx.tcx.hir().body(body);
                     let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) {
                         None
                     } else {
@@ -1252,12 +1249,12 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
             && let Some(args) = self.node_args_opt(expr.hir_id)
             && args.iter().any(|arg| self.generic_arg_contains_target(arg))
             && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id)
-            && self.infcx.tcx.trait_of_item(def_id).is_some()
+            && self.tecx.tcx.trait_of_item(def_id).is_some()
             && !has_impl_trait(def_id)
         {
             let successor =
                 method_args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo()));
-            let args = self.infcx.resolve_vars_if_possible(args);
+            let args = self.tecx.resolve_vars_if_possible(args);
             self.update_infer_source(InferSource {
                 span: path.ident.span,
                 kind: InferSourceKind::FullyQualifiedMethodCall {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/sub_relations.rs b/compiler/rustc_infer/src/infer/error_reporting/sub_relations.rs
new file mode 100644
index 00000000000..ef26a8ff7b8
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/sub_relations.rs
@@ -0,0 +1,81 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::undo_log::NoUndo;
+use rustc_data_structures::unify as ut;
+use rustc_middle::ty;
+
+use crate::infer::InferCtxt;
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+struct SubId(u32);
+impl ut::UnifyKey for SubId {
+    type Value = ();
+    #[inline]
+    fn index(&self) -> u32 {
+        self.0
+    }
+    #[inline]
+    fn from_index(i: u32) -> SubId {
+        SubId(i)
+    }
+    fn tag() -> &'static str {
+        "SubId"
+    }
+}
+
+/// When reporting ambiguity errors, we sometimes want to
+/// treat all inference vars which are subtypes of each
+/// others as if they are equal. For this case we compute
+/// the transitive closure of our subtype obligations here.
+///
+/// E.g. when encountering ambiguity errors, we want to suggest
+/// specifying some method argument or to add a type annotation
+/// to a local variable. Because subtyping cannot change the
+/// shape of a type, it's fine if the cause of the ambiguity error
+/// is only related to the suggested variable via subtyping.
+///
+/// Even for something like `let x = returns_arg(); x.method();` the
+/// type of `x` is only a supertype of the argument of `returns_arg`. We
+/// still want to suggest specifying the type of the argument.
+#[derive(Default)]
+pub struct SubRelations {
+    map: FxHashMap<ty::TyVid, SubId>,
+    table: ut::UnificationTableStorage<SubId>,
+}
+
+impl SubRelations {
+    fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId {
+        let root_vid = infcx.root_var(vid);
+        *self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(()))
+    }
+
+    pub fn add_constraints<'tcx>(
+        &mut self,
+        infcx: &InferCtxt<'tcx>,
+        obls: impl IntoIterator<Item = ty::Predicate<'tcx>>,
+    ) {
+        for p in obls {
+            let (a, b) = match p.kind().skip_binder() {
+                ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
+                    (a, b)
+                }
+                ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
+                _ => continue,
+            };
+
+            match (a.kind(), b.kind()) {
+                (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
+                    let a = self.get_id(infcx, a_vid);
+                    let b = self.get_id(infcx, b_vid);
+                    self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap();
+                }
+                _ => continue,
+            }
+        }
+    }
+
+    pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool {
+        let a = self.get_id(infcx, a);
+        let b = self.get_id(infcx, b);
+        self.table.with_log(&mut NoUndo).unioned(a, b)
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
index 2d5fa1b5c70..ef9c407acef 100644
--- a/compiler/rustc_infer/src/infer/freshen.rs
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -56,49 +56,46 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
         }
     }
 
-    fn freshen_ty<F>(&mut self, opt_ty: Option<Ty<'tcx>>, key: ty::InferTy, mk_fresh: F) -> Ty<'tcx>
+    fn freshen_ty<F>(&mut self, input: Result<Ty<'tcx>, ty::InferTy>, mk_fresh: F) -> Ty<'tcx>
     where
         F: FnOnce(u32) -> Ty<'tcx>,
     {
-        if let Some(ty) = opt_ty {
-            return ty.fold_with(self);
-        }
-
-        match self.ty_freshen_map.entry(key) {
-            Entry::Occupied(entry) => *entry.get(),
-            Entry::Vacant(entry) => {
-                let index = self.ty_freshen_count;
-                self.ty_freshen_count += 1;
-                let t = mk_fresh(index);
-                entry.insert(t);
-                t
-            }
+        match input {
+            Ok(ty) => ty.fold_with(self),
+            Err(key) => match self.ty_freshen_map.entry(key) {
+                Entry::Occupied(entry) => *entry.get(),
+                Entry::Vacant(entry) => {
+                    let index = self.ty_freshen_count;
+                    self.ty_freshen_count += 1;
+                    let t = mk_fresh(index);
+                    entry.insert(t);
+                    t
+                }
+            },
         }
     }
 
     fn freshen_const<F>(
         &mut self,
-        opt_ct: Option<ty::Const<'tcx>>,
-        key: ty::InferConst,
+        input: Result<ty::Const<'tcx>, ty::InferConst>,
         freshener: F,
         ty: Ty<'tcx>,
     ) -> ty::Const<'tcx>
     where
         F: FnOnce(u32) -> ty::InferConst,
     {
-        if let Some(ct) = opt_ct {
-            return ct.fold_with(self);
-        }
-
-        match self.const_freshen_map.entry(key) {
-            Entry::Occupied(entry) => *entry.get(),
-            Entry::Vacant(entry) => {
-                let index = self.const_freshen_count;
-                self.const_freshen_count += 1;
-                let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index), ty);
-                entry.insert(ct);
-                ct
-            }
+        match input {
+            Ok(ct) => ct.fold_with(self),
+            Err(key) => match self.const_freshen_map.entry(key) {
+                Entry::Occupied(entry) => *entry.get(),
+                Entry::Vacant(entry) => {
+                    let index = self.const_freshen_count;
+                    self.const_freshen_count += 1;
+                    let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index), ty);
+                    entry.insert(ct);
+                    ct
+                }
+            },
         }
     }
 }
@@ -146,19 +143,22 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
     fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
         match ct.kind() {
             ty::ConstKind::Infer(ty::InferConst::Var(v)) => {
-                let opt_ct =
-                    self.infcx.inner.borrow_mut().const_unification_table().probe_value(v).known();
-                self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty())
+                let mut inner = self.infcx.inner.borrow_mut();
+                let input =
+                    inner.const_unification_table().probe_value(v).known().ok_or_else(|| {
+                        ty::InferConst::Var(inner.const_unification_table().find(v).vid)
+                    });
+                drop(inner);
+                self.freshen_const(input, ty::InferConst::Fresh, ct.ty())
             }
             ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
-                let opt_ct =
-                    self.infcx.inner.borrow_mut().effect_unification_table().probe_value(v).known();
-                self.freshen_const(
-                    opt_ct,
-                    ty::InferConst::EffectVar(v),
-                    ty::InferConst::Fresh,
-                    ct.ty(),
-                )
+                let mut inner = self.infcx.inner.borrow_mut();
+                let input =
+                    inner.effect_unification_table().probe_value(v).known().ok_or_else(|| {
+                        ty::InferConst::EffectVar(inner.effect_unification_table().find(v).vid)
+                    });
+                drop(inner);
+                self.freshen_const(input, ty::InferConst::Fresh, ct.ty())
             }
             ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
                 if i >= self.const_freshen_count {
@@ -191,35 +191,37 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
     fn fold_infer_ty(&mut self, v: ty::InferTy) -> Option<Ty<'tcx>> {
         match v {
             ty::TyVar(v) => {
-                let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
-                Some(self.freshen_ty(opt_ty, ty::TyVar(v), |n| Ty::new_fresh(self.infcx.tcx, n)))
+                let mut inner = self.infcx.inner.borrow_mut();
+                let input = inner
+                    .type_variables()
+                    .probe(v)
+                    .known()
+                    .ok_or_else(|| ty::TyVar(inner.type_variables().root_var(v)));
+                drop(inner);
+                Some(self.freshen_ty(input, |n| Ty::new_fresh(self.infcx.tcx, n)))
             }
 
-            ty::IntVar(v) => Some(
-                self.freshen_ty(
-                    self.infcx
-                        .inner
-                        .borrow_mut()
-                        .int_unification_table()
-                        .probe_value(v)
-                        .map(|v| v.to_type(self.infcx.tcx)),
-                    ty::IntVar(v),
-                    |n| Ty::new_fresh_int(self.infcx.tcx, n),
-                ),
-            ),
-
-            ty::FloatVar(v) => Some(
-                self.freshen_ty(
-                    self.infcx
-                        .inner
-                        .borrow_mut()
-                        .float_unification_table()
-                        .probe_value(v)
-                        .map(|v| v.to_type(self.infcx.tcx)),
-                    ty::FloatVar(v),
-                    |n| Ty::new_fresh_float(self.infcx.tcx, n),
-                ),
-            ),
+            ty::IntVar(v) => {
+                let mut inner = self.infcx.inner.borrow_mut();
+                let input = inner
+                    .int_unification_table()
+                    .probe_value(v)
+                    .map(|v| v.to_type(self.infcx.tcx))
+                    .ok_or_else(|| ty::IntVar(inner.int_unification_table().find(v)));
+                drop(inner);
+                Some(self.freshen_ty(input, |n| Ty::new_fresh_int(self.infcx.tcx, n)))
+            }
+
+            ty::FloatVar(v) => {
+                let mut inner = self.infcx.inner.borrow_mut();
+                let input = inner
+                    .float_unification_table()
+                    .probe_value(v)
+                    .map(|v| v.to_type(self.infcx.tcx))
+                    .ok_or_else(|| ty::FloatVar(inner.float_unification_table().find(v)));
+                drop(inner);
+                Some(self.freshen_ty(input, |n| Ty::new_fresh_float(self.infcx.tcx, n)))
+            }
 
             ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct) => {
                 if ct >= self.ty_freshen_count {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 8fc71671b27..8b5710ee9ed 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -762,6 +762,7 @@ impl<'tcx> InferCtxt<'tcx> {
     pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
         TypeErrCtxt {
             infcx: self,
+            sub_relations: Default::default(),
             typeck_results: None,
             fallback_has_occurred: false,
             normalize_fn_sig: Box::new(|fn_sig| fn_sig),
@@ -1029,7 +1030,6 @@ impl<'tcx> InferCtxt<'tcx> {
         let r_b = self.shallow_resolve(predicate.skip_binder().b);
         match (r_a.kind(), r_b.kind()) {
             (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
-                self.inner.borrow_mut().type_variables().sub(a_vid, b_vid);
                 return Err((a_vid, b_vid));
             }
             _ => {}
diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index 90f10a0eba9..c4c9ddb1ad8 100644
--- a/compiler/rustc_infer/src/infer/relate/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -217,10 +217,9 @@ impl<'tcx> InferCtxt<'tcx> {
     ) -> RelateResult<'tcx, Generalization<T>> {
         assert!(!source_term.has_escaping_bound_vars());
         let (for_universe, root_vid) = match target_vid.into() {
-            ty::TermVid::Ty(ty_vid) => (
-                self.probe_ty_var(ty_vid).unwrap_err(),
-                ty::TermVid::Ty(self.inner.borrow_mut().type_variables().sub_root_var(ty_vid)),
-            ),
+            ty::TermVid::Ty(ty_vid) => {
+                (self.probe_ty_var(ty_vid).unwrap_err(), ty::TermVid::Ty(self.root_var(ty_vid)))
+            }
             ty::TermVid::Const(ct_vid) => (
                 self.probe_const_var(ct_vid).unwrap_err(),
                 ty::TermVid::Const(
@@ -424,9 +423,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
             ty::Infer(ty::TyVar(vid)) => {
                 let mut inner = self.infcx.inner.borrow_mut();
                 let vid = inner.type_variables().root_var(vid);
-                let sub_vid = inner.type_variables().sub_root_var(vid);
-
-                if ty::TermVid::Ty(sub_vid) == self.root_vid {
+                if ty::TermVid::Ty(vid) == self.root_vid {
                     // If sub-roots are equal, then `root_vid` and
                     // `vid` are related via subtyping.
                     Err(self.cyclic_term_error())
@@ -461,11 +458,6 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                             let new_var_id =
                                 inner.type_variables().new_var(self.for_universe, origin);
                             let u = Ty::new_var(self.tcx(), new_var_id);
-
-                            // Record that we replaced `vid` with `new_var_id` as part of a generalization
-                            // operation. This is needed to detect cyclic types. To see why, see the
-                            // docs in the `type_variables` module.
-                            inner.type_variables().sub(vid, new_var_id);
                             debug!("replacing original vid={:?} with new={:?}", vid, u);
                             Ok(u)
                         }
diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs
index 8b81eac8739..3630b0f439f 100644
--- a/compiler/rustc_infer/src/infer/type_variable.rs
+++ b/compiler/rustc_infer/src/infer/type_variable.rs
@@ -1,3 +1,4 @@
+use rustc_data_structures::undo_log::Rollback;
 use rustc_hir::def_id::DefId;
 use rustc_index::IndexVec;
 use rustc_middle::ty::{self, Ty, TyVid};
@@ -12,35 +13,9 @@ use std::cmp;
 use std::marker::PhantomData;
 use std::ops::Range;
 
-use rustc_data_structures::undo_log::Rollback;
-
-/// Represents a single undo-able action that affects a type inference variable.
-#[derive(Clone)]
-pub(crate) enum UndoLog<'tcx> {
-    EqRelation(sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>),
-    SubRelation(sv::UndoLog<ut::Delegate<ty::TyVid>>),
-}
-
-/// Convert from a specific kind of undo to the more general UndoLog
-impl<'tcx> From<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for UndoLog<'tcx> {
-    fn from(l: sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>) -> Self {
-        UndoLog::EqRelation(l)
-    }
-}
-
-/// Convert from a specific kind of undo to the more general UndoLog
-impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::TyVid>>> for UndoLog<'tcx> {
-    fn from(l: sv::UndoLog<ut::Delegate<ty::TyVid>>) -> Self {
-        UndoLog::SubRelation(l)
-    }
-}
-
-impl<'tcx> Rollback<UndoLog<'tcx>> for TypeVariableStorage<'tcx> {
-    fn reverse(&mut self, undo: UndoLog<'tcx>) {
-        match undo {
-            UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo),
-            UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo),
-        }
+impl<'tcx> Rollback<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for TypeVariableStorage<'tcx> {
+    fn reverse(&mut self, undo: sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>) {
+        self.eq_relations.reverse(undo)
     }
 }
 
@@ -52,41 +27,6 @@ pub struct TypeVariableStorage<'tcx> {
     /// constraint `?X == ?Y`. This table also stores, for each key,
     /// the known value.
     eq_relations: ut::UnificationTableStorage<TyVidEqKey<'tcx>>,
-
-    /// Two variables are unified in `sub_relations` when we have a
-    /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second
-    /// table exists only to help with the occurs check. In particular,
-    /// we want to report constraints like these as an occurs check
-    /// violation:
-    /// ``` text
-    /// ?1 <: ?3
-    /// Box<?3> <: ?1
-    /// ```
-    /// Without this second table, what would happen in a case like
-    /// this is that we would instantiate `?1` with a generalized
-    /// type like `Box<?6>`. We would then relate `Box<?3> <: Box<?6>`
-    /// and infer that `?3 <: ?6`. Next, since `?1` was instantiated,
-    /// we would process `?1 <: ?3`, generalize `?1 = Box<?6>` to `Box<?9>`,
-    /// and instantiate `?3` with `Box<?9>`. Finally, we would relate
-    /// `?6 <: ?9`. But now that we instantiated `?3`, we can process
-    /// `?3 <: ?6`, which gives us `Box<?9> <: ?6`... and the cycle
-    /// continues. (This is `occurs-check-2.rs`.)
-    ///
-    /// What prevents this cycle is that when we generalize
-    /// `Box<?3>` to `Box<?6>`, we also sub-unify `?3` and `?6`
-    /// (in the generalizer). When we then process `Box<?6> <: ?3`,
-    /// the occurs check then fails because `?6` and `?3` are sub-unified,
-    /// and hence generalization fails.
-    ///
-    /// This is reasonable because, in Rust, subtypes have the same
-    /// "skeleton" and hence there is no possible type such that
-    /// (e.g.)  `Box<?3> <: ?3` for any `?3`.
-    ///
-    /// In practice, we sometimes sub-unify variables in other spots, such
-    /// as when processing subtype predicates. This is not necessary but is
-    /// done to aid diagnostics, as it allows us to be more effective when
-    /// we guide the user towards where they should insert type hints.
-    sub_relations: ut::UnificationTableStorage<ty::TyVid>,
 }
 
 pub struct TypeVariableTable<'a, 'tcx> {
@@ -158,7 +98,6 @@ impl<'tcx> TypeVariableStorage<'tcx> {
         TypeVariableStorage {
             values: Default::default(),
             eq_relations: ut::UnificationTableStorage::new(),
-            sub_relations: ut::UnificationTableStorage::new(),
         }
     }
 
@@ -197,16 +136,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
         debug_assert!(self.probe(a).is_unknown());
         debug_assert!(self.probe(b).is_unknown());
         self.eq_relations().union(a, b);
-        self.sub_relations().union(a, b);
-    }
-
-    /// Records that `a <: b`, depending on `dir`.
-    ///
-    /// Precondition: neither `a` nor `b` are known.
-    pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) {
-        debug_assert!(self.probe(a).is_unknown());
-        debug_assert!(self.probe(b).is_unknown());
-        self.sub_relations().union(a, b);
     }
 
     /// Instantiates `vid` with the type `ty`.
@@ -240,10 +169,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
         origin: TypeVariableOrigin,
     ) -> ty::TyVid {
         let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
-
-        let sub_key = self.sub_relations().new_key(());
-        debug_assert_eq!(eq_key.vid, sub_key);
-
         let index = self.storage.values.push(TypeVariableData { origin });
         debug_assert_eq!(eq_key.vid, index);
 
@@ -266,24 +191,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
         self.eq_relations().find(vid).vid
     }
 
-    /// Returns the "root" variable of `vid` in the `sub_relations`
-    /// equivalence table. All type variables that have been are
-    /// related via equality or subtyping will yield the same root
-    /// variable (per the union-find algorithm), so `sub_root_var(a)
-    /// == sub_root_var(b)` implies that:
-    /// ```text
-    /// exists X. (a <: X || X <: a) && (b <: X || X <: b)
-    /// ```
-    pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
-        self.sub_relations().find(vid)
-    }
-
-    /// Returns `true` if `a` and `b` have same "sub-root" (i.e., exists some
-    /// type X such that `forall i in {a, b}. (i <: X || X <: i)`.
-    pub fn sub_unified(&mut self, a: ty::TyVid, b: ty::TyVid) -> bool {
-        self.sub_root_var(a) == self.sub_root_var(b)
-    }
-
     /// Retrieves the type to which `vid` has been instantiated, if
     /// any.
     pub fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> {
@@ -314,11 +221,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
         self.storage.eq_relations.with_log(self.undo_log)
     }
 
-    #[inline]
-    fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> {
-        self.storage.sub_relations.with_log(self.undo_log)
-    }
-
     /// Returns a range of the type variables created during the snapshot.
     pub fn vars_since_snapshot(
         &mut self,
diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs
index be02452d89f..829b0a73a0d 100644
--- a/compiler/rustc_infer/src/infer/undo_log.rs
+++ b/compiler/rustc_infer/src/infer/undo_log.rs
@@ -20,7 +20,7 @@ pub struct Snapshot<'tcx> {
 #[derive(Clone)]
 pub(crate) enum UndoLog<'tcx> {
     OpaqueTypes(OpaqueTypeKey<'tcx>, Option<OpaqueHiddenType<'tcx>>),
-    TypeVariables(type_variable::UndoLog<'tcx>),
+    TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
     ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
     IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
     FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
@@ -46,17 +46,13 @@ macro_rules! impl_from {
 // Upcast from a single kind of "undoable action" to the general enum
 impl_from! {
     RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
-    TypeVariables(type_variable::UndoLog<'tcx>),
 
     TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
-    TypeVariables(sv::UndoLog<ut::Delegate<ty::TyVid>>),
-
     IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
-
     FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
-    EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>),
 
     ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
+    EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>),
 
     RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
     ProjectionCache(traits::UndoLog<'tcx>),
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index 91312c9fdd6..b1c03e82cab 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -1,4 +1,4 @@
-use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::error_reporting::{OverflowCause, TypeErrCtxtExt};
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::{BoundVarReplacer, PlaceholderReplacer};
 use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -60,8 +60,12 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
         let tcx = infcx.tcx;
         let recursion_limit = tcx.recursion_limit();
         if !recursion_limit.value_within_limit(self.depth) {
+            let ty::Alias(_, data) = *alias_ty.kind() else {
+                unreachable!();
+            };
+
             self.at.infcx.err_ctxt().report_overflow_error(
-                &alias_ty,
+                OverflowCause::DeeplyNormalize(data),
                 self.at.cause.span,
                 true,
                 |_| {},
@@ -109,7 +113,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
         let recursion_limit = tcx.recursion_limit();
         if !recursion_limit.value_within_limit(self.depth) {
             self.at.infcx.err_ctxt().report_overflow_error(
-                &ty::Const::new_unevaluated(tcx, uv, ty),
+                OverflowCause::DeeplyNormalize(ty::AliasTy::new(tcx, uv.def, uv.args)),
                 self.at.cause.span,
                 true,
                 |_| {},
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 7186b96b40d..2b74b15ec9f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -57,12 +57,21 @@ use super::{
 
 pub use rustc_infer::traits::error_reporting::*;
 
+pub enum OverflowCause<'tcx> {
+    DeeplyNormalize(ty::AliasTy<'tcx>),
+    TraitSolver(ty::Predicate<'tcx>),
+}
+
 #[extension(pub trait TypeErrCtxtExt<'tcx>)]
 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn report_fulfillment_errors(
         &self,
         mut errors: Vec<FulfillmentError<'tcx>>,
     ) -> ErrorGuaranteed {
+        self.sub_relations
+            .borrow_mut()
+            .add_constraints(self, errors.iter().map(|e| e.obligation.predicate));
+
         #[derive(Debug)]
         struct ErrorDescriptor<'tcx> {
             predicate: ty::Predicate<'tcx>,
@@ -180,49 +189,78 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// whose result could not be truly determined and thus we can't say
     /// if the program type checks or not -- and they are unusual
     /// occurrences in any case.
-    fn report_overflow_error<T>(
+    fn report_overflow_error(
         &self,
-        predicate: &T,
+        cause: OverflowCause<'tcx>,
         span: Span,
         suggest_increasing_limit: bool,
         mutate: impl FnOnce(&mut DiagnosticBuilder<'_>),
-    ) -> !
-    where
-        T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
-    {
-        let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit);
+    ) -> ! {
+        let mut err = self.build_overflow_error(cause, span, suggest_increasing_limit);
         mutate(&mut err);
         err.emit();
         FatalError.raise();
     }
 
-    fn build_overflow_error<T>(
+    fn build_overflow_error(
         &self,
-        predicate: &T,
+        cause: OverflowCause<'tcx>,
         span: Span,
         suggest_increasing_limit: bool,
-    ) -> DiagnosticBuilder<'tcx>
-    where
-        T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
-    {
-        let predicate = self.resolve_vars_if_possible(predicate.clone());
-        let mut pred_str = predicate.to_string();
-
-        if pred_str.len() > 50 {
-            // We don't need to save the type to a file, we will be talking about this type already
-            // in a separate note when we explain the obligation, so it will be available that way.
-            let mut cx: FmtPrinter<'_, '_> =
-                FmtPrinter::new_with_limit(self.tcx, Namespace::TypeNS, rustc_session::Limit(6));
-            predicate.print(&mut cx).unwrap();
-            pred_str = cx.into_buffer();
+    ) -> DiagnosticBuilder<'tcx> {
+        fn with_short_path<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> String
+        where
+            T: fmt::Display + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
+        {
+            let s = value.to_string();
+            if s.len() > 50 {
+                // We don't need to save the type to a file, we will be talking about this type already
+                // in a separate note when we explain the obligation, so it will be available that way.
+                let mut cx: FmtPrinter<'_, '_> =
+                    FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, rustc_session::Limit(6));
+                value.print(&mut cx).unwrap();
+                cx.into_buffer()
+            } else {
+                s
+            }
         }
-        let mut err = struct_span_code_err!(
-            self.dcx(),
-            span,
-            E0275,
-            "overflow evaluating the requirement `{}`",
-            pred_str,
-        );
+
+        let mut err = match cause {
+            OverflowCause::DeeplyNormalize(alias_ty) => {
+                let alias_ty = self.resolve_vars_if_possible(alias_ty);
+                let kind = alias_ty.opt_kind(self.tcx).map_or("alias", |k| k.descr());
+                let alias_str = with_short_path(self.tcx, alias_ty);
+                struct_span_code_err!(
+                    self.dcx(),
+                    span,
+                    E0275,
+                    "overflow normalizing the {kind} `{alias_str}`",
+                )
+            }
+            OverflowCause::TraitSolver(predicate) => {
+                let predicate = self.resolve_vars_if_possible(predicate);
+                match predicate.kind().skip_binder() {
+                    ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ })
+                    | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
+                        struct_span_code_err!(
+                            self.dcx(),
+                            span,
+                            E0275,
+                            "overflow assigning `{a}` to `{b}`",
+                        )
+                    }
+                    _ => {
+                        let pred_str = with_short_path(self.tcx, predicate);
+                        struct_span_code_err!(
+                            self.dcx(),
+                            span,
+                            E0275,
+                            "overflow evaluating the requirement `{pred_str}`",
+                        )
+                    }
+                }
+            }
+        };
 
         if suggest_increasing_limit {
             self.suggest_new_overflow_limit(&mut err);
@@ -248,7 +286,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let predicate = obligation.predicate.clone().to_predicate(self.tcx);
         let predicate = self.resolve_vars_if_possible(predicate);
         self.report_overflow_error(
-            &predicate,
+            OverflowCause::TraitSolver(predicate),
             obligation.cause.span,
             suggest_increasing_limit,
             |err| {
@@ -299,7 +337,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed {
         let obligation = self.resolve_vars_if_possible(obligation);
-        let mut err = self.build_overflow_error(&obligation.predicate, obligation.cause.span, true);
+        let mut err = self.build_overflow_error(
+            OverflowCause::TraitSolver(obligation.predicate),
+            obligation.cause.span,
+            true,
+        );
         self.note_obligation_cause(&mut err, &obligation);
         self.point_at_returns_when_relevant(&mut err, &obligation);
         err.emit()
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index fd981130af8..b91698af942 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -450,12 +450,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                     .recursion_limit()
                     .value_within_limit(obligation.recursion_depth) =>
                 {
-                    self.selcx.infcx.err_ctxt().report_overflow_error(
-                        &obligation.predicate,
-                        obligation.cause.span,
-                        false,
-                        |_| {},
-                    );
+                    self.selcx.infcx.err_ctxt().report_overflow_obligation(&obligation, false);
                 }
 
                 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index 429e5a5d7a4..15bfffef3ce 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -1,4 +1,8 @@
 //! Deeply normalize types using the old trait solver.
+use super::error_reporting::OverflowCause;
+use super::error_reporting::TypeErrCtxtExt;
+use super::SelectionContext;
+use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::infer::at::At;
 use rustc_infer::infer::InferOk;
@@ -8,10 +12,6 @@ use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder};
 use rustc_middle::ty::{TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitableExt};
 
-use super::error_reporting::TypeErrCtxtExt;
-use super::SelectionContext;
-use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
-
 #[extension(pub trait NormalizeExt<'tcx>)]
 impl<'tcx> At<'_, 'tcx> {
     /// Normalize a value using the `AssocTypeNormalizer`.
@@ -173,7 +173,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
         }
 
         let (kind, data) = match *ty.kind() {
-            ty::Alias(kind, alias_ty) => (kind, alias_ty),
+            ty::Alias(kind, data) => (kind, data),
             _ => return ty.super_fold_with(self),
         };
 
@@ -210,7 +210,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
                         let recursion_limit = self.interner().recursion_limit();
                         if !recursion_limit.value_within_limit(self.depth) {
                             self.selcx.infcx.err_ctxt().report_overflow_error(
-                                &ty,
+                                OverflowCause::DeeplyNormalize(data),
                                 self.cause.span,
                                 true,
                                 |_| {},
@@ -306,7 +306,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
                 let recursion_limit = self.interner().recursion_limit();
                 if !recursion_limit.value_within_limit(self.depth) {
                     self.selcx.infcx.err_ctxt().report_overflow_error(
-                        &ty,
+                        OverflowCause::DeeplyNormalize(data),
                         self.cause.span,
                         false,
                         |diag| {
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 70fd0b7e50b..e5f8e336860 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -5,6 +5,7 @@
 use crate::infer::at::At;
 use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::{InferCtxt, InferOk};
+use crate::traits::error_reporting::OverflowCause;
 use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::normalize::needs_normalization;
 use crate::traits::{BoundVarReplacer, PlaceholderReplacer};
@@ -228,7 +229,11 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                             let guar = self
                                 .infcx
                                 .err_ctxt()
-                                .build_overflow_error(&ty, self.cause.span, true)
+                                .build_overflow_error(
+                                    OverflowCause::DeeplyNormalize(data),
+                                    self.cause.span,
+                                    true,
+                                )
                                 .delay_as_bug();
                             return Ok(Ty::new_error(self.interner(), guar));
                         }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 1146f869fc1..a4499d438c1 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -40,7 +40,6 @@ 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::fold::BottomUpFolder;
 use rustc_middle::ty::relate::TypeRelation;
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
@@ -2435,28 +2434,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         match self.match_impl(impl_def_id, impl_trait_header, obligation) {
             Ok(args) => args,
             Err(()) => {
-                // FIXME: A rematch may fail when a candidate cache hit occurs
-                // on the freshened form of the trait predicate, but the match
-                // fails for some reason that is not captured in the freshened
-                // cache key. For example, equating an impl trait ref against
-                // the placeholder trait ref may fail due the Generalizer relation
-                // raising a CyclicalTy error due to a sub_root_var relation
-                // for a variable being generalized...
-                let guar = self.infcx.dcx().span_delayed_bug(
-                    obligation.cause.span,
-                    format!(
-                        "Impl {impl_def_id:?} was matchable against {obligation:?} but now is not"
-                    ),
-                );
-                let value = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id);
-                let err = Ty::new_error(self.tcx(), guar);
-                let value = value.fold_with(&mut BottomUpFolder {
-                    tcx: self.tcx(),
-                    ty_op: |_| err,
-                    lt_op: |l| l,
-                    ct_op: |c| c,
-                });
-                Normalized { value, obligations: vec![] }
+                let predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
+                bug!("impl {impl_def_id:?} was matchable against {predicate:?} but now is not")
             }
         }
     }
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index d389933fd2d..9f715a01d44 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -105,6 +105,17 @@ pub enum AliasKind {
     Weak,
 }
 
+impl AliasKind {
+    pub fn descr(self) -> &'static str {
+        match self {
+            AliasKind::Projection => "associated type",
+            AliasKind::Inherent => "inherent associated type",
+            AliasKind::Opaque => "opaque type",
+            AliasKind::Weak => "type alias",
+        }
+    }
+}
+
 /// Defines the kinds of types used by the type system.
 ///
 /// Types written by the user start out as `hir::TyKind` and get
diff --git a/tests/ui/const-generics/occurs-check/unused-substs-2.rs b/tests/ui/const-generics/occurs-check/unused-substs-2.rs
index 84e24d1a3f5..5bdd3e39806 100644
--- a/tests/ui/const-generics/occurs-check/unused-substs-2.rs
+++ b/tests/ui/const-generics/occurs-check/unused-substs-2.rs
@@ -20,9 +20,10 @@ impl<T> Bind<T> for Foo<{ 6 + 1 }> {
 
 fn main() {
     let (mut t, foo) = Foo::bind();
+    //~^ ERROR mismatched types
+    //~| NOTE cyclic type
+
     // `t` is `ty::Infer(TyVar(?1t))`
     // `foo` contains `ty::Infer(TyVar(?1t))` in its substs
     t = foo;
-    //~^ ERROR mismatched types
-    //~| NOTE cyclic type
 }
diff --git a/tests/ui/const-generics/occurs-check/unused-substs-2.stderr b/tests/ui/const-generics/occurs-check/unused-substs-2.stderr
index 4b1b9f20559..a2c4dec4724 100644
--- a/tests/ui/const-generics/occurs-check/unused-substs-2.stderr
+++ b/tests/ui/const-generics/occurs-check/unused-substs-2.stderr
@@ -1,8 +1,8 @@
 error[E0308]: mismatched types
-  --> $DIR/unused-substs-2.rs:25:9
+  --> $DIR/unused-substs-2.rs:22:24
    |
-LL |     t = foo;
-   |         ^^^ cyclic type of infinite size
+LL |     let (mut t, foo) = Foo::bind();
+   |                        ^^^^^^^^^^^ cyclic type of infinite size
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/occurs-check/unused-substs-5.stderr b/tests/ui/const-generics/occurs-check/unused-substs-5.stderr
index 1b3a5492328..46230d455b2 100644
--- a/tests/ui/const-generics/occurs-check/unused-substs-5.stderr
+++ b/tests/ui/const-generics/occurs-check/unused-substs-5.stderr
@@ -1,10 +1,8 @@
 error[E0308]: mismatched types
-  --> $DIR/unused-substs-5.rs:15:9
+  --> $DIR/unused-substs-5.rs:15:19
    |
 LL |     x = q::<_, N>(x);
-   |         ^^^^^^^^^^^^- help: try using a conversion method: `.to_vec()`
-   |         |
-   |         cyclic type of infinite size
+   |                   ^ cyclic type of infinite size
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/impl-trait/issues/issue-62742.rs b/tests/ui/impl-trait/issues/issue-62742.rs
index 041bd0e3855..11a75737e55 100644
--- a/tests/ui/impl-trait/issues/issue-62742.rs
+++ b/tests/ui/impl-trait/issues/issue-62742.rs
@@ -3,6 +3,7 @@ use std::marker::PhantomData;
 fn _alias_check() {
     WrongImpl::foo(0i32);
     //~^ ERROR the trait bound `RawImpl<_>: Raw<_>` is not satisfied
+    //~| ERROR the trait bound `RawImpl<_>: Raw<_>` is not satisfied
     WrongImpl::<()>::foo(0i32);
     //~^ ERROR the trait bound `RawImpl<()>: Raw<()>` is not satisfied
     //~| ERROR trait bounds were not satisfied
diff --git a/tests/ui/impl-trait/issues/issue-62742.stderr b/tests/ui/impl-trait/issues/issue-62742.stderr
index 8d969e8e0f3..9ec581c231b 100644
--- a/tests/ui/impl-trait/issues/issue-62742.stderr
+++ b/tests/ui/impl-trait/issues/issue-62742.stderr
@@ -2,17 +2,32 @@ error[E0277]: the trait bound `RawImpl<_>: Raw<_>` is not satisfied
   --> $DIR/issue-62742.rs:4:5
    |
 LL |     WrongImpl::foo(0i32);
+   |     ^^^^^^^^^^^^^^^^^^^^ the trait `Raw<_>` is not implemented for `RawImpl<_>`
+   |
+   = help: the trait `Raw<[_]>` is implemented for `RawImpl<_>`
+note: required by a bound in `SafeImpl::<T, A>::foo`
+  --> $DIR/issue-62742.rs:29:20
+   |
+LL | impl<T: ?Sized, A: Raw<T>> SafeImpl<T, A> {
+   |                    ^^^^^^ required by this bound in `SafeImpl::<T, A>::foo`
+LL |     pub fn foo(value: A::Value) {}
+   |            --- required by a bound in this associated function
+
+error[E0277]: the trait bound `RawImpl<_>: Raw<_>` is not satisfied
+  --> $DIR/issue-62742.rs:4:5
+   |
+LL |     WrongImpl::foo(0i32);
    |     ^^^^^^^^^ the trait `Raw<_>` is not implemented for `RawImpl<_>`
    |
    = help: the trait `Raw<[_]>` is implemented for `RawImpl<_>`
 note: required by a bound in `SafeImpl`
-  --> $DIR/issue-62742.rs:26:35
+  --> $DIR/issue-62742.rs:27:35
    |
 LL | pub struct SafeImpl<T: ?Sized, A: Raw<T>>(PhantomData<(A, T)>);
    |                                   ^^^^^^ required by this bound in `SafeImpl`
 
 error[E0599]: the function or associated item `foo` exists for struct `SafeImpl<(), RawImpl<()>>`, but its trait bounds were not satisfied
-  --> $DIR/issue-62742.rs:6:22
+  --> $DIR/issue-62742.rs:7:22
    |
 LL |     WrongImpl::<()>::foo(0i32);
    |                      ^^^ function or associated item cannot be called on `SafeImpl<(), RawImpl<()>>` due to unsatisfied trait bounds
@@ -24,20 +39,20 @@ LL | pub struct SafeImpl<T: ?Sized, A: Raw<T>>(PhantomData<(A, T)>);
    | ----------------------------------------- function or associated item `foo` not found for this struct
    |
 note: trait bound `RawImpl<()>: Raw<()>` was not satisfied
-  --> $DIR/issue-62742.rs:28:20
+  --> $DIR/issue-62742.rs:29:20
    |
 LL | impl<T: ?Sized, A: Raw<T>> SafeImpl<T, A> {
    |                    ^^^^^^  --------------
    |                    |
    |                    unsatisfied trait bound introduced here
 note: the trait `Raw` must be implemented
-  --> $DIR/issue-62742.rs:12:1
+  --> $DIR/issue-62742.rs:13:1
    |
 LL | pub trait Raw<T: ?Sized> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0277]: the trait bound `RawImpl<()>: Raw<()>` is not satisfied
-  --> $DIR/issue-62742.rs:6:5
+  --> $DIR/issue-62742.rs:7:5
    |
 LL |     WrongImpl::<()>::foo(0i32);
    |     ^^^^^^^^^^^^^^^ the trait `Raw<()>` is not implemented for `RawImpl<()>`
@@ -45,12 +60,12 @@ LL |     WrongImpl::<()>::foo(0i32);
    = help: the trait `Raw<[()]>` is implemented for `RawImpl<()>`
    = help: for that trait implementation, expected `[()]`, found `()`
 note: required by a bound in `SafeImpl`
-  --> $DIR/issue-62742.rs:26:35
+  --> $DIR/issue-62742.rs:27:35
    |
 LL | pub struct SafeImpl<T: ?Sized, A: Raw<T>>(PhantomData<(A, T)>);
    |                                   ^^^^^^ required by this bound in `SafeImpl`
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0277, E0599.
 For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/impl-trait/issues/issue-84073.rs b/tests/ui/impl-trait/issues/issue-84073.rs
index 49a34ccfa3b..85d9d461fd9 100644
--- a/tests/ui/impl-trait/issues/issue-84073.rs
+++ b/tests/ui/impl-trait/issues/issue-84073.rs
@@ -29,5 +29,5 @@ where
 }
 
 fn main() {
-    Race::new(|race| race.when()); //~ ERROR type annotations needed
+    Race::new(|race| race.when()); //~ ERROR overflow assigning `_` to `Option<_>`
 }
diff --git a/tests/ui/impl-trait/issues/issue-84073.stderr b/tests/ui/impl-trait/issues/issue-84073.stderr
index d03e458aeb8..ab119a8a4f4 100644
--- a/tests/ui/impl-trait/issues/issue-84073.stderr
+++ b/tests/ui/impl-trait/issues/issue-84073.stderr
@@ -1,14 +1,9 @@
-error[E0282]: type annotations needed for `RaceBuilder<T, Never<T>>`
-  --> $DIR/issue-84073.rs:32:16
+error[E0275]: overflow assigning `_` to `Option<_>`
+  --> $DIR/issue-84073.rs:32:22
    |
 LL |     Race::new(|race| race.when());
-   |                ^^^^  ---- type must be known at this point
-   |
-help: consider giving this closure parameter an explicit type, where the type for type parameter `T` is specified
-   |
-LL |     Race::new(|race: RaceBuilder<T, Never<T>>| race.when());
-   |                    ++++++++++++++++++++++++++
+   |                      ^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0282`.
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/infinite/infinite-autoderef.rs b/tests/ui/infinite/infinite-autoderef.rs
index d6956e69457..ef6ba8ac87b 100644
--- a/tests/ui/infinite/infinite-autoderef.rs
+++ b/tests/ui/infinite/infinite-autoderef.rs
@@ -1,6 +1,3 @@
-//@ error-pattern: reached the recursion limit while auto-dereferencing
-//@ compile-flags: -Zdeduplicate-diagnostics=yes
-
 use std::ops::Deref;
 
 struct Foo;
@@ -17,6 +14,7 @@ pub fn main() {
     let mut x;
     loop {
         x = Box::new(x);
+        //~^ ERROR overflow assigning `Box<_>` to `_`
         x.foo;
         x.bar();
     }
diff --git a/tests/ui/infinite/infinite-autoderef.stderr b/tests/ui/infinite/infinite-autoderef.stderr
index 51b61e3a66b..7d09af9a7d4 100644
--- a/tests/ui/infinite/infinite-autoderef.stderr
+++ b/tests/ui/infinite/infinite-autoderef.stderr
@@ -1,54 +1,9 @@
-error[E0308]: mismatched types
-  --> $DIR/infinite-autoderef.rs:19:13
+error[E0275]: overflow assigning `Box<_>` to `_`
+  --> $DIR/infinite-autoderef.rs:16:13
    |
 LL |         x = Box::new(x);
-   |             ^^^^^^^^^^^ cyclic type of infinite size
-   |
-help: consider unboxing the value
-   |
-LL |         x = *Box::new(x);
-   |             +
-
-error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
-  --> $DIR/infinite-autoderef.rs:24:5
-   |
-LL |     Foo.foo;
-   |     ^^^^^^^ deref recursion limit reached
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
-
-error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
-  --> $DIR/infinite-autoderef.rs:24:9
-   |
-LL |     Foo.foo;
-   |         ^^^ deref recursion limit reached
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
-
-error[E0609]: no field `foo` on type `Foo`
-  --> $DIR/infinite-autoderef.rs:24:9
-   |
-LL |     Foo.foo;
-   |         ^^^ unknown field
-
-error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
-  --> $DIR/infinite-autoderef.rs:25:9
-   |
-LL |     Foo.bar();
-   |         ^^^ deref recursion limit reached
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
-
-error[E0599]: no method named `bar` found for struct `Foo` in the current scope
-  --> $DIR/infinite-autoderef.rs:25:9
-   |
-LL | struct Foo;
-   | ---------- method `bar` not found for this struct
-...
-LL |     Foo.bar();
-   |         ^^^ method not found in `Foo`
+   |             ^^^^^^^^^^^
 
-error: aborting due to 6 previous errors
+error: aborting due to 1 previous error
 
-Some errors have detailed explanations: E0055, E0308, E0599, E0609.
-For more information about an error, try `rustc --explain E0055`.
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature.stderr
index 8b8fc46dfc5..3dec2c3084f 100644
--- a/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature.stderr
+++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature.stderr
@@ -1,4 +1,4 @@
-error[E0275]: overflow evaluating the requirement `X2`
+error[E0275]: overflow normalizing the type alias `X2`
   --> $DIR/infinite-type-alias-mutual-recursion.rs:6:11
    |
 LL | type X1 = X2;
@@ -6,7 +6,7 @@ LL | type X1 = X2;
    |
    = note: in case this is a recursive type alias, consider using a struct, enum, or union instead
 
-error[E0275]: overflow evaluating the requirement `X3`
+error[E0275]: overflow normalizing the type alias `X3`
   --> $DIR/infinite-type-alias-mutual-recursion.rs:9:11
    |
 LL | type X2 = X3;
@@ -14,7 +14,7 @@ LL | type X2 = X3;
    |
    = note: in case this is a recursive type alias, consider using a struct, enum, or union instead
 
-error[E0275]: overflow evaluating the requirement `X1`
+error[E0275]: overflow normalizing the type alias `X1`
   --> $DIR/infinite-type-alias-mutual-recursion.rs:11:11
    |
 LL | type X3 = X1;
diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs b/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs
index 91cfe89e28a..cf9ea0db4cb 100644
--- a/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs
+++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs
@@ -5,10 +5,10 @@
 
 type X1 = X2;
 //[gated]~^ ERROR cycle detected when expanding type alias `X1`
-//[feature]~^^ ERROR: overflow evaluating the requirement `X2`
+//[feature]~^^ ERROR: overflow normalizing the type alias `X2`
 type X2 = X3;
-//[feature]~^ ERROR: overflow evaluating the requirement `X3`
+//[feature]~^ ERROR: overflow normalizing the type alias `X3`
 type X3 = X1;
-//[feature]~^ ERROR: overflow evaluating the requirement `X1`
+//[feature]~^ ERROR: overflow normalizing the type alias `X1`
 
 fn main() {}
diff --git a/tests/ui/infinite/infinite-vec-type-recursion.feature.stderr b/tests/ui/infinite/infinite-vec-type-recursion.feature.stderr
index 3aac0d7d1db..5c8d50341c1 100644
--- a/tests/ui/infinite/infinite-vec-type-recursion.feature.stderr
+++ b/tests/ui/infinite/infinite-vec-type-recursion.feature.stderr
@@ -1,4 +1,4 @@
-error[E0275]: overflow evaluating the requirement `X`
+error[E0275]: overflow normalizing the type alias `X`
   --> $DIR/infinite-vec-type-recursion.rs:6:10
    |
 LL | type X = Vec<X>;
diff --git a/tests/ui/infinite/infinite-vec-type-recursion.rs b/tests/ui/infinite/infinite-vec-type-recursion.rs
index ff270f3bf8c..c051d11d376 100644
--- a/tests/ui/infinite/infinite-vec-type-recursion.rs
+++ b/tests/ui/infinite/infinite-vec-type-recursion.rs
@@ -5,7 +5,7 @@
 
 type X = Vec<X>;
 //[gated]~^ ERROR cycle detected
-//[feature]~^^ ERROR: overflow evaluating the requirement `X`
+//[feature]~^^ ERROR: overflow normalizing the type alias `X`
 
 #[rustfmt::skip]
 fn main() { let b: X = Vec::new(); }
diff --git a/tests/ui/issues/issue-59494.rs b/tests/ui/issues/issue-59494.rs
index 8dcdd88c618..b4d50bd4ce7 100644
--- a/tests/ui/issues/issue-59494.rs
+++ b/tests/ui/issues/issue-59494.rs
@@ -18,6 +18,6 @@ fn main() {
     let f = |(_, _)| {};
     let g = |(a, _)| a;
     let t7 = |env| |a| |b| t7p(f, g)(((env, a), b));
+    //~^ ERROR mismatched types
     let t8 = t8n(t7, t7p(f, g));
-    //~^ ERROR: expected a `Fn(_)` closure, found `impl Fn(((_, _), _))` [E0277]
 }
diff --git a/tests/ui/issues/issue-59494.stderr b/tests/ui/issues/issue-59494.stderr
index 960de1be299..33d3e48c1aa 100644
--- a/tests/ui/issues/issue-59494.stderr
+++ b/tests/ui/issues/issue-59494.stderr
@@ -1,18 +1,9 @@
-error[E0277]: expected a `Fn(_)` closure, found `impl Fn(((_, _), _))`
-  --> $DIR/issue-59494.rs:21:22
+error[E0308]: mismatched types
+  --> $DIR/issue-59494.rs:20:40
    |
-LL |     let t8 = t8n(t7, t7p(f, g));
-   |              ---     ^^^^^^^^^ expected an `Fn(_)` closure, found `impl Fn(((_, _), _))`
-   |              |
-   |              required by a bound introduced by this call
-   |
-   = help: the trait `Fn<(_,)>` is not implemented for `impl Fn(((_, _), _))`
-note: required by a bound in `t8n`
-  --> $DIR/issue-59494.rs:5:45
-   |
-LL | fn t8n<A, B, C>(f: impl Fn(A) -> B, g: impl Fn(A) -> C) -> impl Fn(A) -> (B, C)
-   |                                             ^^^^^^^^^^ required by this bound in `t8n`
+LL |     let t7 = |env| |a| |b| t7p(f, g)(((env, a), b));
+   |                                        ^^^ cyclic type of infinite size
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr
index 1cace470627..2f00a877142 100644
--- a/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr
+++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr
@@ -1,4 +1,4 @@
-error[E0275]: overflow evaluating the requirement `Loop`
+error[E0275]: overflow normalizing the type alias `Loop`
   --> $DIR/inherent-impls-overflow.rs:7:13
    |
 LL | type Loop = Loop;
@@ -6,7 +6,7 @@ LL | type Loop = Loop;
    |
    = note: in case this is a recursive type alias, consider using a struct, enum, or union instead
 
-error[E0275]: overflow evaluating the requirement `Loop`
+error[E0275]: overflow normalizing the type alias `Loop`
   --> $DIR/inherent-impls-overflow.rs:9:1
    |
 LL | impl Loop {}
@@ -14,24 +14,24 @@ LL | impl Loop {}
    |
    = note: in case this is a recursive type alias, consider using a struct, enum, or union instead
 
-error[E0275]: overflow evaluating the requirement `Poly0<((((((...,),),),),),)>`
-  --> $DIR/inherent-impls-overflow.rs:11:17
+error[E0275]: overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>`
+  --> $DIR/inherent-impls-overflow.rs:13:17
    |
 LL | type Poly0<T> = Poly1<(T,)>;
    |                 ^^^^^^^^^^^
    |
    = note: in case this is a recursive type alias, consider using a struct, enum, or union instead
 
-error[E0275]: overflow evaluating the requirement `Poly1<((((((...,),),),),),)>`
-  --> $DIR/inherent-impls-overflow.rs:14:17
+error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
+  --> $DIR/inherent-impls-overflow.rs:16:17
    |
 LL | type Poly1<T> = Poly0<(T,)>;
    |                 ^^^^^^^^^^^
    |
    = note: in case this is a recursive type alias, consider using a struct, enum, or union instead
 
-error[E0275]: overflow evaluating the requirement `Poly1<((((((...,),),),),),)>`
-  --> $DIR/inherent-impls-overflow.rs:18:1
+error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
+  --> $DIR/inherent-impls-overflow.rs:20:1
    |
 LL | impl Poly0<()> {}
    | ^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr
index 1a6259b5cf9..85e8061f173 100644
--- a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr
+++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr
@@ -7,7 +7,7 @@ LL | impl Loop {}
    = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`)
 
 error[E0392]: type parameter `T` is never used
-  --> $DIR/inherent-impls-overflow.rs:11:12
+  --> $DIR/inherent-impls-overflow.rs:13:12
    |
 LL | type Poly0<T> = Poly1<(T,)>;
    |            ^ unused type parameter
@@ -16,7 +16,7 @@ LL | type Poly0<T> = Poly1<(T,)>;
    = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
 
 error[E0392]: type parameter `T` is never used
-  --> $DIR/inherent-impls-overflow.rs:14:12
+  --> $DIR/inherent-impls-overflow.rs:16:12
    |
 LL | type Poly1<T> = Poly0<(T,)>;
    |            ^ unused type parameter
@@ -25,7 +25,7 @@ LL | type Poly1<T> = Poly0<(T,)>;
    = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
 
 error[E0275]: overflow evaluating the requirement `Poly0<()> == _`
-  --> $DIR/inherent-impls-overflow.rs:18:6
+  --> $DIR/inherent-impls-overflow.rs:20:6
    |
 LL | impl Poly0<()> {}
    |      ^^^^^^^^^
diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs
index b260dedeb07..dbf5c3743e8 100644
--- a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs
+++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs
@@ -4,17 +4,21 @@
 #![feature(lazy_type_alias)]
 #![allow(incomplete_features)]
 
-type Loop = Loop; //[classic]~ ERROR overflow evaluating the requirement
+type Loop = Loop; //[classic]~ ERROR overflow normalizing the type alias `Loop`
 
-impl Loop {} //~ ERROR overflow evaluating the requirement
+impl Loop {}
+//[classic]~^ ERROR overflow normalizing the type alias `Loop`
+//[next]~^^ ERROR overflow evaluating the requirement `Loop == _`
 
 type Poly0<T> = Poly1<(T,)>;
-//[classic]~^ ERROR overflow evaluating the requirement
+//[classic]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>`
 //[next]~^^ ERROR type parameter `T` is never used
 type Poly1<T> = Poly0<(T,)>;
-//[classic]~^ ERROR overflow evaluating the requirement
+//[classic]~^ ERROR  overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
 //[next]~^^ ERROR type parameter `T` is never used
 
-impl Poly0<()> {} //~ ERROR overflow evaluating the requirement
+impl Poly0<()> {}
+//[classic]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
+//[next]~^^ ERROR overflow evaluating the requirement `Poly0<()> == _`
 
 fn main() {}
diff --git a/tests/ui/occurs-check-2.rs b/tests/ui/occurs-check-2.rs
index f36682a3dd8..1ec460a8735 100644
--- a/tests/ui/occurs-check-2.rs
+++ b/tests/ui/occurs-check-2.rs
@@ -5,6 +5,5 @@ fn main() {
 
     g = f;
     f = Box::new(g);
-    //~^  ERROR mismatched types
-    //~| cyclic type of infinite size
+    //~^ ERROR overflow assigning `Box<_>` to `_`
 }
diff --git a/tests/ui/occurs-check-2.stderr b/tests/ui/occurs-check-2.stderr
index 7b6fb9fafa2..54307a6c547 100644
--- a/tests/ui/occurs-check-2.stderr
+++ b/tests/ui/occurs-check-2.stderr
@@ -1,14 +1,9 @@
-error[E0308]: mismatched types
+error[E0275]: overflow assigning `Box<_>` to `_`
   --> $DIR/occurs-check-2.rs:7:9
    |
 LL |     f = Box::new(g);
-   |         ^^^^^^^^^^^ cyclic type of infinite size
-   |
-help: consider unboxing the value
-   |
-LL |     f = *Box::new(g);
-   |         +
+   |         ^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0308`.
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/occurs-check-3.rs b/tests/ui/occurs-check-3.rs
index 9c04204010b..377a043daf3 100644
--- a/tests/ui/occurs-check-3.rs
+++ b/tests/ui/occurs-check-3.rs
@@ -1,5 +1,11 @@
 // From Issue #778
 
 enum Clam<T> { A(T) }
-fn main() { let c; c = Clam::A(c); match c { Clam::A::<isize>(_) => { } } }
-//~^ ERROR mismatched types
+fn main() {
+    let c;
+    c = Clam::A(c);
+    //~^ ERROR overflow assigning `Clam<_>` to `_`
+    match c {
+        Clam::A::<isize>(_) => { }
+    }
+}
diff --git a/tests/ui/occurs-check-3.stderr b/tests/ui/occurs-check-3.stderr
index 675133b6d50..77b67ec1a62 100644
--- a/tests/ui/occurs-check-3.stderr
+++ b/tests/ui/occurs-check-3.stderr
@@ -1,9 +1,9 @@
-error[E0308]: mismatched types
-  --> $DIR/occurs-check-3.rs:4:24
+error[E0275]: overflow assigning `Clam<_>` to `_`
+  --> $DIR/occurs-check-3.rs:6:9
    |
-LL | fn main() { let c; c = Clam::A(c); match c { Clam::A::<isize>(_) => { } } }
-   |                        ^^^^^^^^^^ cyclic type of infinite size
+LL |     c = Clam::A(c);
+   |         ^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0308`.
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/occurs-check.rs b/tests/ui/occurs-check.rs
index aec52d83976..638b9b6d7e4 100644
--- a/tests/ui/occurs-check.rs
+++ b/tests/ui/occurs-check.rs
@@ -1,8 +1,5 @@
 fn main() {
-
     let f;
-
     f = Box::new(f);
-    //~^ ERROR mismatched types
-    //~| cyclic type of infinite size
+    //~^ ERROR overflow assigning `Box<_>` to `_`
 }
diff --git a/tests/ui/occurs-check.stderr b/tests/ui/occurs-check.stderr
index 1cb6b32cb23..30468d68cbd 100644
--- a/tests/ui/occurs-check.stderr
+++ b/tests/ui/occurs-check.stderr
@@ -1,14 +1,9 @@
-error[E0308]: mismatched types
-  --> $DIR/occurs-check.rs:5:9
+error[E0275]: overflow assigning `Box<_>` to `_`
+  --> $DIR/occurs-check.rs:3:9
    |
 LL |     f = Box::new(f);
-   |         ^^^^^^^^^^^ cyclic type of infinite size
-   |
-help: consider unboxing the value
-   |
-LL |     f = *Box::new(f);
-   |         +
+   |         ^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0308`.
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/span/coerce-suggestions.rs b/tests/ui/span/coerce-suggestions.rs
index 7920ae0b26c..13331a016fc 100644
--- a/tests/ui/span/coerce-suggestions.rs
+++ b/tests/ui/span/coerce-suggestions.rs
@@ -13,10 +13,6 @@ fn main() {
     //~^ ERROR E0308
     test2(&y);
     //~^ ERROR E0308
-    let f;
-    f = Box::new(f);
-    //~^ ERROR E0308
-
     let s = &mut String::new();
     s = format!("foo");
     //~^ ERROR E0308
diff --git a/tests/ui/span/coerce-suggestions.stderr b/tests/ui/span/coerce-suggestions.stderr
index ff840b781f0..77b01ee08b7 100644
--- a/tests/ui/span/coerce-suggestions.stderr
+++ b/tests/ui/span/coerce-suggestions.stderr
@@ -54,22 +54,11 @@ LL | fn test2(_x: &mut i32) {}
 error[E0308]: mismatched types
   --> $DIR/coerce-suggestions.rs:17:9
    |
-LL |     f = Box::new(f);
-   |         ^^^^^^^^^^^ cyclic type of infinite size
-   |
-help: consider unboxing the value
-   |
-LL |     f = *Box::new(f);
-   |         +
-
-error[E0308]: mismatched types
-  --> $DIR/coerce-suggestions.rs:21:9
-   |
 LL |     s = format!("foo");
    |         ^^^^^^^^^^^^^^ expected `&mut String`, found `String`
    |
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/traits/subtype-recursion-limit.rs b/tests/ui/traits/subtype-recursion-limit.rs
index 5804748844e..e4b08a7ec67 100644
--- a/tests/ui/traits/subtype-recursion-limit.rs
+++ b/tests/ui/traits/subtype-recursion-limit.rs
@@ -10,7 +10,7 @@ fn main() {
     let x = return;
     let y = return;
     let mut w = (x, y);
-    //~^ ERROR overflow evaluating the requirement
+    //~^ ERROR overflow assigning `_` to `*const _`
     // Avoid creating lifetimes, `Sized` bounds or function calls.
     let a = (ptr::addr_of!(y), ptr::addr_of!(x));
     w = a;
diff --git a/tests/ui/traits/subtype-recursion-limit.stderr b/tests/ui/traits/subtype-recursion-limit.stderr
index 5310f822cc3..e3587de8613 100644
--- a/tests/ui/traits/subtype-recursion-limit.stderr
+++ b/tests/ui/traits/subtype-recursion-limit.stderr
@@ -1,4 +1,4 @@
-error[E0275]: overflow evaluating the requirement `_ <: *const _`
+error[E0275]: overflow assigning `_` to `*const _`
   --> $DIR/subtype-recursion-limit.rs:12:17
    |
 LL |     let mut w = (x, y);
diff --git a/tests/ui/traits/well-formed-recursion-limit.rs b/tests/ui/traits/well-formed-recursion-limit.rs
index 056cf947d4b..770b2222580 100644
--- a/tests/ui/traits/well-formed-recursion-limit.rs
+++ b/tests/ui/traits/well-formed-recursion-limit.rs
@@ -13,8 +13,8 @@ pub fn iso_un_option<A: 'static, B: 'static>(i: ISO<Option<A>, Option<B>>) -> IS
     //~^ ERROR no field `ab` on type
     //~| ERROR no field `ba` on type
     let left = move |o_a| match o_a {
-        //~^ ERROR overflow evaluating the requirement
-        None => panic!("absured"),
+        //~^ ERROR overflow assigning `_` to `Option<_>`
+        None => panic!("absurd"),
         Some(a) => a,
     };
     let right = move |o_b| match o_b {
diff --git a/tests/ui/traits/well-formed-recursion-limit.stderr b/tests/ui/traits/well-formed-recursion-limit.stderr
index 6f5fda02315..e0270ecabbd 100644
--- a/tests/ui/traits/well-formed-recursion-limit.stderr
+++ b/tests/ui/traits/well-formed-recursion-limit.stderr
@@ -10,7 +10,7 @@ error[E0609]: no field `ba` on type `(Box<(dyn Fn(Option<A>) -> Option<B> + 'sta
 LL |     let (ab, ba) = (i.ab, i.ba);
    |                             ^^ unknown field
 
-error[E0275]: overflow evaluating the requirement `_ <: Option<_>`
+error[E0275]: overflow assigning `_` to `Option<_>`
   --> $DIR/well-formed-recursion-limit.rs:15:33
    |
 LL |     let left = move |o_a| match o_a {