diff --git a/Cargo.lock b/Cargo.lock
index 027121774d4ec..3b8d0d87f2a9c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3552,7 +3552,6 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_trait_selection",
- "rustc_ty_utils",
  "rustc_type_ir",
  "smallvec",
  "tracing",
diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
index dd26b3da7bcf6..0761d8cdbd8f6 100644
--- a/compiler/rustc_hir_analysis/Cargo.toml
+++ b/compiler/rustc_hir_analysis/Cargo.toml
@@ -26,7 +26,6 @@ rustc_span = { path = "../rustc_span" }
 rustc_index = { path = "../rustc_index" }
 rustc_infer = { path = "../rustc_infer" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
-rustc_ty_utils = { path = "../rustc_ty_utils" }
 rustc_lint = { path = "../rustc_lint" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_type_ir = { path = "../rustc_type_ir" }
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index a2636f23a4f25..1ef5cd9d1a2a6 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -31,7 +31,6 @@ use rustc_span::{self, Span};
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
-use rustc_ty_utils::representability::{self, Representability};
 
 use std::ops::ControlFlow;
 
@@ -381,7 +380,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
     let span = tcx.def_span(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
-    check_representable(tcx, span, def_id);
+    let _ = tcx.representability(def_id);
 
     if def.repr().simd() {
         check_simd(tcx, span, def_id);
@@ -395,7 +394,7 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
     let span = tcx.def_span(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
-    check_representable(tcx, span, def_id);
+    let _ = tcx.representability(def_id);
     check_transparent(tcx, span, def);
     check_union_fields(tcx, span, def_id);
     check_packed(tcx, span, def);
@@ -1151,27 +1150,6 @@ fn check_impl_items_against_trait<'tcx>(
     }
 }
 
-/// Checks whether a type can be represented in memory. In particular, it
-/// identifies types that contain themselves without indirection through a
-/// pointer, which would mean their size is unbounded.
-pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool {
-    let rty = tcx.type_of(item_def_id);
-
-    // Check that it is possible to represent this type. This call identifies
-    // (1) types that contain themselves and (2) types that contain a different
-    // recursive type. It is only necessary to throw an error on those that
-    // contain themselves. For case 2, there must be an inner type that will be
-    // caught by case 1.
-    match representability::ty_is_representable(tcx, rty, sp, None) {
-        Representability::SelfRecursive(spans) => {
-            recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans);
-            return false;
-        }
-        Representability::Representable | Representability::ContainsRecursive => (),
-    }
-    true
-}
-
 pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
     let t = tcx.type_of(def_id);
     if let ty::Adt(def, substs) = t.kind()
@@ -1509,7 +1487,7 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
 
     detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp);
 
-    check_representable(tcx, sp, def_id);
+    let _ = tcx.representability(def_id);
     check_transparent(tcx, sp, def);
 }
 
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 04e8c9c22d159..331bd7e26c858 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -123,7 +123,6 @@ use rustc_span::{self, BytePos, Span, Symbol};
 use rustc_target::abi::VariantIdx;
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error;
 use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
 use std::cell::RefCell;
 use std::num::NonZeroU32;
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index cbe7afc8e5558..aa54b3d8ac2bc 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -25,6 +25,9 @@ macro_rules! pluralize {
     ($x:expr) => {
         if $x != 1 { "s" } else { "" }
     };
+    ("has", $x:expr) => {
+        if $x == 1 { "has" } else { "have" }
+    };
     ("is", $x:expr) => {
         if $x == 1 { "is" } else { "are" }
     };
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 466da175810d5..c4dff8b3f48ed 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -210,6 +210,7 @@ provide! { tcx, def_id, other, cdata,
     lookup_const_stability => { table }
     lookup_default_body_stability => { table }
     lookup_deprecation_entry => { table }
+    params_in_repr => { table }
     unused_generic_params => { table }
     opt_def_kind => { table_direct }
     impl_parent => { table }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 1a7a3c65c3b3f..3fc10197b9129 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1156,6 +1156,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             if let DefKind::Trait | DefKind::TraitAlias = def_kind {
                 record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id));
             }
+            if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind {
+                let params_in_repr = self.tcx.params_in_repr(def_id);
+                record!(self.tables.params_in_repr[def_id] <- params_in_repr);
+            }
             if should_encode_trait_impl_trait_tys(tcx, def_id)
                 && let Ok(table) = self.tcx.collect_trait_impl_trait_tys(def_id)
             {
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 6d7345570af85..a509ecdf7591b 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -13,7 +13,8 @@ use rustc_hir::def::{CtorKind, DefKind};
 use rustc_hir::def_id::{CrateNum, DefId, DefIndex, DefPathHash, StableCrateId};
 use rustc_hir::definitions::DefKey;
 use rustc_hir::lang_items;
-use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
+use rustc_index::bit_set::{BitSet, FiniteBitSet};
+use rustc_index::vec::IndexVec;
 use rustc_middle::metadata::ModChild;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
@@ -383,6 +384,7 @@ define_tables! {
     inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
     expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
     unused_generic_params: Table<DefIndex, LazyValue<FiniteBitSet<u32>>>,
+    params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>,
     repr_options: Table<DefIndex, LazyValue<ReprOptions>>,
     // `def_keys` and `def_path_hashes` represent a lazy version of a
     // `DefPathTable`. This allows us to avoid deserializing an entire
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 6bdf5a023b6c3..d2847e4bc12a7 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -103,6 +103,7 @@ macro_rules! arena_types {
             [] dep_kind: rustc_middle::dep_graph::DepKindStruct<'tcx>,
 
             [decode] trait_impl_trait_tys: rustc_data_structures::fx::FxHashMap<rustc_hir::def_id::DefId, rustc_middle::ty::Ty<'tcx>>,
+            [] bit_set_u32: rustc_index::bit_set::BitSet<u32>,
         ]);
     )
 }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 334eb953cbcb4..a175b1eb46733 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -301,6 +301,32 @@ rustc_queries! {
         separate_provide_extern
     }
 
+    /// Checks whether a type is representable or infinitely sized
+    query representability(_: LocalDefId) -> rustc_middle::ty::Representability {
+        desc { "checking if {:?} is representable", tcx.def_path_str(key.to_def_id()) }
+        // infinitely sized types will cause a cycle
+        cycle_delay_bug
+        // we don't want recursive representability calls to be forced with
+        // incremental compilation because, if a cycle occurs, we need the
+        // entire cycle to be in memory for diagnostics
+        anon
+    }
+
+    /// An implementation detail for the `representability` query
+    query representability_adt_ty(_: Ty<'tcx>) -> rustc_middle::ty::Representability {
+        desc { "checking if {:?} is representable", key }
+        cycle_delay_bug
+        anon
+    }
+
+    /// Set of param indexes for type params that are in the type's representation
+    query params_in_repr(key: DefId) -> rustc_index::bit_set::BitSet<u32> {
+        desc { "finding type parameters in the representation" }
+        arena_cache
+        no_hash
+        separate_provide_extern
+    }
+
     /// Fetch the THIR for a given body. If typeck for that body failed, returns an empty `Thir`.
     query thir_body(key: ty::WithOptConstParam<LocalDefId>)
         -> Result<(&'tcx Steal<thir::Thir<'tcx>>, thir::ExprId), ErrorGuaranteed>
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 3c485e2640970..80bbc8e630e02 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -566,3 +566,10 @@ impl<'tcx> AdtDef<'tcx> {
         ty::EarlyBinder(tcx.adt_sized_constraint(self.did()).0)
     }
 }
+
+#[derive(Clone, Copy, Debug)]
+#[derive(HashStable)]
+pub enum Representability {
+    Representable,
+    Infinite,
+}
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index 0257ca7b29c71..f289f2265a287 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -82,6 +82,7 @@ trivially_parameterized_over_tcx! {
     rustc_hir::def::DefKind,
     rustc_hir::def_id::DefIndex,
     rustc_hir::definitions::DefKey,
+    rustc_index::bit_set::BitSet<u32>,
     rustc_index::bit_set::FiniteBitSet<u32>,
     rustc_session::cstore::ForeignModule,
     rustc_session::cstore::LinkagePreference,
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index 7fbe9ae2a8418..bb89959b29ded 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -1,8 +1,18 @@
-use rustc_middle::ty::{self, AdtSizedConstraint, Ty, TyCtxt};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_middle::ty::Representability;
+use rustc_middle::ty::{self, AdtSizedConstraint, DefIdTree, Ty, TyCtxt};
+use rustc_query_system::query::QueryInfo;
 use rustc_query_system::Value;
+use rustc_span::def_id::LocalDefId;
+use rustc_span::Span;
+
+use std::fmt::Write;
 
 impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self {
+    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
         // SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
         // FIXME: Represent the above fact in the trait system somehow.
         unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(tcx.ty_error()) }
@@ -10,7 +20,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self {
+    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
         // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
         // FIXME: Represent the above fact in the trait system somehow.
         unsafe {
@@ -22,7 +32,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for AdtSizedConstraint<'_> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self {
+    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
         // SAFETY: This is never called when `Self` is not `AdtSizedConstraint<'tcx>`.
         // FIXME: Represent the above fact in the trait system somehow.
         unsafe {
@@ -34,7 +44,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for AdtSizedConstraint<'_> {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self {
+    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
         let err = tcx.ty_error();
         // FIXME(compiler-errors): It would be nice if we could get the
         // query key, so we could at least generate a fn signature that
@@ -52,3 +62,153 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
         unsafe { std::mem::transmute::<ty::PolyFnSig<'tcx>, ty::Binder<'_, ty::FnSig<'_>>>(fn_sig) }
     }
 }
+
+impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
+    fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo]) -> Self {
+        let mut item_and_field_ids = Vec::new();
+        let mut representable_ids = FxHashSet::default();
+        for info in cycle {
+            if info.query.name == "representability"
+                && let Some(field_id) = info.query.def_id
+                && let Some(field_id) = field_id.as_local()
+                && let Some(DefKind::Field) = info.query.def_kind
+            {
+                let parent_id = tcx.parent(field_id.to_def_id());
+                let item_id = match tcx.def_kind(parent_id) {
+                    DefKind::Variant => tcx.parent(parent_id),
+                    _ => parent_id,
+                };
+                item_and_field_ids.push((item_id.expect_local(), field_id));
+            }
+        }
+        for info in cycle {
+            if info.query.name == "representability_adt_ty"
+                && let Some(def_id) = info.query.ty_adt_id
+                && let Some(def_id) = def_id.as_local()
+                && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
+            {
+                representable_ids.insert(def_id);
+            }
+        }
+        recursive_type_error(tcx, item_and_field_ids, &representable_ids);
+        Representability::Infinite
+    }
+}
+
+// item_and_field_ids should form a cycle where each field contains the
+// type in the next element in the list
+pub fn recursive_type_error(
+    tcx: TyCtxt<'_>,
+    mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
+    representable_ids: &FxHashSet<LocalDefId>,
+) {
+    const ITEM_LIMIT: usize = 5;
+
+    // Rotate the cycle so that the item with the lowest span is first
+    let start_index = item_and_field_ids
+        .iter()
+        .enumerate()
+        .min_by_key(|&(_, &(id, _))| tcx.def_span(id))
+        .unwrap()
+        .0;
+    item_and_field_ids.rotate_left(start_index);
+
+    let cycle_len = item_and_field_ids.len();
+    let show_cycle_len = cycle_len.min(ITEM_LIMIT);
+
+    let mut err_span = MultiSpan::from_spans(
+        item_and_field_ids[..show_cycle_len]
+            .iter()
+            .map(|(id, _)| tcx.def_span(id.to_def_id()))
+            .collect(),
+    );
+    let mut suggestion = Vec::with_capacity(show_cycle_len * 2);
+    for i in 0..show_cycle_len {
+        let (_, field_id) = item_and_field_ids[i];
+        let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len];
+        // Find the span(s) that contain the next item in the cycle
+        let hir_id = tcx.hir().local_def_id_to_hir_id(field_id);
+        let hir::Node::Field(field) = tcx.hir().get(hir_id) else { bug!("expected field") };
+        let mut found = Vec::new();
+        find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids);
+
+        // Couldn't find the type. Maybe it's behind a type alias?
+        // In any case, we'll just suggest boxing the whole field.
+        if found.is_empty() {
+            found.push(field.ty.span);
+        }
+
+        for span in found {
+            err_span.push_span_label(span, "recursive without indirection");
+            // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
+            suggestion.push((span.shrink_to_lo(), "Box<".to_string()));
+            suggestion.push((span.shrink_to_hi(), ">".to_string()));
+        }
+    }
+    let items_list = {
+        let mut s = String::new();
+        for (i, (item_id, _)) in item_and_field_ids.iter().enumerate() {
+            let path = tcx.def_path_str(item_id.to_def_id());
+            write!(&mut s, "`{path}`").unwrap();
+            if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT {
+                write!(&mut s, " and {} more", cycle_len - 5).unwrap();
+                break;
+            }
+            if cycle_len > 1 && i < cycle_len - 2 {
+                s.push_str(", ");
+            } else if cycle_len > 1 && i == cycle_len - 2 {
+                s.push_str(" and ")
+            }
+        }
+        s
+    };
+    let mut err = struct_span_err!(
+        tcx.sess,
+        err_span,
+        E0072,
+        "recursive type{} {} {} infinite size",
+        pluralize!(cycle_len),
+        items_list,
+        pluralize!("has", cycle_len),
+    );
+    err.multipart_suggestion(
+        "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle",
+        suggestion,
+        Applicability::HasPlaceholders,
+    );
+    err.emit();
+}
+
+fn find_item_ty_spans(
+    tcx: TyCtxt<'_>,
+    ty: &hir::Ty<'_>,
+    needle: LocalDefId,
+    spans: &mut Vec<Span>,
+    seen_representable: &FxHashSet<LocalDefId>,
+) {
+    match ty.kind {
+        hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
+            if let Some(def_id) = path.res.opt_def_id() {
+                let check_params = def_id.as_local().map_or(true, |def_id| {
+                    if def_id == needle {
+                        spans.push(ty.span);
+                    }
+                    seen_representable.contains(&def_id)
+                });
+                if check_params && let Some(args) = path.segments.last().unwrap().args {
+                    let params_in_repr = tcx.params_in_repr(def_id);
+                    for (i, arg) in args.args.iter().enumerate() {
+                        if let hir::GenericArg::Type(ty) = arg && params_in_repr.contains(i as u32) {
+                            find_item_ty_spans(tcx, ty, needle, spans, seen_representable);
+                        }
+                    }
+                }
+            }
+        }
+        hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable),
+        hir::TyKind::Tup(tys) => {
+            tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable))
+        }
+        _ => {}
+    }
+}
diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs
index cdbf734cdbe18..8be2e2be86b58 100644
--- a/compiler/rustc_query_impl/src/keys.rs
+++ b/compiler/rustc_query_impl/src/keys.rs
@@ -27,6 +27,10 @@ pub trait Key {
     fn key_as_def_id(&self) -> Option<DefId> {
         None
     }
+
+    fn ty_adt_id(&self) -> Option<DefId> {
+        None
+    }
 }
 
 impl Key for () {
@@ -407,6 +411,12 @@ impl<'tcx> Key for Ty<'tcx> {
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
+    fn ty_adt_id(&self) -> Option<DefId> {
+        match self.kind() {
+            ty::Adt(adt, _) => Some(adt.did()),
+            _ => None,
+        }
+    }
 }
 
 impl<'tcx> Key for TyAndLayout<'tcx> {
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 2b3850bc0dfb5..aaeaa3bd51d63 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -318,13 +318,12 @@ pub(crate) fn create_query_frame<
     } else {
         Some(key.default_span(*tcx))
     };
+    let def_id = key.key_as_def_id();
     let def_kind = if kind == dep_graph::DepKind::opt_def_kind {
         // Try to avoid infinite recursion.
         None
     } else {
-        key.key_as_def_id()
-            .and_then(|def_id| def_id.as_local())
-            .and_then(|def_id| tcx.opt_def_kind(def_id))
+        def_id.and_then(|def_id| def_id.as_local()).and_then(|def_id| tcx.opt_def_kind(def_id))
     };
     let hash = || {
         tcx.with_stable_hashing_context(|mut hcx| {
@@ -334,8 +333,9 @@ pub(crate) fn create_query_frame<
             hasher.finish::<u64>()
         })
     };
+    let ty_adt_id = key.ty_adt_id();
 
-    QueryStackFrame::new(name, description, span, def_kind, hash)
+    QueryStackFrame::new(name, description, span, def_id, def_kind, ty_adt_id, hash)
 }
 
 fn try_load_from_on_disk_cache<'tcx, Q>(tcx: TyCtxt<'tcx>, dep_node: DepNode)
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 64aba4703ca3a..ed65393f57e4d 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -551,7 +551,7 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
 #[cold]
 pub(crate) fn report_cycle<'a>(
     sess: &'a Session,
-    CycleError { usage, cycle: stack }: CycleError,
+    CycleError { usage, cycle: stack }: &CycleError,
 ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
     assert!(!stack.is_empty());
 
@@ -569,10 +569,10 @@ pub(crate) fn report_cycle<'a>(
     }
 
     let mut cycle_usage = None;
-    if let Some((span, query)) = usage {
+    if let Some((span, ref query)) = *usage {
         cycle_usage = Some(crate::error::CycleUsage {
             span: query.default_span(span),
-            usage: query.description,
+            usage: query.description.to_string(),
         });
     }
 
diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs
index 7a96c53b60481..118703fc0d48c 100644
--- a/compiler/rustc_query_system/src/query/mod.rs
+++ b/compiler/rustc_query_system/src/query/mod.rs
@@ -18,6 +18,7 @@ use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
 use rustc_data_structures::sync::Lock;
 use rustc_errors::Diagnostic;
 use rustc_hir::def::DefKind;
+use rustc_span::def_id::DefId;
 use rustc_span::Span;
 use thin_vec::ThinVec;
 
@@ -29,7 +30,9 @@ pub struct QueryStackFrame {
     pub name: &'static str,
     pub description: String,
     span: Option<Span>,
-    def_kind: Option<DefKind>,
+    pub def_id: Option<DefId>,
+    pub def_kind: Option<DefKind>,
+    pub ty_adt_id: Option<DefId>,
     /// This hash is used to deterministically pick
     /// a query to remove cycles in the parallel compiler.
     #[cfg(parallel_compiler)]
@@ -42,14 +45,18 @@ impl QueryStackFrame {
         name: &'static str,
         description: String,
         span: Option<Span>,
+        def_id: Option<DefId>,
         def_kind: Option<DefKind>,
+        ty_adt_id: Option<DefId>,
         _hash: impl FnOnce() -> u64,
     ) -> Self {
         Self {
             name,
             description,
             span,
+            def_id,
             def_kind,
+            ty_adt_id,
             #[cfg(parallel_compiler)]
             hash: _hash(),
         }
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 8179a674afaec..15b89daa6db11 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -119,7 +119,7 @@ where
 #[inline(never)]
 fn mk_cycle<CTX, V, R>(
     tcx: CTX,
-    error: CycleError,
+    cycle_error: CycleError,
     handler: HandleCycleError,
     cache: &dyn crate::query::QueryStorage<Value = V, Stored = R>,
 ) -> R
@@ -128,13 +128,14 @@ where
     V: std::fmt::Debug + Value<CTX::DepContext>,
     R: Clone,
 {
-    let error = report_cycle(tcx.dep_context().sess(), error);
-    let value = handle_cycle_error(*tcx.dep_context(), error, handler);
+    let error = report_cycle(tcx.dep_context().sess(), &cycle_error);
+    let value = handle_cycle_error(*tcx.dep_context(), &cycle_error, error, handler);
     cache.store_nocache(value)
 }
 
 fn handle_cycle_error<CTX, V>(
     tcx: CTX,
+    cycle_error: &CycleError,
     mut error: DiagnosticBuilder<'_, ErrorGuaranteed>,
     handler: HandleCycleError,
 ) -> V
@@ -146,7 +147,7 @@ where
     match handler {
         Error => {
             error.emit();
-            Value::from_cycle_error(tcx)
+            Value::from_cycle_error(tcx, &cycle_error.cycle)
         }
         Fatal => {
             error.emit();
@@ -155,7 +156,7 @@ where
         }
         DelayBug => {
             error.delay_as_bug();
-            Value::from_cycle_error(tcx)
+            Value::from_cycle_error(tcx, &cycle_error.cycle)
         }
     }
 }
diff --git a/compiler/rustc_query_system/src/values.rs b/compiler/rustc_query_system/src/values.rs
index aeef66f86dacc..67fbf14e61299 100644
--- a/compiler/rustc_query_system/src/values.rs
+++ b/compiler/rustc_query_system/src/values.rs
@@ -1,11 +1,12 @@
 use crate::dep_graph::DepContext;
+use crate::query::QueryInfo;
 
 pub trait Value<CTX: DepContext>: Sized {
-    fn from_cycle_error(tcx: CTX) -> Self;
+    fn from_cycle_error(tcx: CTX, cycle: &[QueryInfo]) -> Self;
 }
 
 impl<CTX: DepContext, T> Value<CTX> for T {
-    default fn from_cycle_error(tcx: CTX) -> T {
+    default fn from_cycle_error(tcx: CTX, _: &[QueryInfo]) -> T {
         tcx.sess().abort_if_errors();
         // Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's
         // non-trivial to define it earlier.
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 6dcf9c4d26174..8afe20bf2298f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -2741,82 +2741,6 @@ impl<'v> Visitor<'v> for FindTypeParam {
     }
 }
 
-pub fn recursive_type_with_infinite_size_error<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    type_def_id: DefId,
-    spans: Vec<(Span, Option<hir::HirId>)>,
-) {
-    assert!(type_def_id.is_local());
-    let span = tcx.def_span(type_def_id);
-    let path = tcx.def_path_str(type_def_id);
-    let mut err =
-        struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size", path);
-    err.span_label(span, "recursive type has infinite size");
-    for &(span, _) in &spans {
-        err.span_label(span, "recursive without indirection");
-    }
-    let msg = format!(
-        "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `{}` representable",
-        path,
-    );
-    if spans.len() <= 4 {
-        // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
-        err.multipart_suggestion(
-            &msg,
-            spans
-                .into_iter()
-                .flat_map(|(span, field_id)| {
-                    if let Some(generic_span) = get_option_generic_from_field_id(tcx, field_id) {
-                        // If we match an `Option` and can grab the span of the Option's generic, then
-                        // suggest boxing the generic arg for a non-null niche optimization.
-                        vec![
-                            (generic_span.shrink_to_lo(), "Box<".to_string()),
-                            (generic_span.shrink_to_hi(), ">".to_string()),
-                        ]
-                    } else {
-                        vec![
-                            (span.shrink_to_lo(), "Box<".to_string()),
-                            (span.shrink_to_hi(), ">".to_string()),
-                        ]
-                    }
-                })
-                .collect(),
-            Applicability::HasPlaceholders,
-        );
-    } else {
-        err.help(&msg);
-    }
-    err.emit();
-}
-
-/// Extract the span for the generic type `T` of `Option<T>` in a field definition
-fn get_option_generic_from_field_id(tcx: TyCtxt<'_>, field_id: Option<hir::HirId>) -> Option<Span> {
-    let node = tcx.hir().find(field_id?);
-
-    // Expect a field from our field_id
-    let Some(hir::Node::Field(field_def)) = node
-        else { bug!("Expected HirId corresponding to FieldDef, found: {:?}", node) };
-
-    // Match a type that is a simple QPath with no Self
-    let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = &field_def.ty.kind
-        else { return None };
-
-    // Check if the path we're checking resolves to Option
-    let hir::def::Res::Def(_, did) = path.res
-        else { return None };
-
-    // Bail if this path doesn't describe `::core::option::Option`
-    if !tcx.is_diagnostic_item(sym::Option, did) {
-        return None;
-    }
-
-    // Match a single generic arg in the 0th path segment
-    let generic_arg = path.segments.last()?.args?.args.get(0)?;
-
-    // Take the span out of the type, if it's a type
-    if let hir::GenericArg::Type(generic_ty) = generic_arg { Some(generic_ty.span) } else { None }
-}
-
 /// Summarizes information
 #[derive(Clone)]
 pub enum ArgKind {
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 0ddc154fbebbf..cce5a79ddc855 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -39,6 +39,7 @@ pub fn provide(providers: &mut Providers) {
     implied_bounds::provide(providers);
     layout::provide(providers);
     needs_drop::provide(providers);
+    representability::provide(providers);
     ty::provide(providers);
     instance::provide(providers);
 }
diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs
index eded7891682ea..7f48fb804178d 100644
--- a/compiler/rustc_ty_utils/src/representability.rs
+++ b/compiler/rustc_ty_utils/src/representability.rs
@@ -1,386 +1,119 @@
-//! Check whether a type is representable.
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir as hir;
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::Span;
-use std::cmp;
+#![allow(rustc::untranslatable_diagnostic, rustc::diagnostic_outside_of_impl)]
 
-/// Describes whether a type is representable. For types that are not
-/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to
-/// distinguish between types that are recursive with themselves and types that
-/// contain a different recursive type. These cases can therefore be treated
-/// differently when reporting errors.
-///
-/// The ordering of the cases is significant. They are sorted so that cmp::max
-/// will keep the "more erroneous" of two values.
-#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
-pub enum Representability {
-    Representable,
-    ContainsRecursive,
-    /// Return a list of types that are included in themselves:
-    /// the spans where they are self-included, and (if found)
-    /// the HirId of the FieldDef that defines the self-inclusion.
-    SelfRecursive(Vec<(Span, Option<hir::HirId>)>),
-}
+use rustc_hir::def::DefKind;
+use rustc_index::bit_set::BitSet;
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::{self, Representability, Ty, TyCtxt};
+use rustc_span::def_id::{DefId, LocalDefId};
 
-/// Check whether a type is representable. This means it cannot contain unboxed
-/// structural recursion. This check is needed for structs and enums.
-pub fn ty_is_representable<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty: Ty<'tcx>,
-    sp: Span,
-    field_id: Option<hir::HirId>,
-) -> Representability {
-    debug!("is_type_representable: {:?}", ty);
-    // To avoid a stack overflow when checking an enum variant or struct that
-    // contains a different, structurally recursive type, maintain a stack of
-    // seen types and check recursion for each of them (issues #3008, #3779,
-    // #74224, #84611). `shadow_seen` contains the full stack and `seen` only
-    // the one for the current type (e.g. if we have structs A and B, B contains
-    // a field of type A, and we're currently looking at B, then `seen` will be
-    // cleared when recursing to check A, but `shadow_seen` won't, so that we
-    // can catch cases of mutual recursion where A also contains B).
-    let mut seen: Vec<Ty<'_>> = Vec::new();
-    let mut shadow_seen: Vec<ty::AdtDef<'tcx>> = Vec::new();
-    let mut representable_cache = FxHashMap::default();
-    let mut force_result = false;
-    let r = is_type_structurally_recursive(
-        tcx,
-        &mut seen,
-        &mut shadow_seen,
-        &mut representable_cache,
-        ty,
-        sp,
-        field_id,
-        &mut force_result,
-    );
-    debug!("is_type_representable: {:?} is {:?}", ty, r);
-    r
+pub fn provide(providers: &mut Providers) {
+    *providers =
+        Providers { representability, representability_adt_ty, params_in_repr, ..*providers };
 }
 
-// Iterate until something non-representable is found
-fn fold_repr<It: Iterator<Item = Representability>>(iter: It) -> Representability {
-    iter.fold(Representability::Representable, |r1, r2| match (r1, r2) {
-        (Representability::SelfRecursive(v1), Representability::SelfRecursive(v2)) => {
-            Representability::SelfRecursive(v1.into_iter().chain(v2).collect())
+macro_rules! rtry {
+    ($e:expr) => {
+        match $e {
+            e @ Representability::Infinite => return e,
+            Representability::Representable => {}
         }
-        (r1, r2) => cmp::max(r1, r2),
-    })
+    };
 }
 
-fn are_inner_types_recursive<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    seen: &mut Vec<Ty<'tcx>>,
-    shadow_seen: &mut Vec<ty::AdtDef<'tcx>>,
-    representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
-    ty: Ty<'tcx>,
-    sp: Span,
-    field_id: Option<hir::HirId>,
-    force_result: &mut bool,
-) -> Representability {
-    debug!("are_inner_types_recursive({:?}, {:?}, {:?})", ty, seen, shadow_seen);
-    match ty.kind() {
-        ty::Tuple(fields) => {
-            // Find non representable
-            fold_repr(fields.iter().map(|ty| {
-                is_type_structurally_recursive(
-                    tcx,
-                    seen,
-                    shadow_seen,
-                    representable_cache,
-                    ty,
-                    sp,
-                    field_id,
-                    force_result,
-                )
-            }))
-        }
-        // Fixed-length vectors.
-        // FIXME(#11924) Behavior undecided for zero-length vectors.
-        ty::Array(ty, _) => is_type_structurally_recursive(
-            tcx,
-            seen,
-            shadow_seen,
-            representable_cache,
-            *ty,
-            sp,
-            field_id,
-            force_result,
-        ),
-        ty::Adt(def, substs) => {
-            // Find non representable fields with their spans
-            fold_repr(def.all_fields().map(|field| {
-                let ty = field.ty(tcx, substs);
-                let (sp, field_id) = match field
-                    .did
-                    .as_local()
-                    .map(|id| tcx.hir().local_def_id_to_hir_id(id))
-                    .and_then(|id| tcx.hir().find(id))
-                {
-                    Some(hir::Node::Field(field)) => (field.ty.span, Some(field.hir_id)),
-                    _ => (sp, field_id),
-                };
-
-                let mut result = None;
-
-                // First, we check whether the field type per se is representable.
-                // This catches cases as in #74224 and #84611. There is a special
-                // case related to mutual recursion, though; consider this example:
-                //
-                //   struct A<T> {
-                //       z: T,
-                //       x: B<T>,
-                //   }
-                //
-                //   struct B<T> {
-                //       y: A<T>
-                //   }
-                //
-                // Here, without the following special case, both A and B are
-                // ContainsRecursive, which is a problem because we only report
-                // errors for SelfRecursive. We fix this by detecting this special
-                // case (shadow_seen.first() is the type we are originally
-                // interested in, and if we ever encounter the same AdtDef again,
-                // we know that it must be SelfRecursive) and "forcibly" returning
-                // SelfRecursive (by setting force_result, which tells the calling
-                // invocations of are_inner_types_representable to forward the
-                // result without adjusting).
-                if shadow_seen.len() > seen.len() && shadow_seen.first() == Some(def) {
-                    *force_result = true;
-                    result = Some(Representability::SelfRecursive(vec![(sp, field_id)]));
-                }
-
-                if result == None {
-                    result = Some(Representability::Representable);
-
-                    // Now, we check whether the field types per se are representable, e.g.
-                    // for struct Foo { x: Option<Foo> }, we first check whether Option<_>
-                    // by itself is representable (which it is), and the nesting of Foo
-                    // will be detected later. This is necessary for #74224 and #84611.
-
-                    // If we have encountered an ADT definition that we have not seen
-                    // before (no need to check them twice), recurse to see whether that
-                    // definition is SelfRecursive. If so, we must be ContainsRecursive.
-                    if shadow_seen.len() > 1
-                        && !shadow_seen
-                            .iter()
-                            .take(shadow_seen.len() - 1)
-                            .any(|seen_def| seen_def == def)
-                    {
-                        let adt_def_id = def.did();
-                        let raw_adt_ty = tcx.type_of(adt_def_id);
-                        debug!("are_inner_types_recursive: checking nested type: {:?}", raw_adt_ty);
-
-                        // Check independently whether the ADT is SelfRecursive. If so,
-                        // we must be ContainsRecursive (except for the special case
-                        // mentioned above).
-                        let mut nested_seen: Vec<Ty<'_>> = vec![];
-                        result = Some(
-                            match is_type_structurally_recursive(
-                                tcx,
-                                &mut nested_seen,
-                                shadow_seen,
-                                representable_cache,
-                                raw_adt_ty,
-                                sp,
-                                field_id,
-                                force_result,
-                            ) {
-                                Representability::SelfRecursive(_) => {
-                                    if *force_result {
-                                        Representability::SelfRecursive(vec![(sp, field_id)])
-                                    } else {
-                                        Representability::ContainsRecursive
-                                    }
-                                }
-                                x => x,
-                            },
-                        );
-                    }
-
-                    // We only enter the following block if the type looks representable
-                    // so far. This is necessary for cases such as this one (#74224):
-                    //
-                    //   struct A<T> {
-                    //       x: T,
-                    //       y: A<A<T>>,
-                    //   }
-                    //
-                    //   struct B {
-                    //       z: A<usize>
-                    //   }
-                    //
-                    // When checking B, we recurse into A and check field y of type
-                    // A<A<usize>>. We haven't seen this exact type before, so we recurse
-                    // into A<A<usize>>, which contains, A<A<A<usize>>>, and so forth,
-                    // ad infinitum. We can prevent this from happening by first checking
-                    // A separately (the code above) and only checking for nested Bs if
-                    // A actually looks representable (which it wouldn't in this example).
-                    if result == Some(Representability::Representable) {
-                        // Now, even if the type is representable (e.g. Option<_>),
-                        // it might still contribute to a recursive type, e.g.:
-                        //   struct Foo { x: Option<Option<Foo>> }
-                        // These cases are handled by passing the full `seen`
-                        // stack to is_type_structurally_recursive (instead of the
-                        // empty `nested_seen` above):
-                        result = Some(
-                            match is_type_structurally_recursive(
-                                tcx,
-                                seen,
-                                shadow_seen,
-                                representable_cache,
-                                ty,
-                                sp,
-                                field_id,
-                                force_result,
-                            ) {
-                                Representability::SelfRecursive(_) => {
-                                    Representability::SelfRecursive(vec![(sp, field_id)])
-                                }
-                                x => x,
-                            },
-                        );
-                    }
+fn representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability {
+    match tcx.def_kind(def_id) {
+        DefKind::Struct | DefKind::Union | DefKind::Enum => {
+            let adt_def = tcx.adt_def(def_id);
+            for variant in adt_def.variants() {
+                for field in variant.fields.iter() {
+                    rtry!(tcx.representability(field.did.expect_local()));
                 }
-
-                result.unwrap()
-            }))
-        }
-        ty::Closure(..) => {
-            // this check is run on type definitions, so we don't expect
-            // to see closure types
-            bug!("requires check invoked on inapplicable type: {:?}", ty)
+            }
+            Representability::Representable
         }
-        _ => Representability::Representable,
+        DefKind::Field => representability_ty(tcx, tcx.type_of(def_id)),
+        def_kind => bug!("unexpected {def_kind:?}"),
     }
 }
 
-fn same_adt<'tcx>(ty: Ty<'tcx>, def: ty::AdtDef<'tcx>) -> bool {
+fn representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability {
     match *ty.kind() {
-        ty::Adt(ty_def, _) => ty_def == def,
-        _ => false,
+        ty::Adt(..) => tcx.representability_adt_ty(ty),
+        // FIXME(#11924) allow zero-length arrays?
+        ty::Array(ty, _) => representability_ty(tcx, ty),
+        ty::Tuple(tys) => {
+            for ty in tys {
+                rtry!(representability_ty(tcx, ty));
+            }
+            Representability::Representable
+        }
+        _ => Representability::Representable,
     }
 }
 
-// Does the type `ty` directly (without indirection through a pointer)
-// contain any types on stack `seen`?
-fn is_type_structurally_recursive<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    seen: &mut Vec<Ty<'tcx>>,
-    shadow_seen: &mut Vec<ty::AdtDef<'tcx>>,
-    representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
-    ty: Ty<'tcx>,
-    sp: Span,
-    field_id: Option<hir::HirId>,
-    force_result: &mut bool,
-) -> Representability {
-    debug!("is_type_structurally_recursive: {:?} {:?} {:?}", ty, sp, field_id);
-    if let Some(representability) = representable_cache.get(&ty) {
-        debug!(
-            "is_type_structurally_recursive: {:?} {:?} {:?} - (cached) {:?}",
-            ty, sp, field_id, representability
-        );
-        return representability.clone();
+/*
+The reason for this being a separate query is very subtle:
+Consider this infinitely sized struct: `struct Foo(Box<Foo>, Bar<Foo>)`:
+When calling representability(Foo), a query cycle will occur:
+  representability(Foo)
+    -> representability_adt_ty(Bar<Foo>)
+    -> representability(Foo)
+For the diagnostic output (in `Value::from_cycle_error`), we want to detect that
+the `Foo` in the *second* field of the struct is culpable. This requires
+traversing the HIR of the struct and calling `params_in_repr(Bar)`. But we can't
+call params_in_repr for a given type unless it is known to be representable.
+params_in_repr will cycle/panic on infinitely sized types. Looking at the query
+cycle above, we know that `Bar` is representable because
+representability_adt_ty(Bar<..>) is in the cycle and representability(Bar) is
+*not* in the cycle.
+*/
+fn representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability {
+    let ty::Adt(adt, substs) = ty.kind() else { bug!("expected adt") };
+    if let Some(def_id) = adt.did().as_local() {
+        rtry!(tcx.representability(def_id));
     }
-
-    let representability = is_type_structurally_recursive_inner(
-        tcx,
-        seen,
-        shadow_seen,
-        representable_cache,
-        ty,
-        sp,
-        field_id,
-        force_result,
-    );
-
-    representable_cache.insert(ty, representability.clone());
-    representability
+    // At this point, we know that the item of the ADT type is representable;
+    // but the type parameters may cause a cycle with an upstream type
+    let params_in_repr = tcx.params_in_repr(adt.did());
+    for (i, subst) in substs.iter().enumerate() {
+        if let ty::GenericArgKind::Type(ty) = subst.unpack() {
+            if params_in_repr.contains(i as u32) {
+                rtry!(representability_ty(tcx, ty));
+            }
+        }
+    }
+    Representability::Representable
 }
 
-fn is_type_structurally_recursive_inner<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    seen: &mut Vec<Ty<'tcx>>,
-    shadow_seen: &mut Vec<ty::AdtDef<'tcx>>,
-    representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
-    ty: Ty<'tcx>,
-    sp: Span,
-    field_id: Option<hir::HirId>,
-    force_result: &mut bool,
-) -> Representability {
-    match ty.kind() {
-        ty::Adt(def, _) => {
-            {
-                debug!("is_type_structurally_recursive_inner: adt: {:?}, seen: {:?}", ty, seen);
-
-                // Iterate through stack of previously seen types.
-                let mut iter = seen.iter();
-
-                // The first item in `seen` is the type we are actually curious about.
-                // We want to return SelfRecursive if this type contains itself.
-                // It is important that we DON'T take generic parameters into account
-                // for this check, so that Bar<T> in this example counts as SelfRecursive:
-                //
-                // struct Foo;
-                // struct Bar<T> { x: Bar<Foo> }
-
-                if let Some(&seen_adt) = iter.next() {
-                    if same_adt(seen_adt, *def) {
-                        debug!("SelfRecursive: {:?} contains {:?}", seen_adt, ty);
-                        return Representability::SelfRecursive(vec![(sp, field_id)]);
-                    }
-                }
-
-                // We also need to know whether the first item contains other types
-                // that are structurally recursive. If we don't catch this case, we
-                // will recurse infinitely for some inputs.
-                //
-                // It is important that we DO take generic parameters into account
-                // here, because nesting e.g. Options is allowed (as long as the
-                // definition of Option doesn't itself include an Option field, which
-                // would be a case of SelfRecursive above). The following, too, counts
-                // as SelfRecursive:
-                //
-                // struct Foo { Option<Option<Foo>> }
+fn params_in_repr(tcx: TyCtxt<'_>, def_id: DefId) -> BitSet<u32> {
+    let adt_def = tcx.adt_def(def_id);
+    let generics = tcx.generics_of(def_id);
+    let mut params_in_repr = BitSet::new_empty(generics.params.len());
+    for variant in adt_def.variants() {
+        for field in variant.fields.iter() {
+            params_in_repr_ty(tcx, tcx.type_of(field.did), &mut params_in_repr);
+        }
+    }
+    params_in_repr
+}
 
-                for &seen_adt in iter {
-                    if ty == seen_adt {
-                        debug!("ContainsRecursive: {:?} contains {:?}", seen_adt, ty);
-                        return Representability::ContainsRecursive;
+fn params_in_repr_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, params_in_repr: &mut BitSet<u32>) {
+    match *ty.kind() {
+        ty::Adt(adt, substs) => {
+            let inner_params_in_repr = tcx.params_in_repr(adt.did());
+            for (i, subst) in substs.iter().enumerate() {
+                if let ty::GenericArgKind::Type(ty) = subst.unpack() {
+                    if inner_params_in_repr.contains(i as u32) {
+                        params_in_repr_ty(tcx, ty, params_in_repr);
                     }
                 }
             }
-
-            // For structs and enums, track all previously seen types by pushing them
-            // onto the 'seen' stack.
-            seen.push(ty);
-            shadow_seen.push(*def);
-            let out = are_inner_types_recursive(
-                tcx,
-                seen,
-                shadow_seen,
-                representable_cache,
-                ty,
-                sp,
-                field_id,
-                force_result,
-            );
-            shadow_seen.pop();
-            seen.pop();
-            out
         }
-        _ => {
-            // No need to push in other cases.
-            are_inner_types_recursive(
-                tcx,
-                seen,
-                shadow_seen,
-                representable_cache,
-                ty,
-                sp,
-                field_id,
-                force_result,
-            )
+        ty::Array(ty, _) => params_in_repr_ty(tcx, ty, params_in_repr),
+        ty::Tuple(tys) => tys.iter().for_each(|ty| params_in_repr_ty(tcx, ty, params_in_repr)),
+        ty::Param(param) => {
+            params_in_repr.insert(param.index);
         }
+        _ => {}
     }
 }
diff --git a/src/test/incremental/issue-61323.rs b/src/test/incremental/issue-61323.rs
index 97cbfe408f261..7ce47947cbb0c 100644
--- a/src/test/incremental/issue-61323.rs
+++ b/src/test/incremental/issue-61323.rs
@@ -1,7 +1,7 @@
 // revisions: rpass cfail
 
 enum A {
-    //[cfail]~^ ERROR 3:1: 3:7: recursive type `A` has infinite size [E0072]
+    //[cfail]~^ ERROR 3:1: 3:7: recursive types `A` and `C` have infinite size [E0072]
     B(C),
 }
 
@@ -10,6 +10,5 @@ struct C(Box<A>);
 
 #[cfg(cfail)]
 struct C(A);
-//[cfail]~^ ERROR 12:1: 12:9: recursive type `C` has infinite size [E0072]
 
 fn main() {}
diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr
index aa39d26feed1e..e6ab67d59ce58 100644
--- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr
+++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `DEF_ID` has infinite size
   --> $DIR/infinite-recursive-type-impl-trait-return.rs:7:5
    |
 LL |     enum E {
-   |     ^^^^^^ recursive type has infinite size
+   |     ^^^^^^
 LL |
 LL |         This(E),
    |              - recursive without indirection
    |
-help: insert some indirection (e.g., a `DEF_ID` representable
+help: insert some indirection (e.g., a `DEF_ID`) to break the cycle
    |
 LL |         This(Box<E>),
    |              ++++ +
diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr
index 009bedec5ed6a..165ff67837244 100644
--- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr
+++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `f::E` has infinite size
   --> $DIR/infinite-recursive-type-impl-trait.rs:2:5
    |
 LL |     enum E {
-   |     ^^^^^^ recursive type has infinite size
+   |     ^^^^^^
 LL |
 LL |         V(E),
    |           - recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `f::E` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |         V(Box<E>),
    |           ++++ +
diff --git a/src/test/rustdoc-ui/infinite-recursive-type.stderr b/src/test/rustdoc-ui/infinite-recursive-type.stderr
index b33aba446223c..9e2c3ff164209 100644
--- a/src/test/rustdoc-ui/infinite-recursive-type.stderr
+++ b/src/test/rustdoc-ui/infinite-recursive-type.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `E` has infinite size
   --> $DIR/infinite-recursive-type.rs:1:1
    |
 LL | enum E {
-   | ^^^^^^ recursive type has infinite size
+   | ^^^^^^
 LL |
 LL |     V(E),
    |       - recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `E` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     V(Box<E>),
    |       ++++ +
diff --git a/src/test/ui/infinite/infinite-struct.rs b/src/test/ui/infinite/infinite-struct.rs
index 74185dc597b52..f08e10f6bdbc0 100644
--- a/src/test/ui/infinite/infinite-struct.rs
+++ b/src/test/ui/infinite/infinite-struct.rs
@@ -6,4 +6,11 @@ fn foo() -> Take {
     Take(loop {})
 }
 
+// mutually infinite structs
+struct Foo { //~ ERROR has infinite size
+    x: Bar<Foo>,
+}
+
+struct Bar<T>([T; 1]);
+
 fn main() {}
diff --git a/src/test/ui/infinite/infinite-struct.stderr b/src/test/ui/infinite/infinite-struct.stderr
index 5a6d13786d197..b6c72b1de4695 100644
--- a/src/test/ui/infinite/infinite-struct.stderr
+++ b/src/test/ui/infinite/infinite-struct.stderr
@@ -3,14 +3,25 @@ error[E0072]: recursive type `Take` has infinite size
    |
 LL | struct Take(Take);
    | ^^^^^^^^^^^ ---- recursive without indirection
-   | |
-   | recursive type has infinite size
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Take` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL | struct Take(Box<Take>);
    |             ++++    +
 
-error: aborting due to previous error
+error[E0072]: recursive type `Foo` has infinite size
+  --> $DIR/infinite-struct.rs:10:1
+   |
+LL | struct Foo {
+   | ^^^^^^^^^^
+LL |     x: Bar<Foo>,
+   |            --- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
+   |
+LL |     x: Bar<Box<Foo>>,
+   |            ++++   +
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/infinite/infinite-tag-type-recursion.stderr b/src/test/ui/infinite/infinite-tag-type-recursion.stderr
index d2dad4b9178d9..513bbfc1b8cf1 100644
--- a/src/test/ui/infinite/infinite-tag-type-recursion.stderr
+++ b/src/test/ui/infinite/infinite-tag-type-recursion.stderr
@@ -3,10 +3,8 @@ error[E0072]: recursive type `MList` has infinite size
    |
 LL | enum MList { Cons(isize, MList), Nil }
    | ^^^^^^^^^^               ----- recursive without indirection
-   | |
-   | recursive type has infinite size
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `MList` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL | enum MList { Cons(isize, Box<MList>), Nil }
    |                          ++++     +
diff --git a/src/test/ui/issues/issue-17431-1.stderr b/src/test/ui/issues/issue-17431-1.stderr
index db32eb952ba79..e3af8976cee91 100644
--- a/src/test/ui/issues/issue-17431-1.stderr
+++ b/src/test/ui/issues/issue-17431-1.stderr
@@ -2,14 +2,12 @@ error[E0072]: recursive type `Foo` has infinite size
   --> $DIR/issue-17431-1.rs:1:1
    |
 LL | struct Foo { foo: Option<Option<Foo>> }
-   | ^^^^^^^^^^        ------------------- recursive without indirection
-   | |
-   | recursive type has infinite size
+   | ^^^^^^^^^^                      --- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-LL | struct Foo { foo: Option<Box<Option<Foo>>> }
-   |                          ++++           +
+LL | struct Foo { foo: Option<Option<Box<Foo>>> }
+   |                                 ++++   +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-17431-2.rs b/src/test/ui/issues/issue-17431-2.rs
index 9ed97f63177f0..f7b9c6a55ddf7 100644
--- a/src/test/ui/issues/issue-17431-2.rs
+++ b/src/test/ui/issues/issue-17431-2.rs
@@ -1,8 +1,7 @@
 struct Baz { q: Option<Foo> }
-//~^ ERROR recursive type `Baz` has infinite size
+//~^ ERROR recursive types `Baz` and `Foo` have infinite size
 
 struct Foo { q: Option<Baz> }
-//~^ ERROR recursive type `Foo` has infinite size
 
 impl Foo { fn bar(&self) {} }
 
diff --git a/src/test/ui/issues/issue-17431-2.stderr b/src/test/ui/issues/issue-17431-2.stderr
index d23fd1474ac12..39a99ec1ef739 100644
--- a/src/test/ui/issues/issue-17431-2.stderr
+++ b/src/test/ui/issues/issue-17431-2.stderr
@@ -1,29 +1,20 @@
-error[E0072]: recursive type `Baz` has infinite size
+error[E0072]: recursive types `Baz` and `Foo` have infinite size
   --> $DIR/issue-17431-2.rs:1:1
    |
 LL | struct Baz { q: Option<Foo> }
-   | ^^^^^^^^^^      ----------- recursive without indirection
-   | |
-   | recursive type has infinite size
-   |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Baz` representable
-   |
-LL | struct Baz { q: Option<Box<Foo>> }
-   |                        ++++   +
-
-error[E0072]: recursive type `Foo` has infinite size
-  --> $DIR/issue-17431-2.rs:4:1
-   |
+   | ^^^^^^^^^^             --- recursive without indirection
+...
 LL | struct Foo { q: Option<Baz> }
-   | ^^^^^^^^^^      ----------- recursive without indirection
-   | |
-   | recursive type has infinite size
+   | ^^^^^^^^^^             --- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+LL ~ struct Baz { q: Option<Box<Foo>> }
+LL |
+LL | 
+LL ~ struct Foo { q: Option<Box<Baz>> }
    |
-LL | struct Foo { q: Option<Box<Baz>> }
-   |                        ++++   +
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/issues/issue-17431-3.stderr b/src/test/ui/issues/issue-17431-3.stderr
index 0dde6f382c34a..394134c78556b 100644
--- a/src/test/ui/issues/issue-17431-3.stderr
+++ b/src/test/ui/issues/issue-17431-3.stderr
@@ -2,14 +2,12 @@ error[E0072]: recursive type `Foo` has infinite size
   --> $DIR/issue-17431-3.rs:3:1
    |
 LL | struct Foo { foo: Mutex<Option<Foo>> }
-   | ^^^^^^^^^^        ------------------ recursive without indirection
-   | |
-   | recursive type has infinite size
+   | ^^^^^^^^^^                     --- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-LL | struct Foo { foo: Box<Mutex<Option<Foo>>> }
-   |                   ++++                  +
+LL | struct Foo { foo: Mutex<Option<Box<Foo>>> }
+   |                                ++++   +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-17431-4.stderr b/src/test/ui/issues/issue-17431-4.stderr
index ddf669b8fd1cb..3d141e44babf4 100644
--- a/src/test/ui/issues/issue-17431-4.stderr
+++ b/src/test/ui/issues/issue-17431-4.stderr
@@ -2,14 +2,12 @@ error[E0072]: recursive type `Foo` has infinite size
   --> $DIR/issue-17431-4.rs:3:1
    |
 LL | struct Foo<T> { foo: Option<Option<Foo<T>>>, marker: marker::PhantomData<T> }
-   | ^^^^^^^^^^^^^        ---------------------- recursive without indirection
-   | |
-   | recursive type has infinite size
+   | ^^^^^^^^^^^^^                      ------ recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-LL | struct Foo<T> { foo: Option<Box<Option<Foo<T>>>>, marker: marker::PhantomData<T> }
-   |                             ++++              +
+LL | struct Foo<T> { foo: Option<Option<Box<Foo<T>>>>, marker: marker::PhantomData<T> }
+   |                                    ++++      +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-17431-5.stderr b/src/test/ui/issues/issue-17431-5.stderr
index a379598c2657c..44a90a6fe3898 100644
--- a/src/test/ui/issues/issue-17431-5.stderr
+++ b/src/test/ui/issues/issue-17431-5.stderr
@@ -3,10 +3,8 @@ error[E0072]: recursive type `Bar` has infinite size
    |
 LL | struct Bar<T> { x: Bar<Foo> , marker: marker::PhantomData<T> }
    | ^^^^^^^^^^^^^      -------- recursive without indirection
-   | |
-   | recursive type has infinite size
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Bar` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL | struct Bar<T> { x: Box<Bar<Foo>> , marker: marker::PhantomData<T> }
    |                    ++++        +
diff --git a/src/test/ui/issues/issue-17431-6.stderr b/src/test/ui/issues/issue-17431-6.stderr
index fcac420b23845..e0a8225507e5a 100644
--- a/src/test/ui/issues/issue-17431-6.stderr
+++ b/src/test/ui/issues/issue-17431-6.stderr
@@ -2,14 +2,12 @@ error[E0072]: recursive type `Foo` has infinite size
   --> $DIR/issue-17431-6.rs:3:1
    |
 LL | enum Foo { X(Mutex<Option<Foo>>) }
-   | ^^^^^^^^     ------------------ recursive without indirection
-   | |
-   | recursive type has infinite size
+   | ^^^^^^^^                  --- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-LL | enum Foo { X(Box<Mutex<Option<Foo>>>) }
-   |              ++++                  +
+LL | enum Foo { X(Mutex<Option<Box<Foo>>>) }
+   |                           ++++   +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-17431-7.stderr b/src/test/ui/issues/issue-17431-7.stderr
index 6f8a7e3867b09..ecf072b8e8aea 100644
--- a/src/test/ui/issues/issue-17431-7.stderr
+++ b/src/test/ui/issues/issue-17431-7.stderr
@@ -2,14 +2,12 @@ error[E0072]: recursive type `Foo` has infinite size
   --> $DIR/issue-17431-7.rs:1:1
    |
 LL | enum Foo { Voo(Option<Option<Foo>>) }
-   | ^^^^^^^^       ------------------- recursive without indirection
-   | |
-   | recursive type has infinite size
+   | ^^^^^^^^                     --- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-LL | enum Foo { Voo(Option<Box<Option<Foo>>>) }
-   |                       ++++           +
+LL | enum Foo { Voo(Option<Option<Box<Foo>>>) }
+   |                              ++++   +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-2718-a.stderr b/src/test/ui/issues/issue-2718-a.stderr
index c6e703f4876f8..7ea620f386a20 100644
--- a/src/test/ui/issues/issue-2718-a.stderr
+++ b/src/test/ui/issues/issue-2718-a.stderr
@@ -3,10 +3,8 @@ error[E0072]: recursive type `Pong` has infinite size
    |
 LL |     pub struct Pong(SendPacket<Ping>);
    |     ^^^^^^^^^^^^^^^ ---------------- recursive without indirection
-   |     |
-   |     recursive type has infinite size
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Pong` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     pub struct Pong(Box<SendPacket<Ping>>);
    |                     ++++                +
diff --git a/src/test/ui/issues/issue-3008-1.stderr b/src/test/ui/issues/issue-3008-1.stderr
index e49d8e6aa0d8e..be25b9091d589 100644
--- a/src/test/ui/issues/issue-3008-1.stderr
+++ b/src/test/ui/issues/issue-3008-1.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `Bar` has infinite size
   --> $DIR/issue-3008-1.rs:5:1
    |
 LL | enum Bar {
-   | ^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^
 ...
 LL |     BarSome(Bar)
    |             --- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Bar` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     BarSome(Box<Bar>)
    |             ++++   +
diff --git a/src/test/ui/issues/issue-3008-2.stderr b/src/test/ui/issues/issue-3008-2.stderr
index b3ce6e42096bb..858a8fd6af808 100644
--- a/src/test/ui/issues/issue-3008-2.stderr
+++ b/src/test/ui/issues/issue-3008-2.stderr
@@ -3,10 +3,8 @@ error[E0072]: recursive type `Bar` has infinite size
    |
 LL | struct Bar { x: Bar }
    | ^^^^^^^^^^      --- recursive without indirection
-   | |
-   | recursive type has infinite size
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Bar` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL | struct Bar { x: Box<Bar> }
    |                 ++++   +
diff --git a/src/test/ui/issues/issue-3008-3.stderr b/src/test/ui/issues/issue-3008-3.stderr
index c1c043e217b4e..a1a81e2936775 100644
--- a/src/test/ui/issues/issue-3008-3.stderr
+++ b/src/test/ui/issues/issue-3008-3.stderr
@@ -3,10 +3,8 @@ error[E0072]: recursive type `E2` has infinite size
    |
 LL | enum E2<T> { V2(E2<E1>, marker::PhantomData<T>), }
    | ^^^^^^^^^^      ------ recursive without indirection
-   | |
-   | recursive type has infinite size
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `E2` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL | enum E2<T> { V2(Box<E2<E1>>, marker::PhantomData<T>), }
    |                 ++++      +
diff --git a/src/test/ui/issues/issue-32326.stderr b/src/test/ui/issues/issue-32326.stderr
index cea765850564f..dc51198d98ce8 100644
--- a/src/test/ui/issues/issue-32326.stderr
+++ b/src/test/ui/issues/issue-32326.stderr
@@ -2,16 +2,14 @@ error[E0072]: recursive type `Expr` has infinite size
   --> $DIR/issue-32326.rs:5:1
    |
 LL | enum Expr {
-   | ^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^
 LL |     Plus(Expr, Expr),
-   |          ----  ---- recursive without indirection
-   |          |
-   |          recursive without indirection
+   |          ---- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Expr` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-LL |     Plus(Box<Expr>, Box<Expr>),
-   |          ++++    +  ++++    +
+LL |     Plus(Box<Expr>, Expr),
+   |          ++++    +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-3779.stderr b/src/test/ui/issues/issue-3779.stderr
index e853d0f8c8932..a0dbcc920fa3d 100644
--- a/src/test/ui/issues/issue-3779.stderr
+++ b/src/test/ui/issues/issue-3779.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `S` has infinite size
   --> $DIR/issue-3779.rs:1:1
    |
 LL | struct S {
-   | ^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^
 LL |
 LL |     element: Option<S>
-   |              --------- recursive without indirection
+   |                     - recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `S` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     element: Option<Box<S>>
    |                     ++++ +
diff --git a/src/test/ui/issues/issue-57271.rs b/src/test/ui/issues/issue-57271.rs
index 9940fecbeed53..f74222e3e51db 100644
--- a/src/test/ui/issues/issue-57271.rs
+++ b/src/test/ui/issues/issue-57271.rs
@@ -4,7 +4,7 @@ extern crate issue_57271_lib;
 
 use issue_57271_lib::BaseType;
 
-pub enum ObjectType { //~ ERROR recursive type `ObjectType` has infinite size
+pub enum ObjectType { //~ ERROR recursive types `ObjectType` and `TypeSignature` have infinite size
     Class(ClassTypeSignature),
     Array(TypeSignature),
     TypeVariable(()),
@@ -16,7 +16,7 @@ pub struct ClassTypeSignature {
     pub inner: (),
 }
 
-pub enum TypeSignature { //~ ERROR recursive type `TypeSignature` has infinite size
+pub enum TypeSignature {
     Base(BaseType),
     Object(ObjectType),
 }
diff --git a/src/test/ui/issues/issue-57271.stderr b/src/test/ui/issues/issue-57271.stderr
index 8250096656ab2..391e69c91fb0f 100644
--- a/src/test/ui/issues/issue-57271.stderr
+++ b/src/test/ui/issues/issue-57271.stderr
@@ -1,31 +1,27 @@
-error[E0072]: recursive type `ObjectType` has infinite size
+error[E0072]: recursive types `ObjectType` and `TypeSignature` have infinite size
   --> $DIR/issue-57271.rs:7:1
    |
 LL | pub enum ObjectType {
-   | ^^^^^^^^^^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^^^^^^^^^^
 LL |     Class(ClassTypeSignature),
 LL |     Array(TypeSignature),
    |           ------------- recursive without indirection
-   |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `ObjectType` representable
-   |
-LL |     Array(Box<TypeSignature>),
-   |           ++++             +
-
-error[E0072]: recursive type `TypeSignature` has infinite size
-  --> $DIR/issue-57271.rs:19:1
-   |
+...
 LL | pub enum TypeSignature {
-   | ^^^^^^^^^^^^^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^^^^^^^^^^^^^
 LL |     Base(BaseType),
 LL |     Object(ObjectType),
    |            ---------- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `TypeSignature` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
+   |
+LL ~     Array(Box<TypeSignature>),
+LL |     TypeVariable(()),
+ ...
+LL |     Base(BaseType),
+LL ~     Object(Box<ObjectType>),
    |
-LL |     Object(Box<ObjectType>),
-   |            ++++          +
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/issues/issue-72554.stderr b/src/test/ui/issues/issue-72554.stderr
index 3e5adcae133ca..bc85cd7b18d55 100644
--- a/src/test/ui/issues/issue-72554.stderr
+++ b/src/test/ui/issues/issue-72554.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `ElemDerived` has infinite size
   --> $DIR/issue-72554.rs:4:1
    |
 LL | pub enum ElemDerived {
-   | ^^^^^^^^^^^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^^^^^^^^^^^
 ...
 LL |     A(ElemDerived)
    |       ----------- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `ElemDerived` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     A(Box<ElemDerived>)
    |       ++++           +
diff --git a/src/test/ui/keyword/keyword-self-as-type-param.stderr b/src/test/ui/keyword/keyword-self-as-type-param.stderr
index 419652e13492f..5aef94754869f 100644
--- a/src/test/ui/keyword/keyword-self-as-type-param.stderr
+++ b/src/test/ui/keyword/keyword-self-as-type-param.stderr
@@ -19,10 +19,8 @@ error[E0072]: recursive type `Foo` has infinite size
    |
 LL | struct Foo<Self>(Self);
    | ^^^^^^^^^^^^^^^^ ---- recursive without indirection
-   | |
-   | recursive type has infinite size
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL | struct Foo<Self>(Box<Self>);
    |                  ++++    +
diff --git a/src/test/ui/recursion/recursive-enum.stderr b/src/test/ui/recursion/recursive-enum.stderr
index f5d25c5641dd1..d662d1022038d 100644
--- a/src/test/ui/recursion/recursive-enum.stderr
+++ b/src/test/ui/recursion/recursive-enum.stderr
@@ -3,10 +3,8 @@ error[E0072]: recursive type `List` has infinite size
    |
 LL | enum List<T> { Cons(T, List<T>), Nil }
    | ^^^^^^^^^^^^           ------- recursive without indirection
-   | |
-   | recursive type has infinite size
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL | enum List<T> { Cons(T, Box<List<T>>), Nil }
    |                        ++++       +
diff --git a/src/test/ui/sized-cycle-note.rs b/src/test/ui/sized-cycle-note.rs
index ca8be5692ba97..766a5fa0de3dc 100644
--- a/src/test/ui/sized-cycle-note.rs
+++ b/src/test/ui/sized-cycle-note.rs
@@ -1,15 +1,6 @@
-// Test the error message resulting from a cycle in solving `Foo:
-// Sized`. The specifics of the message will of course but the main
-// thing we want to preserve is that:
-//
-// 1. the message should appear attached to one of the structs
-//    defined in this file;
-// 2. it should elaborate the steps that led to the cycle.
-
 struct Baz { q: Option<Foo> }
-//~^ ERROR recursive type `Baz` has infinite size
+//~^ ERROR recursive types `Baz` and `Foo` have infinite size
 struct Foo { q: Option<Baz> }
-//~^ ERROR recursive type `Foo` has infinite size
 
 impl Foo { fn bar(&self) {} }
 
diff --git a/src/test/ui/sized-cycle-note.stderr b/src/test/ui/sized-cycle-note.stderr
index 536510814c57c..06c87b61f41f6 100644
--- a/src/test/ui/sized-cycle-note.stderr
+++ b/src/test/ui/sized-cycle-note.stderr
@@ -1,29 +1,19 @@
-error[E0072]: recursive type `Baz` has infinite size
-  --> $DIR/sized-cycle-note.rs:9:1
+error[E0072]: recursive types `Baz` and `Foo` have infinite size
+  --> $DIR/sized-cycle-note.rs:1:1
    |
 LL | struct Baz { q: Option<Foo> }
-   | ^^^^^^^^^^      ----------- recursive without indirection
-   | |
-   | recursive type has infinite size
-   |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Baz` representable
-   |
-LL | struct Baz { q: Option<Box<Foo>> }
-   |                        ++++   +
-
-error[E0072]: recursive type `Foo` has infinite size
-  --> $DIR/sized-cycle-note.rs:11:1
-   |
+   | ^^^^^^^^^^             --- recursive without indirection
+LL |
 LL | struct Foo { q: Option<Baz> }
-   | ^^^^^^^^^^      ----------- recursive without indirection
-   | |
-   | recursive type has infinite size
+   | ^^^^^^^^^^             --- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+LL ~ struct Baz { q: Option<Box<Foo>> }
+LL |
+LL ~ struct Foo { q: Option<Box<Baz>> }
    |
-LL | struct Foo { q: Option<Box<Baz>> }
-   |                        ++++   +
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/span/E0072.stderr b/src/test/ui/span/E0072.stderr
index 882ed577cf366..20f2e0df05efe 100644
--- a/src/test/ui/span/E0072.stderr
+++ b/src/test/ui/span/E0072.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `ListNode` has infinite size
   --> $DIR/E0072.rs:1:1
    |
 LL | struct ListNode {
-   | ^^^^^^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^^^^^^
 LL |     head: u8,
 LL |     tail: Option<ListNode>,
-   |           ---------------- recursive without indirection
+   |                  -------- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `ListNode` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     tail: Option<Box<ListNode>>,
    |                  ++++        +
diff --git a/src/test/ui/span/multiline-span-E0072.stderr b/src/test/ui/span/multiline-span-E0072.stderr
index 79b13f45fd8ee..fc2f6e6622c2a 100644
--- a/src/test/ui/span/multiline-span-E0072.stderr
+++ b/src/test/ui/span/multiline-span-E0072.stderr
@@ -3,12 +3,12 @@ error[E0072]: recursive type `ListNode` has infinite size
    |
 LL | / struct
 LL | | ListNode
-   | |________^ recursive type has infinite size
+   | |________^
 ...
 LL |       tail: Option<ListNode>,
-   |             ---------------- recursive without indirection
+   |                    -------- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `ListNode` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     tail: Option<Box<ListNode>>,
    |                  ++++        +
diff --git a/src/test/ui/span/recursive-type-field.rs b/src/test/ui/span/recursive-type-field.rs
index 58ea81b8395ee..bd4c435347cfc 100644
--- a/src/test/ui/span/recursive-type-field.rs
+++ b/src/test/ui/span/recursive-type-field.rs
@@ -1,11 +1,11 @@
 use std::rc::Rc;
 
-struct Foo<'a> { //~ ERROR recursive type
+struct Foo<'a> { //~ ERROR recursive types `Foo` and `Bar` have infinite size
     bar: Bar<'a>,
     b: Rc<Bar<'a>>,
 }
 
-struct Bar<'a> { //~ ERROR recursive type
+struct Bar<'a> {
     y: (Foo<'a>, Foo<'a>),
     z: Option<Bar<'a>>,
     a: &'a Foo<'a>,
diff --git a/src/test/ui/span/recursive-type-field.stderr b/src/test/ui/span/recursive-type-field.stderr
index 08e97e750c31a..10af4c36ba4b4 100644
--- a/src/test/ui/span/recursive-type-field.stderr
+++ b/src/test/ui/span/recursive-type-field.stderr
@@ -1,35 +1,27 @@
-error[E0072]: recursive type `Foo` has infinite size
+error[E0072]: recursive types `Foo` and `Bar` have infinite size
   --> $DIR/recursive-type-field.rs:3:1
    |
 LL | struct Foo<'a> {
-   | ^^^^^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^^^^^
 LL |     bar: Bar<'a>,
    |          ------- recursive without indirection
+...
+LL | struct Bar<'a> {
+   | ^^^^^^^^^^^^^^
+LL |     y: (Foo<'a>, Foo<'a>),
+   |         -------  ------- recursive without indirection
+   |         |
+   |         recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
-   |
-LL |     bar: Box<Bar<'a>>,
-   |          ++++       +
-
-error[E0072]: recursive type `Bar` has infinite size
-  --> $DIR/recursive-type-field.rs:8:1
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
+LL ~     bar: Box<Bar<'a>>,
+LL |     b: Rc<Bar<'a>>,
+ ...
 LL | struct Bar<'a> {
-   | ^^^^^^^^^^^^^^ recursive type has infinite size
-LL |     y: (Foo<'a>, Foo<'a>),
-   |        ------------------ recursive without indirection
-LL |     z: Option<Bar<'a>>,
-   |        --------------- recursive without indirection
-...
-LL |     d: [Bar<'a>; 1],
-   |        ------------ recursive without indirection
-LL |     e: Foo<'a>,
-   |        ------- recursive without indirection
-LL |     x: Bar<'a>,
-   |        ------- recursive without indirection
+LL ~     y: (Box<Foo<'a>>, Box<Foo<'a>>),
    |
-   = help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Bar` representable
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/structs-enums/struct-rec/issue-74224.stderr b/src/test/ui/structs-enums/struct-rec/issue-74224.stderr
index 6199178467fcf..f1d50bc8af5ac 100644
--- a/src/test/ui/structs-enums/struct-rec/issue-74224.stderr
+++ b/src/test/ui/structs-enums/struct-rec/issue-74224.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `A` has infinite size
   --> $DIR/issue-74224.rs:1:1
    |
 LL | struct A<T> {
-   | ^^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^^
 ...
 LL |     y: A<A<T>>,
    |        ------- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `A` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     y: Box<A<A<T>>>,
    |        ++++       +
diff --git a/src/test/ui/structs-enums/struct-rec/issue-84611.stderr b/src/test/ui/structs-enums/struct-rec/issue-84611.stderr
index 2e99435e033d5..536f54e3e9611 100644
--- a/src/test/ui/structs-enums/struct-rec/issue-84611.stderr
+++ b/src/test/ui/structs-enums/struct-rec/issue-84611.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `Foo` has infinite size
   --> $DIR/issue-84611.rs:1:1
    |
 LL | struct Foo<T> {
-   | ^^^^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^^^^
 LL |
 LL |     x: Foo<[T; 1]>,
    |        ----------- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     x: Box<Foo<[T; 1]>>,
    |        ++++           +
diff --git a/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.rs b/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.rs
index cca97f43effc3..3bfce8b4f9656 100644
--- a/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.rs
+++ b/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.rs
@@ -1,22 +1,20 @@
 struct A<T> {
-//~^ ERROR recursive type `A` has infinite size
+//~^ ERROR recursive types `A` and `B` have infinite size
     x: T,
     y: B<T>,
 }
 
 struct B<T> {
-//~^ ERROR recursive type `B` has infinite size
     z: A<T>
 }
 
 struct C<T> {
-//~^ ERROR recursive type `C` has infinite size
+//~^ ERROR recursive types `C` and `D` have infinite size
     x: T,
     y: Option<Option<D<T>>>,
 }
 
 struct D<T> {
-//~^ ERROR recursive type `D` has infinite size
     z: Option<Option<C<T>>>,
 }
 
diff --git a/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.stderr b/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.stderr
index 80a494f3f65eb..881bc2819369b 100644
--- a/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.stderr
+++ b/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.stderr
@@ -1,59 +1,49 @@
-error[E0072]: recursive type `A` has infinite size
+error[E0072]: recursive types `A` and `B` have infinite size
   --> $DIR/mutual-struct-recursion.rs:1:1
    |
 LL | struct A<T> {
-   | ^^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^^
 ...
 LL |     y: B<T>,
    |        ---- recursive without indirection
-   |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `A` representable
-   |
-LL |     y: Box<B<T>>,
-   |        ++++    +
-
-error[E0072]: recursive type `B` has infinite size
-  --> $DIR/mutual-struct-recursion.rs:7:1
-   |
+...
 LL | struct B<T> {
-   | ^^^^^^^^^^^ recursive type has infinite size
-LL |
+   | ^^^^^^^^^^^
 LL |     z: A<T>
    |        ---- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `B` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
+   |
+LL ~     y: Box<B<T>>,
+LL | }
+LL | 
+LL | struct B<T> {
+LL ~     z: Box<A<T>>
    |
-LL |     z: Box<A<T>>
-   |        ++++    +
 
-error[E0072]: recursive type `C` has infinite size
-  --> $DIR/mutual-struct-recursion.rs:12:1
+error[E0072]: recursive types `C` and `D` have infinite size
+  --> $DIR/mutual-struct-recursion.rs:11:1
    |
 LL | struct C<T> {
-   | ^^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^^
 ...
 LL |     y: Option<Option<D<T>>>,
-   |        -------------------- recursive without indirection
-   |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `C` representable
-   |
-LL |     y: Option<Box<Option<D<T>>>>,
-   |               ++++            +
-
-error[E0072]: recursive type `D` has infinite size
-  --> $DIR/mutual-struct-recursion.rs:18:1
-   |
+   |                      ---- recursive without indirection
+...
 LL | struct D<T> {
-   | ^^^^^^^^^^^ recursive type has infinite size
-LL |
+   | ^^^^^^^^^^^
 LL |     z: Option<Option<C<T>>>,
-   |        -------------------- recursive without indirection
+   |                      ---- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `D` representable
+LL ~     y: Option<Option<Box<D<T>>>>,
+LL | }
+LL | 
+LL | struct D<T> {
+LL ~     z: Option<Option<Box<C<T>>>>,
    |
-LL |     z: Option<Box<Option<C<T>>>>,
-   |               ++++            +
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/type/type-recursive-box-shadowed.stderr b/src/test/ui/type/type-recursive-box-shadowed.stderr
index c22d848f3797e..cb0e982877cf7 100644
--- a/src/test/ui/type/type-recursive-box-shadowed.stderr
+++ b/src/test/ui/type/type-recursive-box-shadowed.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `Foo` has infinite size
   --> $DIR/type-recursive-box-shadowed.rs:7:1
    |
 LL | struct Foo {
-   | ^^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^^
 LL |
 LL |     inner: Foo,
    |            --- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     inner: Box<Foo>,
    |            ++++   +
diff --git a/src/test/ui/type/type-recursive.stderr b/src/test/ui/type/type-recursive.stderr
index 3202710286e1a..9a4d798f61e01 100644
--- a/src/test/ui/type/type-recursive.stderr
+++ b/src/test/ui/type/type-recursive.stderr
@@ -2,12 +2,12 @@ error[E0072]: recursive type `T1` has infinite size
   --> $DIR/type-recursive.rs:1:1
    |
 LL | struct T1 {
-   | ^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^
 LL |     foo: isize,
 LL |     foolish: T1,
    |              -- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `T1` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     foolish: Box<T1>,
    |              ++++  +
@@ -16,11 +16,11 @@ error[E0072]: recursive type `T2` has infinite size
   --> $DIR/type-recursive.rs:6:1
    |
 LL | struct T2 {
-   | ^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^
 LL |     inner: Option<T2>,
-   |            ---------- recursive without indirection
+   |                   -- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `T2` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     inner: Option<Box<T2>>,
    |                   ++++  +
@@ -29,11 +29,11 @@ error[E0072]: recursive type `T3` has infinite size
   --> $DIR/type-recursive.rs:12:1
    |
 LL | struct T3 {
-   | ^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^
 LL |     inner: OptionT3,
    |            -------- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `T3` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     inner: Box<OptionT3>,
    |            ++++        +
@@ -42,11 +42,9 @@ error[E0072]: recursive type `T4` has infinite size
   --> $DIR/type-recursive.rs:16:1
    |
 LL | struct T4(Option<T4>);
-   | ^^^^^^^^^ ---------- recursive without indirection
-   | |
-   | recursive type has infinite size
+   | ^^^^^^^^^        -- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `T4` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL | struct T4(Option<Box<T4>>);
    |                  ++++  +
@@ -55,11 +53,11 @@ error[E0072]: recursive type `T5` has infinite size
   --> $DIR/type-recursive.rs:18:1
    |
 LL | enum T5 {
-   | ^^^^^^^ recursive type has infinite size
+   | ^^^^^^^
 LL |     Variant(Option<T5>),
-   |             ---------- recursive without indirection
+   |                    -- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `T5` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     Variant(Option<Box<T5>>),
    |                    ++++  +
@@ -68,11 +66,11 @@ error[E0072]: recursive type `T6` has infinite size
   --> $DIR/type-recursive.rs:22:1
    |
 LL | enum T6 {
-   | ^^^^^^^ recursive type has infinite size
+   | ^^^^^^^
 LL |     Variant{ field: Option<T6> },
-   |                     ---------- recursive without indirection
+   |                            -- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `T6` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
 LL |     Variant{ field: Option<Box<T6>> },
    |                            ++++  +
@@ -81,14 +79,14 @@ error[E0072]: recursive type `T7` has infinite size
   --> $DIR/type-recursive.rs:26:1
    |
 LL | struct T7 {
-   | ^^^^^^^^^ recursive type has infinite size
+   | ^^^^^^^^^
 LL |     foo: std::cell::Cell<Option<T7>>,
-   |          --------------------------- recursive without indirection
+   |                                 -- recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `T7` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-LL |     foo: Box<std::cell::Cell<Option<T7>>>,
-   |          ++++                           +
+LL |     foo: std::cell::Cell<Option<Box<T7>>>,
+   |                                 ++++  +
 
 error: aborting due to 7 previous errors
 
diff --git a/src/test/ui/union/union-nonrepresentable.stderr b/src/test/ui/union/union-nonrepresentable.stderr
index 9804b1418b208..c266d2e9e138b 100644
--- a/src/test/ui/union/union-nonrepresentable.stderr
+++ b/src/test/ui/union/union-nonrepresentable.stderr
@@ -2,15 +2,15 @@ error[E0072]: recursive type `U` has infinite size
   --> $DIR/union-nonrepresentable.rs:1:1
    |
 LL | union U {
-   | ^^^^^^^ recursive type has infinite size
+   | ^^^^^^^
 LL |     a: u8,
 LL |     b: std::mem::ManuallyDrop<U>,
-   |        ------------------------- recursive without indirection
+   |                               - recursive without indirection
    |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `U` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
    |
-LL |     b: Box<std::mem::ManuallyDrop<U>>,
-   |        ++++                         +
+LL |     b: std::mem::ManuallyDrop<Box<U>>,
+   |                               ++++ +
 
 error: aborting due to previous error