about summary refs log tree commit diff
diff options
context:
space:
mode:
-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/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/traits/error_reporting/type_err_ctxt_ext.rs4
-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-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/issues/issue-59494.rs2
-rw-r--r--tests/ui/issues/issue-59494.stderr19
-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
27 files changed, 180 insertions, 280 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/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/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 7186b96b40d..be4c6b4d9d1 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
@@ -63,6 +63,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &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>,
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-84073.rs b/tests/ui/impl-trait/issues/issue-84073.rs
index 49a34ccfa3b..60f98a7c7b5 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 evaluating the requirement `_ <: Option<_>`
 }
diff --git a/tests/ui/impl-trait/issues/issue-84073.stderr b/tests/ui/impl-trait/issues/issue-84073.stderr
index d03e458aeb8..b9039211db6 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 evaluating the requirement `_ <: 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..cf5c9fe5678 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 evaluating the requirement `Box<_> <: _`
         x.foo;
         x.bar();
     }
diff --git a/tests/ui/infinite/infinite-autoderef.stderr b/tests/ui/infinite/infinite-autoderef.stderr
index 51b61e3a66b..24be1a11373 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 evaluating the requirement `Box<_> <: _`
+  --> $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/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/occurs-check-2.rs b/tests/ui/occurs-check-2.rs
index f36682a3dd8..6cdb757d65b 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 evaluating the requirement `Box<_> <: _`
 }
diff --git a/tests/ui/occurs-check-2.stderr b/tests/ui/occurs-check-2.stderr
index 7b6fb9fafa2..acc474319b0 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 evaluating the requirement `Box<_> <: _`
   --> $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..cdf8dc3bd16 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 evaluating the requirement `Clam<_> <: _`
+    match c {
+        Clam::A::<isize>(_) => { }
+    }
+}
diff --git a/tests/ui/occurs-check-3.stderr b/tests/ui/occurs-check-3.stderr
index 675133b6d50..7bc28e61d9f 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 evaluating the requirement `Clam<_> <: _`
+  --> $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..3fae3ff1238 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 evaluating the requirement `Box<_> <: _`
 }
diff --git a/tests/ui/occurs-check.stderr b/tests/ui/occurs-check.stderr
index 1cb6b32cb23..3f61d8fd08d 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 evaluating the requirement `Box<_> <: _`
+  --> $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`.