about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_abi/src/layout.rs1218
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs55
-rw-r--r--compiler/rustc_borrowck/src/borrowck_errors.rs56
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs46
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs49
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs13
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs18
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs65
-rw-r--r--compiler/rustc_borrowck/src/lib.rs21
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs13
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs22
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs36
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs4
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs8
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs140
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs50
-rw-r--r--compiler/rustc_driver_impl/src/args.rs2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs23
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs2
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs79
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs80
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs14
-rw-r--r--compiler/rustc_errors/src/emitter.rs37
-rw-r--r--compiler/rustc_errors/src/json.rs10
-rw-r--r--compiler/rustc_errors/src/lib.rs327
-rw-r--r--compiler/rustc_expand/src/base.rs9
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs8
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs4
-rw-r--r--compiler/rustc_expand/src/module.rs2
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs7
-rw-r--r--compiler/rustc_hir/src/hir.rs78
-rw-r--r--compiler/rustc_hir/src/lang_items.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs14
-rw-r--r--compiler/rustc_index/src/bit_set.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs8
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs10
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs6
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs8
-rw-r--r--compiler/rustc_infer/src/traits/error_reporting/mod.rs6
-rw-r--r--compiler/rustc_interface/src/interface.rs2
-rw-r--r--compiler/rustc_interface/src/util.rs12
-rw-r--r--compiler/rustc_metadata/src/rmeta/table.rs20
-rw-r--r--compiler/rustc_middle/src/lint.rs36
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs5
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs2
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs38
-rw-r--r--compiler/rustc_middle/src/ty/context.rs15
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs22
-rw-r--r--compiler/rustc_mir_build/src/errors.rs8
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs12
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs12
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs44
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs1
-rw-r--r--compiler/rustc_mir_transform/src/lint.rs119
-rw-r--r--compiler/rustc_mir_transform/src/pass_manager.rs12
-rw-r--r--compiler/rustc_mir_transform/src/ref_prop.rs3
-rw-r--r--compiler/rustc_parse/src/errors.rs16
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs21
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs5
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs6
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs8
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs4
-rw-r--r--compiler/rustc_pattern_analysis/Cargo.toml1
-rw-r--r--compiler/rustc_pattern_analysis/src/constructor.rs3
-rw-r--r--compiler/rustc_pattern_analysis/src/lib.rs18
-rw-r--r--compiler/rustc_pattern_analysis/src/lints.rs4
-rw-r--r--compiler/rustc_pattern_analysis/src/pat.rs21
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs6
-rw-r--r--compiler/rustc_pattern_analysis/src/usefulness.rs24
-rw-r--r--compiler/rustc_query_system/src/query/job.rs4
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs4
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs2
-rw-r--r--compiler/rustc_resolve/src/late.rs2
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs4
-rw-r--r--compiler/rustc_session/src/config.rs120
-rw-r--r--compiler/rustc_session/src/errors.rs9
-rw-r--r--compiler/rustc_session/src/options.rs8
-rw-r--r--compiler/rustc_session/src/parse.rs14
-rw-r--r--compiler/rustc_session/src/search_paths.rs2
-rw-r--r--compiler/rustc_session/src/session.rs56
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mod.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs93
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs75
-rw-r--r--compiler/rustc_ty_utils/src/abi.rs12
-rw-r--r--compiler/stable_mir/src/mir/body.rs55
113 files changed, 1869 insertions, 1840 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 5252472261f..ec3ab828b71 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -11,6 +11,24 @@ use crate::{
     Variants, WrappingRange,
 };
 
+// A variant is absent if it's uninhabited and only has ZST fields.
+// Present uninhabited variants only require space for their fields,
+// but *not* an encoding of the discriminant (e.g., a tag value).
+// See issue #49298 for more details on the need to leave space
+// for non-ZST uninhabited data (mostly partial initialization).
+fn absent<'a, FieldIdx, VariantIdx, F>(fields: &IndexSlice<FieldIdx, F>) -> bool
+where
+    FieldIdx: Idx,
+    VariantIdx: Idx,
+    F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
+{
+    let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited());
+    // We cannot ignore alignment; that might lead us to entirely discard a variant and
+    // produce an enum that is less aligned than it should be!
+    let is_1zst = fields.iter().all(|f| f.is_1zst());
+    uninhabited && is_1zst
+}
+
 pub trait LayoutCalculator {
     type TargetDataLayoutRef: Borrow<TargetDataLayout>;
 
@@ -162,24 +180,6 @@ pub trait LayoutCalculator {
         let dl = self.current_data_layout();
         let dl = dl.borrow();
 
-        let scalar_unit = |value: Primitive| {
-            let size = value.size(dl);
-            assert!(size.bits() <= 128);
-            Scalar::Initialized { value, valid_range: WrappingRange::full(size) }
-        };
-
-        // A variant is absent if it's uninhabited and only has ZST fields.
-        // Present uninhabited variants only require space for their fields,
-        // but *not* an encoding of the discriminant (e.g., a tag value).
-        // See issue #49298 for more details on the need to leave space
-        // for non-ZST uninhabited data (mostly partial initialization).
-        let absent = |fields: &IndexSlice<FieldIdx, F>| {
-            let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited());
-            // We cannot ignore alignment; that might lead us to entirely discard a variant and
-            // produce an enum that is less aligned than it should be!
-            let is_1zst = fields.iter().all(|f| f.is_1zst());
-            uninhabited && is_1zst
-        };
         let (present_first, present_second) = {
             let mut present_variants = variants
                 .iter_enumerated()
@@ -197,574 +197,37 @@ pub trait LayoutCalculator {
             None => VariantIdx::new(0),
         };
 
-        let is_struct = !is_enum ||
-                    // Only one variant is present.
-                    (present_second.is_none() &&
-                        // Representation optimizations are allowed.
-                        !repr.inhibit_enum_layout_opt());
-        if is_struct {
-            // Struct, or univariant enum equivalent to a struct.
-            // (Typechecking will reject discriminant-sizing attrs.)
-
-            let v = present_first;
-            let kind = if is_enum || variants[v].is_empty() || always_sized {
-                StructKind::AlwaysSized
-            } else {
-                StructKind::MaybeUnsized
-            };
-
-            let mut st = self.univariant(dl, &variants[v], repr, kind)?;
-            st.variants = Variants::Single { index: v };
-
-            if is_unsafe_cell {
-                let hide_niches = |scalar: &mut _| match scalar {
-                    Scalar::Initialized { value, valid_range } => {
-                        *valid_range = WrappingRange::full(value.size(dl))
-                    }
-                    // Already doesn't have any niches
-                    Scalar::Union { .. } => {}
-                };
-                match &mut st.abi {
-                    Abi::Uninhabited => {}
-                    Abi::Scalar(scalar) => hide_niches(scalar),
-                    Abi::ScalarPair(a, b) => {
-                        hide_niches(a);
-                        hide_niches(b);
-                    }
-                    Abi::Vector { element, count: _ } => hide_niches(element),
-                    Abi::Aggregate { sized: _ } => {}
-                }
-                st.largest_niche = None;
-                return Some(st);
-            }
-
-            let (start, end) = scalar_valid_range;
-            match st.abi {
-                Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => {
-                    // Enlarging validity ranges would result in missed
-                    // optimizations, *not* wrongly assuming the inner
-                    // value is valid. e.g. unions already enlarge validity ranges,
-                    // because the values may be uninitialized.
-                    //
-                    // Because of that we only check that the start and end
-                    // of the range is representable with this scalar type.
-
-                    let max_value = scalar.size(dl).unsigned_int_max();
-                    if let Bound::Included(start) = start {
-                        // FIXME(eddyb) this might be incorrect - it doesn't
-                        // account for wrap-around (end < start) ranges.
-                        assert!(start <= max_value, "{start} > {max_value}");
-                        scalar.valid_range_mut().start = start;
-                    }
-                    if let Bound::Included(end) = end {
-                        // FIXME(eddyb) this might be incorrect - it doesn't
-                        // account for wrap-around (end < start) ranges.
-                        assert!(end <= max_value, "{end} > {max_value}");
-                        scalar.valid_range_mut().end = end;
-                    }
-
-                    // Update `largest_niche` if we have introduced a larger niche.
-                    let niche = Niche::from_scalar(dl, Size::ZERO, *scalar);
-                    if let Some(niche) = niche {
-                        match st.largest_niche {
-                            Some(largest_niche) => {
-                                // Replace the existing niche even if they're equal,
-                                // because this one is at a lower offset.
-                                if largest_niche.available(dl) <= niche.available(dl) {
-                                    st.largest_niche = Some(niche);
-                                }
-                            }
-                            None => st.largest_niche = Some(niche),
-                        }
-                    }
-                }
-                _ => assert!(
-                    start == Bound::Unbounded && end == Bound::Unbounded,
-                    "nonscalar layout for layout_scalar_valid_range type: {st:#?}",
-                ),
-            }
-
-            return Some(st);
-        }
-
-        // At this point, we have handled all unions and
-        // structs. (We have also handled univariant enums
-        // that allow representation optimization.)
-        assert!(is_enum);
-
-        // Until we've decided whether to use the tagged or
-        // niche filling LayoutS, we don't want to intern the
-        // variant layouts, so we can't store them in the
-        // overall LayoutS. Store the overall LayoutS
-        // and the variant LayoutSs here until then.
-        struct TmpLayout<FieldIdx: Idx, VariantIdx: Idx> {
-            layout: LayoutS<FieldIdx, VariantIdx>,
-            variants: IndexVec<VariantIdx, LayoutS<FieldIdx, VariantIdx>>,
-        }
-
-        let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> {
-            if dont_niche_optimize_enum {
-                return None;
-            }
-
-            if variants.len() < 2 {
-                return None;
-            }
-
-            let mut align = dl.aggregate_align;
-            let mut max_repr_align = repr.align;
-            let mut unadjusted_abi_align = align.abi;
-
-            let mut variant_layouts = variants
-                .iter_enumerated()
-                .map(|(j, v)| {
-                    let mut st = self.univariant(dl, v, repr, StructKind::AlwaysSized)?;
-                    st.variants = Variants::Single { index: j };
-
-                    align = align.max(st.align);
-                    max_repr_align = max_repr_align.max(st.max_repr_align);
-                    unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
-
-                    Some(st)
-                })
-                .collect::<Option<IndexVec<VariantIdx, _>>>()?;
-
-            let largest_variant_index = variant_layouts
-                .iter_enumerated()
-                .max_by_key(|(_i, layout)| layout.size.bytes())
-                .map(|(i, _layout)| i)?;
-
-            let all_indices = variants.indices();
-            let needs_disc =
-                |index: VariantIdx| index != largest_variant_index && !absent(&variants[index]);
-            let niche_variants = all_indices.clone().find(|v| needs_disc(*v)).unwrap()
-                ..=all_indices.rev().find(|v| needs_disc(*v)).unwrap();
-
-            let count =
-                (niche_variants.end().index() as u128 - niche_variants.start().index() as u128) + 1;
-
-            // Find the field with the largest niche
-            let (field_index, niche, (niche_start, niche_scalar)) = variants[largest_variant_index]
-                .iter()
-                .enumerate()
-                .filter_map(|(j, field)| Some((j, field.largest_niche?)))
-                .max_by_key(|(_, niche)| niche.available(dl))
-                .and_then(|(j, niche)| Some((j, niche, niche.reserve(dl, count)?)))?;
-            let niche_offset =
-                niche.offset + variant_layouts[largest_variant_index].fields.offset(field_index);
-            let niche_size = niche.value.size(dl);
-            let size = variant_layouts[largest_variant_index].size.align_to(align.abi);
-
-            let all_variants_fit = variant_layouts.iter_enumerated_mut().all(|(i, layout)| {
-                if i == largest_variant_index {
-                    return true;
-                }
-
-                layout.largest_niche = None;
-
-                if layout.size <= niche_offset {
-                    // This variant will fit before the niche.
-                    return true;
-                }
-
-                // Determine if it'll fit after the niche.
-                let this_align = layout.align.abi;
-                let this_offset = (niche_offset + niche_size).align_to(this_align);
-
-                if this_offset + layout.size > size {
-                    return false;
-                }
-
-                // It'll fit, but we need to make some adjustments.
-                match layout.fields {
-                    FieldsShape::Arbitrary { ref mut offsets, .. } => {
-                        for offset in offsets.iter_mut() {
-                            *offset += this_offset;
-                        }
-                    }
-                    FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
-                        panic!("Layout of fields should be Arbitrary for variants")
-                    }
-                }
-
-                // It can't be a Scalar or ScalarPair because the offset isn't 0.
-                if !layout.abi.is_uninhabited() {
-                    layout.abi = Abi::Aggregate { sized: true };
-                }
-                layout.size += this_offset;
-
-                true
-            });
-
-            if !all_variants_fit {
-                return None;
-            }
-
-            let largest_niche = Niche::from_scalar(dl, niche_offset, niche_scalar);
-
-            let others_zst = variant_layouts
-                .iter_enumerated()
-                .all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO);
-            let same_size = size == variant_layouts[largest_variant_index].size;
-            let same_align = align == variant_layouts[largest_variant_index].align;
-
-            let abi = if variant_layouts.iter().all(|v| v.abi.is_uninhabited()) {
-                Abi::Uninhabited
-            } else if same_size && same_align && others_zst {
-                match variant_layouts[largest_variant_index].abi {
-                    // When the total alignment and size match, we can use the
-                    // same ABI as the scalar variant with the reserved niche.
-                    Abi::Scalar(_) => Abi::Scalar(niche_scalar),
-                    Abi::ScalarPair(first, second) => {
-                        // Only the niche is guaranteed to be initialised,
-                        // so use union layouts for the other primitive.
-                        if niche_offset == Size::ZERO {
-                            Abi::ScalarPair(niche_scalar, second.to_union())
-                        } else {
-                            Abi::ScalarPair(first.to_union(), niche_scalar)
-                        }
-                    }
-                    _ => Abi::Aggregate { sized: true },
-                }
-            } else {
-                Abi::Aggregate { sized: true }
-            };
-
-            let layout = LayoutS {
-                variants: Variants::Multiple {
-                    tag: niche_scalar,
-                    tag_encoding: TagEncoding::Niche {
-                        untagged_variant: largest_variant_index,
-                        niche_variants,
-                        niche_start,
-                    },
-                    tag_field: 0,
-                    variants: IndexVec::new(),
-                },
-                fields: FieldsShape::Arbitrary {
-                    offsets: [niche_offset].into(),
-                    memory_index: [0].into(),
-                },
-                abi,
-                largest_niche,
-                size,
-                align,
-                max_repr_align,
-                unadjusted_abi_align,
-            };
-
-            Some(TmpLayout { layout, variants: variant_layouts })
-        };
-
-        let niche_filling_layout = calculate_niche_filling_layout();
-
-        let (mut min, mut max) = (i128::MAX, i128::MIN);
-        let discr_type = repr.discr_type();
-        let bits = Integer::from_attr(dl, discr_type).size().bits();
-        for (i, mut val) in discriminants {
-            if variants[i].iter().any(|f| f.abi.is_uninhabited()) {
-                continue;
-            }
-            if discr_type.is_signed() {
-                // sign extend the raw representation to be an i128
-                val = (val << (128 - bits)) >> (128 - bits);
-            }
-            if val < min {
-                min = val;
-            }
-            if val > max {
-                max = val;
-            }
-        }
-        // We might have no inhabited variants, so pretend there's at least one.
-        if (min, max) == (i128::MAX, i128::MIN) {
-            min = 0;
-            max = 0;
-        }
-        assert!(min <= max, "discriminant range is {min}...{max}");
-        let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max);
-
-        let mut align = dl.aggregate_align;
-        let mut max_repr_align = repr.align;
-        let mut unadjusted_abi_align = align.abi;
-
-        let mut size = Size::ZERO;
-
-        // We're interested in the smallest alignment, so start large.
-        let mut start_align = Align::from_bytes(256).unwrap();
-        assert_eq!(Integer::for_align(dl, start_align), None);
-
-        // repr(C) on an enum tells us to make a (tag, union) layout,
-        // so we need to grow the prefix alignment to be at least
-        // the alignment of the union. (This value is used both for
-        // determining the alignment of the overall enum, and the
-        // determining the alignment of the payload after the tag.)
-        let mut prefix_align = min_ity.align(dl).abi;
-        if repr.c() {
-            for fields in variants {
-                for field in fields {
-                    prefix_align = prefix_align.max(field.align.abi);
-                }
-            }
-        }
-
-        // Create the set of structs that represent each variant.
-        let mut layout_variants = variants
-            .iter_enumerated()
-            .map(|(i, field_layouts)| {
-                let mut st = self.univariant(
-                    dl,
-                    field_layouts,
-                    repr,
-                    StructKind::Prefixed(min_ity.size(), prefix_align),
-                )?;
-                st.variants = Variants::Single { index: i };
-                // Find the first field we can't move later
-                // to make room for a larger discriminant.
-                for field_idx in st.fields.index_by_increasing_offset() {
-                    let field = &field_layouts[FieldIdx::new(field_idx)];
-                    if !field.is_1zst() {
-                        start_align = start_align.min(field.align.abi);
-                        break;
-                    }
-                }
-                size = cmp::max(size, st.size);
-                align = align.max(st.align);
-                max_repr_align = max_repr_align.max(st.max_repr_align);
-                unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
-                Some(st)
-            })
-            .collect::<Option<IndexVec<VariantIdx, _>>>()?;
-
-        // Align the maximum variant size to the largest alignment.
-        size = size.align_to(align.abi);
-
-        // FIXME(oli-obk): deduplicate and harden these checks
-        if size.bytes() >= dl.obj_size_bound() {
-            return None;
-        }
-
-        let typeck_ity = Integer::from_attr(dl, repr.discr_type());
-        if typeck_ity < min_ity {
-            // It is a bug if Layout decided on a greater discriminant size than typeck for
-            // some reason at this point (based on values discriminant can take on). Mostly
-            // because this discriminant will be loaded, and then stored into variable of
-            // type calculated by typeck. Consider such case (a bug): typeck decided on
-            // byte-sized discriminant, but layout thinks we need a 16-bit to store all
-            // discriminant values. That would be a bug, because then, in codegen, in order
-            // to store this 16-bit discriminant into 8-bit sized temporary some of the
-            // space necessary to represent would have to be discarded (or layout is wrong
-            // on thinking it needs 16 bits)
-            panic!(
-                "layout decided on a larger discriminant type ({min_ity:?}) than typeck ({typeck_ity:?})"
-            );
-            // However, it is fine to make discr type however large (as an optimisation)
-            // after this point – we’ll just truncate the value we load in codegen.
-        }
-
-        // Check to see if we should use a different type for the
-        // discriminant. We can safely use a type with the same size
-        // as the alignment of the first field of each variant.
-        // We increase the size of the discriminant to avoid LLVM copying
-        // padding when it doesn't need to. This normally causes unaligned
-        // load/stores and excessive memcpy/memset operations. By using a
-        // bigger integer size, LLVM can be sure about its contents and
-        // won't be so conservative.
-
-        // Use the initial field alignment
-        let mut ity = if repr.c() || repr.int.is_some() {
-            min_ity
-        } else {
-            Integer::for_align(dl, start_align).unwrap_or(min_ity)
-        };
-
-        // If the alignment is not larger than the chosen discriminant size,
-        // don't use the alignment as the final size.
-        if ity <= min_ity {
-            ity = min_ity;
-        } else {
-            // Patch up the variants' first few fields.
-            let old_ity_size = min_ity.size();
-            let new_ity_size = ity.size();
-            for variant in &mut layout_variants {
-                match variant.fields {
-                    FieldsShape::Arbitrary { ref mut offsets, .. } => {
-                        for i in offsets {
-                            if *i <= old_ity_size {
-                                assert_eq!(*i, old_ity_size);
-                                *i = new_ity_size;
-                            }
-                        }
-                        // We might be making the struct larger.
-                        if variant.size <= old_ity_size {
-                            variant.size = new_ity_size;
-                        }
-                    }
-                    FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
-                        panic!("encountered a non-arbitrary layout during enum layout")
-                    }
-                }
-            }
-        }
-
-        let tag_mask = ity.size().unsigned_int_max();
-        let tag = Scalar::Initialized {
-            value: Primitive::Int(ity, signed),
-            valid_range: WrappingRange {
-                start: (min as u128 & tag_mask),
-                end: (max as u128 & tag_mask),
-            },
-        };
-        let mut abi = Abi::Aggregate { sized: true };
-
-        if layout_variants.iter().all(|v| v.abi.is_uninhabited()) {
-            abi = Abi::Uninhabited;
-        } else if tag.size(dl) == size {
-            // Make sure we only use scalar layout when the enum is entirely its
-            // own tag (i.e. it has no padding nor any non-ZST variant fields).
-            abi = Abi::Scalar(tag);
+        // take the struct path if it is an actual struct
+        if !is_enum ||
+            // or for optimizing univariant enums
+            (present_second.is_none() && !repr.inhibit_enum_layout_opt())
+        {
+            layout_of_struct(
+                self,
+                repr,
+                variants,
+                is_enum,
+                is_unsafe_cell,
+                scalar_valid_range,
+                always_sized,
+                dl,
+                present_first,
+            )
         } else {
-            // Try to use a ScalarPair for all tagged enums.
-            // That's possible only if we can find a common primitive type for all variants.
-            let mut common_prim = None;
-            let mut common_prim_initialized_in_all_variants = true;
-            for (field_layouts, layout_variant) in iter::zip(variants, &layout_variants) {
-                let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
-                    panic!("encountered a non-arbitrary layout during enum layout");
-                };
-                // We skip *all* ZST here and later check if we are good in terms of alignment.
-                // This lets us handle some cases involving aligned ZST.
-                let mut fields = iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst());
-                let (field, offset) = match (fields.next(), fields.next()) {
-                    (None, None) => {
-                        common_prim_initialized_in_all_variants = false;
-                        continue;
-                    }
-                    (Some(pair), None) => pair,
-                    _ => {
-                        common_prim = None;
-                        break;
-                    }
-                };
-                let prim = match field.abi {
-                    Abi::Scalar(scalar) => {
-                        common_prim_initialized_in_all_variants &=
-                            matches!(scalar, Scalar::Initialized { .. });
-                        scalar.primitive()
-                    }
-                    _ => {
-                        common_prim = None;
-                        break;
-                    }
-                };
-                if let Some(pair) = common_prim {
-                    // This is pretty conservative. We could go fancier
-                    // by conflating things like i32 and u32, or even
-                    // realising that (u8, u8) could just cohabit with
-                    // u16 or even u32.
-                    if pair != (prim, offset) {
-                        common_prim = None;
-                        break;
-                    }
-                } else {
-                    common_prim = Some((prim, offset));
-                }
-            }
-            if let Some((prim, offset)) = common_prim {
-                let prim_scalar = if common_prim_initialized_in_all_variants {
-                    scalar_unit(prim)
-                } else {
-                    // Common prim might be uninit.
-                    Scalar::Union { value: prim }
-                };
-                let pair = self.scalar_pair::<FieldIdx, VariantIdx>(tag, prim_scalar);
-                let pair_offsets = match pair.fields {
-                    FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
-                        assert_eq!(memory_index.raw, [0, 1]);
-                        offsets
-                    }
-                    _ => panic!("encountered a non-arbitrary layout during enum layout"),
-                };
-                if pair_offsets[FieldIdx::new(0)] == Size::ZERO
-                    && pair_offsets[FieldIdx::new(1)] == *offset
-                    && align == pair.align
-                    && size == pair.size
-                {
-                    // We can use `ScalarPair` only when it matches our
-                    // already computed layout (including `#[repr(C)]`).
-                    abi = pair.abi;
-                }
-            }
-        }
-
-        // If we pick a "clever" (by-value) ABI, we might have to adjust the ABI of the
-        // variants to ensure they are consistent. This is because a downcast is
-        // semantically a NOP, and thus should not affect layout.
-        if matches!(abi, Abi::Scalar(..) | Abi::ScalarPair(..)) {
-            for variant in &mut layout_variants {
-                // We only do this for variants with fields; the others are not accessed anyway.
-                // Also do not overwrite any already existing "clever" ABIs.
-                if variant.fields.count() > 0 && matches!(variant.abi, Abi::Aggregate { .. }) {
-                    variant.abi = abi;
-                    // Also need to bump up the size and alignment, so that the entire value fits
-                    // in here.
-                    variant.size = cmp::max(variant.size, size);
-                    variant.align.abi = cmp::max(variant.align.abi, align.abi);
-                }
-            }
+            // At this point, we have handled all unions and
+            // structs. (We have also handled univariant enums
+            // that allow representation optimization.)
+            assert!(is_enum);
+            layout_of_enum(
+                self,
+                repr,
+                variants,
+                discr_range_of_repr,
+                discriminants,
+                dont_niche_optimize_enum,
+                dl,
+            )
         }
-
-        let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag);
-
-        let tagged_layout = LayoutS {
-            variants: Variants::Multiple {
-                tag,
-                tag_encoding: TagEncoding::Direct,
-                tag_field: 0,
-                variants: IndexVec::new(),
-            },
-            fields: FieldsShape::Arbitrary {
-                offsets: [Size::ZERO].into(),
-                memory_index: [0].into(),
-            },
-            largest_niche,
-            abi,
-            align,
-            size,
-            max_repr_align,
-            unadjusted_abi_align,
-        };
-
-        let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants };
-
-        let mut best_layout = match (tagged_layout, niche_filling_layout) {
-            (tl, Some(nl)) => {
-                // Pick the smaller layout; otherwise,
-                // pick the layout with the larger niche; otherwise,
-                // pick tagged as it has simpler codegen.
-                use cmp::Ordering::*;
-                let niche_size = |tmp_l: &TmpLayout<FieldIdx, VariantIdx>| {
-                    tmp_l.layout.largest_niche.map_or(0, |n| n.available(dl))
-                };
-                match (tl.layout.size.cmp(&nl.layout.size), niche_size(&tl).cmp(&niche_size(&nl))) {
-                    (Greater, _) => nl,
-                    (Equal, Less) => nl,
-                    _ => tl,
-                }
-            }
-            (tl, None) => tl,
-        };
-
-        // Now we can intern the variant layouts and store them in the enum layout.
-        best_layout.layout.variants = match best_layout.layout.variants {
-            Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
-                Variants::Multiple { tag, tag_encoding, tag_field, variants: best_layout.variants }
-            }
-            Variants::Single { .. } => {
-                panic!("encountered a single-variant enum during multi-variant layout")
-            }
-        };
-        Some(best_layout.layout)
     }
 
     fn layout_of_union<
@@ -872,6 +335,593 @@ pub trait LayoutCalculator {
     }
 }
 
+/// single-variant enums are just structs, if you think about it
+fn layout_of_struct<'a, LC, FieldIdx: Idx, VariantIdx: Idx, F>(
+    layout_calc: &LC,
+    repr: &ReprOptions,
+    variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
+    is_enum: bool,
+    is_unsafe_cell: bool,
+    scalar_valid_range: (Bound<u128>, Bound<u128>),
+    always_sized: bool,
+    dl: &TargetDataLayout,
+    present_first: VariantIdx,
+) -> Option<LayoutS<FieldIdx, VariantIdx>>
+where
+    LC: LayoutCalculator + ?Sized,
+    F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
+{
+    // Struct, or univariant enum equivalent to a struct.
+    // (Typechecking will reject discriminant-sizing attrs.)
+
+    let v = present_first;
+    let kind = if is_enum || variants[v].is_empty() || always_sized {
+        StructKind::AlwaysSized
+    } else {
+        StructKind::MaybeUnsized
+    };
+
+    let mut st = layout_calc.univariant(dl, &variants[v], repr, kind)?;
+    st.variants = Variants::Single { index: v };
+
+    if is_unsafe_cell {
+        let hide_niches = |scalar: &mut _| match scalar {
+            Scalar::Initialized { value, valid_range } => {
+                *valid_range = WrappingRange::full(value.size(dl))
+            }
+            // Already doesn't have any niches
+            Scalar::Union { .. } => {}
+        };
+        match &mut st.abi {
+            Abi::Uninhabited => {}
+            Abi::Scalar(scalar) => hide_niches(scalar),
+            Abi::ScalarPair(a, b) => {
+                hide_niches(a);
+                hide_niches(b);
+            }
+            Abi::Vector { element, count: _ } => hide_niches(element),
+            Abi::Aggregate { sized: _ } => {}
+        }
+        st.largest_niche = None;
+        return Some(st);
+    }
+
+    let (start, end) = scalar_valid_range;
+    match st.abi {
+        Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => {
+            // Enlarging validity ranges would result in missed
+            // optimizations, *not* wrongly assuming the inner
+            // value is valid. e.g. unions already enlarge validity ranges,
+            // because the values may be uninitialized.
+            //
+            // Because of that we only check that the start and end
+            // of the range is representable with this scalar type.
+
+            let max_value = scalar.size(dl).unsigned_int_max();
+            if let Bound::Included(start) = start {
+                // FIXME(eddyb) this might be incorrect - it doesn't
+                // account for wrap-around (end < start) ranges.
+                assert!(start <= max_value, "{start} > {max_value}");
+                scalar.valid_range_mut().start = start;
+            }
+            if let Bound::Included(end) = end {
+                // FIXME(eddyb) this might be incorrect - it doesn't
+                // account for wrap-around (end < start) ranges.
+                assert!(end <= max_value, "{end} > {max_value}");
+                scalar.valid_range_mut().end = end;
+            }
+
+            // Update `largest_niche` if we have introduced a larger niche.
+            let niche = Niche::from_scalar(dl, Size::ZERO, *scalar);
+            if let Some(niche) = niche {
+                match st.largest_niche {
+                    Some(largest_niche) => {
+                        // Replace the existing niche even if they're equal,
+                        // because this one is at a lower offset.
+                        if largest_niche.available(dl) <= niche.available(dl) {
+                            st.largest_niche = Some(niche);
+                        }
+                    }
+                    None => st.largest_niche = Some(niche),
+                }
+            }
+        }
+        _ => assert!(
+            start == Bound::Unbounded && end == Bound::Unbounded,
+            "nonscalar layout for layout_scalar_valid_range type: {st:#?}",
+        ),
+    }
+
+    Some(st)
+}
+
+fn layout_of_enum<'a, LC, FieldIdx: Idx, VariantIdx: Idx, F>(
+    layout_calc: &LC,
+    repr: &ReprOptions,
+    variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
+    discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
+    discriminants: impl Iterator<Item = (VariantIdx, i128)>,
+    dont_niche_optimize_enum: bool,
+    dl: &TargetDataLayout,
+) -> Option<LayoutS<FieldIdx, VariantIdx>>
+where
+    LC: LayoutCalculator + ?Sized,
+    F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
+{
+    // Until we've decided whether to use the tagged or
+    // niche filling LayoutS, we don't want to intern the
+    // variant layouts, so we can't store them in the
+    // overall LayoutS. Store the overall LayoutS
+    // and the variant LayoutSs here until then.
+    struct TmpLayout<FieldIdx: Idx, VariantIdx: Idx> {
+        layout: LayoutS<FieldIdx, VariantIdx>,
+        variants: IndexVec<VariantIdx, LayoutS<FieldIdx, VariantIdx>>,
+    }
+
+    let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> {
+        if dont_niche_optimize_enum {
+            return None;
+        }
+
+        if variants.len() < 2 {
+            return None;
+        }
+
+        let mut align = dl.aggregate_align;
+        let mut max_repr_align = repr.align;
+        let mut unadjusted_abi_align = align.abi;
+
+        let mut variant_layouts = variants
+            .iter_enumerated()
+            .map(|(j, v)| {
+                let mut st = layout_calc.univariant(dl, v, repr, StructKind::AlwaysSized)?;
+                st.variants = Variants::Single { index: j };
+
+                align = align.max(st.align);
+                max_repr_align = max_repr_align.max(st.max_repr_align);
+                unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
+
+                Some(st)
+            })
+            .collect::<Option<IndexVec<VariantIdx, _>>>()?;
+
+        let largest_variant_index = variant_layouts
+            .iter_enumerated()
+            .max_by_key(|(_i, layout)| layout.size.bytes())
+            .map(|(i, _layout)| i)?;
+
+        let all_indices = variants.indices();
+        let needs_disc =
+            |index: VariantIdx| index != largest_variant_index && !absent(&variants[index]);
+        let niche_variants = all_indices.clone().find(|v| needs_disc(*v)).unwrap()
+            ..=all_indices.rev().find(|v| needs_disc(*v)).unwrap();
+
+        let count =
+            (niche_variants.end().index() as u128 - niche_variants.start().index() as u128) + 1;
+
+        // Find the field with the largest niche
+        let (field_index, niche, (niche_start, niche_scalar)) = variants[largest_variant_index]
+            .iter()
+            .enumerate()
+            .filter_map(|(j, field)| Some((j, field.largest_niche?)))
+            .max_by_key(|(_, niche)| niche.available(dl))
+            .and_then(|(j, niche)| Some((j, niche, niche.reserve(dl, count)?)))?;
+        let niche_offset =
+            niche.offset + variant_layouts[largest_variant_index].fields.offset(field_index);
+        let niche_size = niche.value.size(dl);
+        let size = variant_layouts[largest_variant_index].size.align_to(align.abi);
+
+        let all_variants_fit = variant_layouts.iter_enumerated_mut().all(|(i, layout)| {
+            if i == largest_variant_index {
+                return true;
+            }
+
+            layout.largest_niche = None;
+
+            if layout.size <= niche_offset {
+                // This variant will fit before the niche.
+                return true;
+            }
+
+            // Determine if it'll fit after the niche.
+            let this_align = layout.align.abi;
+            let this_offset = (niche_offset + niche_size).align_to(this_align);
+
+            if this_offset + layout.size > size {
+                return false;
+            }
+
+            // It'll fit, but we need to make some adjustments.
+            match layout.fields {
+                FieldsShape::Arbitrary { ref mut offsets, .. } => {
+                    for offset in offsets.iter_mut() {
+                        *offset += this_offset;
+                    }
+                }
+                FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
+                    panic!("Layout of fields should be Arbitrary for variants")
+                }
+            }
+
+            // It can't be a Scalar or ScalarPair because the offset isn't 0.
+            if !layout.abi.is_uninhabited() {
+                layout.abi = Abi::Aggregate { sized: true };
+            }
+            layout.size += this_offset;
+
+            true
+        });
+
+        if !all_variants_fit {
+            return None;
+        }
+
+        let largest_niche = Niche::from_scalar(dl, niche_offset, niche_scalar);
+
+        let others_zst = variant_layouts
+            .iter_enumerated()
+            .all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO);
+        let same_size = size == variant_layouts[largest_variant_index].size;
+        let same_align = align == variant_layouts[largest_variant_index].align;
+
+        let abi = if variant_layouts.iter().all(|v| v.abi.is_uninhabited()) {
+            Abi::Uninhabited
+        } else if same_size && same_align && others_zst {
+            match variant_layouts[largest_variant_index].abi {
+                // When the total alignment and size match, we can use the
+                // same ABI as the scalar variant with the reserved niche.
+                Abi::Scalar(_) => Abi::Scalar(niche_scalar),
+                Abi::ScalarPair(first, second) => {
+                    // Only the niche is guaranteed to be initialised,
+                    // so use union layouts for the other primitive.
+                    if niche_offset == Size::ZERO {
+                        Abi::ScalarPair(niche_scalar, second.to_union())
+                    } else {
+                        Abi::ScalarPair(first.to_union(), niche_scalar)
+                    }
+                }
+                _ => Abi::Aggregate { sized: true },
+            }
+        } else {
+            Abi::Aggregate { sized: true }
+        };
+
+        let layout = LayoutS {
+            variants: Variants::Multiple {
+                tag: niche_scalar,
+                tag_encoding: TagEncoding::Niche {
+                    untagged_variant: largest_variant_index,
+                    niche_variants,
+                    niche_start,
+                },
+                tag_field: 0,
+                variants: IndexVec::new(),
+            },
+            fields: FieldsShape::Arbitrary {
+                offsets: [niche_offset].into(),
+                memory_index: [0].into(),
+            },
+            abi,
+            largest_niche,
+            size,
+            align,
+            max_repr_align,
+            unadjusted_abi_align,
+        };
+
+        Some(TmpLayout { layout, variants: variant_layouts })
+    };
+
+    let niche_filling_layout = calculate_niche_filling_layout();
+
+    let (mut min, mut max) = (i128::MAX, i128::MIN);
+    let discr_type = repr.discr_type();
+    let bits = Integer::from_attr(dl, discr_type).size().bits();
+    for (i, mut val) in discriminants {
+        if variants[i].iter().any(|f| f.abi.is_uninhabited()) {
+            continue;
+        }
+        if discr_type.is_signed() {
+            // sign extend the raw representation to be an i128
+            val = (val << (128 - bits)) >> (128 - bits);
+        }
+        if val < min {
+            min = val;
+        }
+        if val > max {
+            max = val;
+        }
+    }
+    // We might have no inhabited variants, so pretend there's at least one.
+    if (min, max) == (i128::MAX, i128::MIN) {
+        min = 0;
+        max = 0;
+    }
+    assert!(min <= max, "discriminant range is {min}...{max}");
+    let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max);
+
+    let mut align = dl.aggregate_align;
+    let mut max_repr_align = repr.align;
+    let mut unadjusted_abi_align = align.abi;
+
+    let mut size = Size::ZERO;
+
+    // We're interested in the smallest alignment, so start large.
+    let mut start_align = Align::from_bytes(256).unwrap();
+    assert_eq!(Integer::for_align(dl, start_align), None);
+
+    // repr(C) on an enum tells us to make a (tag, union) layout,
+    // so we need to grow the prefix alignment to be at least
+    // the alignment of the union. (This value is used both for
+    // determining the alignment of the overall enum, and the
+    // determining the alignment of the payload after the tag.)
+    let mut prefix_align = min_ity.align(dl).abi;
+    if repr.c() {
+        for fields in variants {
+            for field in fields {
+                prefix_align = prefix_align.max(field.align.abi);
+            }
+        }
+    }
+
+    // Create the set of structs that represent each variant.
+    let mut layout_variants = variants
+        .iter_enumerated()
+        .map(|(i, field_layouts)| {
+            let mut st = layout_calc.univariant(
+                dl,
+                field_layouts,
+                repr,
+                StructKind::Prefixed(min_ity.size(), prefix_align),
+            )?;
+            st.variants = Variants::Single { index: i };
+            // Find the first field we can't move later
+            // to make room for a larger discriminant.
+            for field_idx in st.fields.index_by_increasing_offset() {
+                let field = &field_layouts[FieldIdx::new(field_idx)];
+                if !field.is_1zst() {
+                    start_align = start_align.min(field.align.abi);
+                    break;
+                }
+            }
+            size = cmp::max(size, st.size);
+            align = align.max(st.align);
+            max_repr_align = max_repr_align.max(st.max_repr_align);
+            unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
+            Some(st)
+        })
+        .collect::<Option<IndexVec<VariantIdx, _>>>()?;
+
+    // Align the maximum variant size to the largest alignment.
+    size = size.align_to(align.abi);
+
+    // FIXME(oli-obk): deduplicate and harden these checks
+    if size.bytes() >= dl.obj_size_bound() {
+        return None;
+    }
+
+    let typeck_ity = Integer::from_attr(dl, repr.discr_type());
+    if typeck_ity < min_ity {
+        // It is a bug if Layout decided on a greater discriminant size than typeck for
+        // some reason at this point (based on values discriminant can take on). Mostly
+        // because this discriminant will be loaded, and then stored into variable of
+        // type calculated by typeck. Consider such case (a bug): typeck decided on
+        // byte-sized discriminant, but layout thinks we need a 16-bit to store all
+        // discriminant values. That would be a bug, because then, in codegen, in order
+        // to store this 16-bit discriminant into 8-bit sized temporary some of the
+        // space necessary to represent would have to be discarded (or layout is wrong
+        // on thinking it needs 16 bits)
+        panic!(
+            "layout decided on a larger discriminant type ({min_ity:?}) than typeck ({typeck_ity:?})"
+        );
+        // However, it is fine to make discr type however large (as an optimisation)
+        // after this point – we’ll just truncate the value we load in codegen.
+    }
+
+    // Check to see if we should use a different type for the
+    // discriminant. We can safely use a type with the same size
+    // as the alignment of the first field of each variant.
+    // We increase the size of the discriminant to avoid LLVM copying
+    // padding when it doesn't need to. This normally causes unaligned
+    // load/stores and excessive memcpy/memset operations. By using a
+    // bigger integer size, LLVM can be sure about its contents and
+    // won't be so conservative.
+
+    // Use the initial field alignment
+    let mut ity = if repr.c() || repr.int.is_some() {
+        min_ity
+    } else {
+        Integer::for_align(dl, start_align).unwrap_or(min_ity)
+    };
+
+    // If the alignment is not larger than the chosen discriminant size,
+    // don't use the alignment as the final size.
+    if ity <= min_ity {
+        ity = min_ity;
+    } else {
+        // Patch up the variants' first few fields.
+        let old_ity_size = min_ity.size();
+        let new_ity_size = ity.size();
+        for variant in &mut layout_variants {
+            match variant.fields {
+                FieldsShape::Arbitrary { ref mut offsets, .. } => {
+                    for i in offsets {
+                        if *i <= old_ity_size {
+                            assert_eq!(*i, old_ity_size);
+                            *i = new_ity_size;
+                        }
+                    }
+                    // We might be making the struct larger.
+                    if variant.size <= old_ity_size {
+                        variant.size = new_ity_size;
+                    }
+                }
+                FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
+                    panic!("encountered a non-arbitrary layout during enum layout")
+                }
+            }
+        }
+    }
+
+    let tag_mask = ity.size().unsigned_int_max();
+    let tag = Scalar::Initialized {
+        value: Primitive::Int(ity, signed),
+        valid_range: WrappingRange {
+            start: (min as u128 & tag_mask),
+            end: (max as u128 & tag_mask),
+        },
+    };
+    let mut abi = Abi::Aggregate { sized: true };
+
+    if layout_variants.iter().all(|v| v.abi.is_uninhabited()) {
+        abi = Abi::Uninhabited;
+    } else if tag.size(dl) == size {
+        // Make sure we only use scalar layout when the enum is entirely its
+        // own tag (i.e. it has no padding nor any non-ZST variant fields).
+        abi = Abi::Scalar(tag);
+    } else {
+        // Try to use a ScalarPair for all tagged enums.
+        // That's possible only if we can find a common primitive type for all variants.
+        let mut common_prim = None;
+        let mut common_prim_initialized_in_all_variants = true;
+        for (field_layouts, layout_variant) in iter::zip(variants, &layout_variants) {
+            let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
+                panic!("encountered a non-arbitrary layout during enum layout");
+            };
+            // We skip *all* ZST here and later check if we are good in terms of alignment.
+            // This lets us handle some cases involving aligned ZST.
+            let mut fields = iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst());
+            let (field, offset) = match (fields.next(), fields.next()) {
+                (None, None) => {
+                    common_prim_initialized_in_all_variants = false;
+                    continue;
+                }
+                (Some(pair), None) => pair,
+                _ => {
+                    common_prim = None;
+                    break;
+                }
+            };
+            let prim = match field.abi {
+                Abi::Scalar(scalar) => {
+                    common_prim_initialized_in_all_variants &=
+                        matches!(scalar, Scalar::Initialized { .. });
+                    scalar.primitive()
+                }
+                _ => {
+                    common_prim = None;
+                    break;
+                }
+            };
+            if let Some(pair) = common_prim {
+                // This is pretty conservative. We could go fancier
+                // by conflating things like i32 and u32, or even
+                // realising that (u8, u8) could just cohabit with
+                // u16 or even u32.
+                if pair != (prim, offset) {
+                    common_prim = None;
+                    break;
+                }
+            } else {
+                common_prim = Some((prim, offset));
+            }
+        }
+        if let Some((prim, offset)) = common_prim {
+            let prim_scalar = if common_prim_initialized_in_all_variants {
+                let size = prim.size(dl);
+                assert!(size.bits() <= 128);
+                Scalar::Initialized { value: prim, valid_range: WrappingRange::full(size) }
+            } else {
+                // Common prim might be uninit.
+                Scalar::Union { value: prim }
+            };
+            let pair = layout_calc.scalar_pair::<FieldIdx, VariantIdx>(tag, prim_scalar);
+            let pair_offsets = match pair.fields {
+                FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
+                    assert_eq!(memory_index.raw, [0, 1]);
+                    offsets
+                }
+                _ => panic!("encountered a non-arbitrary layout during enum layout"),
+            };
+            if pair_offsets[FieldIdx::new(0)] == Size::ZERO
+                && pair_offsets[FieldIdx::new(1)] == *offset
+                && align == pair.align
+                && size == pair.size
+            {
+                // We can use `ScalarPair` only when it matches our
+                // already computed layout (including `#[repr(C)]`).
+                abi = pair.abi;
+            }
+        }
+    }
+
+    // If we pick a "clever" (by-value) ABI, we might have to adjust the ABI of the
+    // variants to ensure they are consistent. This is because a downcast is
+    // semantically a NOP, and thus should not affect layout.
+    if matches!(abi, Abi::Scalar(..) | Abi::ScalarPair(..)) {
+        for variant in &mut layout_variants {
+            // We only do this for variants with fields; the others are not accessed anyway.
+            // Also do not overwrite any already existing "clever" ABIs.
+            if variant.fields.count() > 0 && matches!(variant.abi, Abi::Aggregate { .. }) {
+                variant.abi = abi;
+                // Also need to bump up the size and alignment, so that the entire value fits
+                // in here.
+                variant.size = cmp::max(variant.size, size);
+                variant.align.abi = cmp::max(variant.align.abi, align.abi);
+            }
+        }
+    }
+
+    let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag);
+
+    let tagged_layout = LayoutS {
+        variants: Variants::Multiple {
+            tag,
+            tag_encoding: TagEncoding::Direct,
+            tag_field: 0,
+            variants: IndexVec::new(),
+        },
+        fields: FieldsShape::Arbitrary { offsets: [Size::ZERO].into(), memory_index: [0].into() },
+        largest_niche,
+        abi,
+        align,
+        size,
+        max_repr_align,
+        unadjusted_abi_align,
+    };
+
+    let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants };
+
+    let mut best_layout = match (tagged_layout, niche_filling_layout) {
+        (tl, Some(nl)) => {
+            // Pick the smaller layout; otherwise,
+            // pick the layout with the larger niche; otherwise,
+            // pick tagged as it has simpler codegen.
+            use cmp::Ordering::*;
+            let niche_size = |tmp_l: &TmpLayout<FieldIdx, VariantIdx>| {
+                tmp_l.layout.largest_niche.map_or(0, |n| n.available(dl))
+            };
+            match (tl.layout.size.cmp(&nl.layout.size), niche_size(&tl).cmp(&niche_size(&nl))) {
+                (Greater, _) => nl,
+                (Equal, Less) => nl,
+                _ => tl,
+            }
+        }
+        (tl, None) => tl,
+    };
+
+    // Now we can intern the variant layouts and store them in the enum layout.
+    best_layout.layout.variants = match best_layout.layout.variants {
+        Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
+            Variants::Multiple { tag, tag_encoding, tag_field, variants: best_layout.variants }
+        }
+        Variants::Single { .. } => {
+            panic!("encountered a single-variant enum during multi-variant layout")
+        }
+    };
+    Some(best_layout.layout)
+}
+
 /// Determines towards which end of a struct layout optimizations will try to place the best niches.
 enum NicheBias {
     Start,
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 2d61f3bceec..1f6d47ab453 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -670,7 +670,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let params = arena_vec![self; param];
 
         let body = self.lower_body(move |this| {
-            this.coroutine_kind = Some(hir::CoroutineKind::Async(async_coroutine_source));
+            this.coroutine_kind = Some(hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::Async,
+                async_coroutine_source,
+            ));
 
             let old_ctx = this.task_context;
             this.task_context = Some(task_context_hid);
@@ -724,7 +727,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         });
 
         let body = self.lower_body(move |this| {
-            this.coroutine_kind = Some(hir::CoroutineKind::Gen(coroutine_source));
+            this.coroutine_kind = Some(hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::Gen,
+                coroutine_source,
+            ));
 
             let res = body(this);
             (&[], res)
@@ -802,7 +808,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let params = arena_vec![self; param];
 
         let body = self.lower_body(move |this| {
-            this.coroutine_kind = Some(hir::CoroutineKind::AsyncGen(async_coroutine_source));
+            this.coroutine_kind = Some(hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::AsyncGen,
+                async_coroutine_source,
+            ));
 
             let old_ctx = this.task_context;
             this.task_context = Some(task_context_hid);
@@ -888,9 +897,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let full_span = expr.span.to(await_kw_span);
 
         let is_async_gen = match self.coroutine_kind {
-            Some(hir::CoroutineKind::Async(_)) => false,
-            Some(hir::CoroutineKind::AsyncGen(_)) => true,
-            Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) | None => {
+            Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => false,
+            Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => true,
+            Some(hir::CoroutineKind::Coroutine)
+            | Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _))
+            | None => {
                 return hir::ExprKind::Err(self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks {
                     await_kw_span,
                     item_span: self.current_item,
@@ -1123,9 +1134,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 Some(movability)
             }
             Some(
-                hir::CoroutineKind::Gen(_)
-                | hir::CoroutineKind::Async(_)
-                | hir::CoroutineKind::AsyncGen(_),
+                hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
+                | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
+                | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _),
             ) => {
                 panic!("non-`async`/`gen` closure body turned `async`/`gen` during lowering");
             }
@@ -1638,9 +1649,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
         let is_async_gen = match self.coroutine_kind {
-            Some(hir::CoroutineKind::Gen(_)) => false,
-            Some(hir::CoroutineKind::AsyncGen(_)) => true,
-            Some(hir::CoroutineKind::Async(_)) => {
+            Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => false,
+            Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => true,
+            Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
                 return hir::ExprKind::Err(
                     self.tcx.sess.emit_err(AsyncCoroutinesNotSupported { span }),
                 );
@@ -1803,7 +1814,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     arena_vec![self; head],
                 )
             }
-            ForLoopKind::ForAwait => self.arena.alloc(head),
+            // ` unsafe { Pin::new_unchecked(&mut into_async_iter(<head>)) }`
+            ForLoopKind::ForAwait => {
+                // `::core::async_iter::IntoAsyncIterator::into_async_iter(<head>)`
+                let iter = self.expr_call_lang_item_fn(
+                    head_span,
+                    hir::LangItem::IntoAsyncIterIntoIter,
+                    arena_vec![self; head],
+                );
+                let iter = self.expr_mut_addr_of(head_span, iter);
+                // `Pin::new_unchecked(...)`
+                let iter = self.arena.alloc(self.expr_call_lang_item_fn_mut(
+                    head_span,
+                    hir::LangItem::PinNewUnchecked,
+                    arena_vec![self; iter],
+                ));
+                // `unsafe { ... }`
+                let iter = self.arena.alloc(self.expr_unsafe(iter));
+                iter
+            }
         };
 
         let match_expr = self.arena.alloc(self.expr_match(
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index 6731ef12306..e527a91eb6f 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -1,5 +1,5 @@
 use rustc_errors::{
-    struct_span_err, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, MultiSpan,
+    struct_span_err, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, MultiSpan,
 };
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
@@ -12,7 +12,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         place: &str,
         borrow_place: &str,
         value_place: &str,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         self.infcx.tcx.sess.create_err(crate::session_diagnostics::MoveBorrow {
             place,
             span,
@@ -28,7 +28,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         desc: &str,
         borrow_span: Span,
         borrow_desc: &str,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let mut err = struct_span_err!(
             self,
             span,
@@ -50,7 +50,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         old_loan_span: Span,
         old_opt_via: &str,
         old_load_end_span: Option<Span>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") };
         let mut err = struct_span_err!(
             self,
@@ -97,7 +97,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         desc: &str,
         old_loan_span: Span,
         old_load_end_span: Option<Span>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let mut err = struct_span_err!(
             self,
             new_loan_span,
@@ -130,7 +130,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         noun_old: &str,
         old_opt_via: &str,
         previous_end_span: Option<Span>,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let mut err = struct_span_err!(
             self,
             new_loan_span,
@@ -162,7 +162,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         old_opt_via: &str,
         previous_end_span: Option<Span>,
         second_borrow_desc: &str,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let mut err = struct_span_err!(
             self,
             new_loan_span,
@@ -194,7 +194,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         kind_old: &str,
         msg_old: &str,
         old_load_end_span: Option<Span>,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") };
         let mut err = struct_span_err!(
             self,
@@ -235,7 +235,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         span: Span,
         borrow_span: Span,
         desc: &str,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let mut err = struct_span_err!(
             self,
             span,
@@ -254,16 +254,12 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         span: Span,
         desc: &str,
         is_arg: bool,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let msg = if is_arg { "to immutable argument" } else { "twice to immutable variable" };
         struct_span_err!(self, span, E0384, "cannot assign {} {}", msg, desc)
     }
 
-    pub(crate) fn cannot_assign(
-        &self,
-        span: Span,
-        desc: &str,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    pub(crate) fn cannot_assign(&self, span: Span, desc: &str) -> DiagnosticBuilder<'tcx> {
         struct_span_err!(self, span, E0594, "cannot assign to {}", desc)
     }
 
@@ -271,7 +267,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         move_from_span: Span,
         move_from_desc: &str,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         struct_span_err!(self, move_from_span, E0507, "cannot move out of {}", move_from_desc)
     }
 
@@ -283,7 +279,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         move_from_span: Span,
         ty: Ty<'_>,
         is_index: Option<bool>,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let type_name = match (&ty.kind(), is_index) {
             (&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array",
             (&ty::Slice(_), _) => "slice",
@@ -305,7 +301,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         move_from_span: Span,
         container_ty: Ty<'_>,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let mut err = struct_span_err!(
             self,
             move_from_span,
@@ -323,7 +319,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         verb: &str,
         optional_adverb_for_moved: &str,
         moved_path: Option<String>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default();
 
         struct_span_err!(
@@ -342,7 +338,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         span: Span,
         path: &str,
         reason: &str,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         struct_span_err!(self, span, E0596, "cannot borrow {} as mutable{}", path, reason,)
     }
 
@@ -353,7 +349,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         immutable_place: &str,
         immutable_section: &str,
         action: &str,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let mut err = struct_span_err!(
             self,
             mutate_span,
@@ -372,7 +368,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         span: Span,
         yield_span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind;
         let mut err = struct_span_err!(
             self,
@@ -387,7 +383,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
     pub(crate) fn cannot_borrow_across_destructor(
         &self,
         borrow_span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         struct_span_err!(
             self,
             borrow_span,
@@ -400,7 +396,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         span: Span,
         path: &str,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         struct_span_err!(self, span, E0597, "{} does not live long enough", path,)
     }
 
@@ -410,7 +406,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         return_kind: &str,
         reference_desc: &str,
         path_desc: &str,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let mut err = struct_span_err!(
             self,
             span,
@@ -436,7 +432,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         borrowed_path: &str,
         capture_span: Span,
         scope: &str,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let mut err = struct_span_err!(
             self,
             closure_span,
@@ -452,14 +448,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
     pub(crate) fn thread_local_value_does_not_live_long_enough(
         &self,
         span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         struct_span_err!(self, span, E0712, "thread-local variable borrowed past end of function",)
     }
 
     pub(crate) fn temporary_value_borrowed_for_too_long(
         &self,
         span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         struct_span_err!(self, span, E0716, "temporary value dropped while borrowed",)
     }
 
@@ -470,7 +466,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         self.infcx.tcx.sess.struct_span_err_with_code(sp, msg, code)
     }
 }
@@ -479,7 +475,7 @@ pub(crate) fn borrowed_data_escapes_closure<'tcx>(
     tcx: TyCtxt<'tcx>,
     escape_span: Span,
     escapes_from: &str,
-) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'tcx> {
     struct_span_err!(
         tcx.sess,
         escape_span,
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index 60dbf7dc055..e6881316a8f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -1,7 +1,7 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
-use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::DiagnosticBuilder;
 use rustc_infer::infer::canonical::Canonical;
 use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError;
 use rustc_infer::infer::region_constraints::Constraint;
@@ -147,11 +147,7 @@ impl<'tcx> ToUniverseInfo<'tcx> for ! {
 trait TypeOpInfo<'tcx> {
     /// Returns an error to be reported if rerunning the type op fails to
     /// recover the error's cause.
-    fn fallback_error(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
 
     fn base_universe(&self) -> ty::UniverseIndex;
 
@@ -161,7 +157,7 @@ trait TypeOpInfo<'tcx> {
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>;
+    ) -> Option<DiagnosticBuilder<'tcx>>;
 
     #[instrument(level = "debug", skip(self, mbcx))]
     fn report_error(
@@ -224,11 +220,7 @@ struct PredicateQuery<'tcx> {
 }
 
 impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
-    fn fallback_error(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         tcx.sess.create_err(HigherRankedLifetimeError {
             cause: Some(HigherRankedErrorCause::CouldNotProve {
                 predicate: self.canonical_query.value.value.predicate.to_string(),
@@ -247,7 +239,7 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         let (infcx, key, _) =
             mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
         let ocx = ObligationCtxt::new(&infcx);
@@ -265,11 +257,7 @@ impl<'tcx, T> TypeOpInfo<'tcx> for NormalizeQuery<'tcx, T>
 where
     T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx,
 {
-    fn fallback_error(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         tcx.sess.create_err(HigherRankedLifetimeError {
             cause: Some(HigherRankedErrorCause::CouldNotNormalize {
                 value: self.canonical_query.value.value.value.to_string(),
@@ -288,7 +276,7 @@ where
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         let (infcx, key, _) =
             mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
         let ocx = ObligationCtxt::new(&infcx);
@@ -312,11 +300,7 @@ struct AscribeUserTypeQuery<'tcx> {
 }
 
 impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
-    fn fallback_error(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         // FIXME: This error message isn't great, but it doesn't show up in the existing UI tests,
         // and is only the fallback when the nice error fails. Consider improving this some more.
         tcx.sess.create_err(HigherRankedLifetimeError { cause: None, span })
@@ -332,7 +316,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         let (infcx, key, _) =
             mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
         let ocx = ObligationCtxt::new(&infcx);
@@ -342,11 +326,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
 }
 
 impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
-    fn fallback_error(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         // FIXME: This error message isn't great, but it doesn't show up in the existing UI tests,
         // and is only the fallback when the nice error fails. Consider improving this some more.
         tcx.sess.create_err(HigherRankedLifetimeError { cause: None, span })
@@ -362,7 +342,7 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
         _cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         try_extract_error_from_region_constraints(
             mbcx.infcx,
             placeholder_region,
@@ -383,7 +363,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
     ocx: &ObligationCtxt<'_, 'tcx>,
     placeholder_region: ty::Region<'tcx>,
     error_region: Option<ty::Region<'tcx>>,
-) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+) -> Option<DiagnosticBuilder<'tcx>> {
     // We generally shouldn't have errors here because the query was
     // already run, but there's no point using `span_delayed_bug`
     // when we're going to emit an error here anyway.
@@ -407,7 +387,7 @@ fn try_extract_error_from_region_constraints<'tcx>(
     region_constraints: &RegionConstraintData<'tcx>,
     mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin,
     mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex,
-) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+) -> Option<DiagnosticBuilder<'tcx>> {
     let placeholder_universe = match placeholder_region.kind() {
         ty::RePlaceholder(p) => p.universe,
         ty::ReVar(vid) => universe_of_region(vid),
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index db0f4559a6b..1577b2896c3 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1,13 +1,11 @@
 use either::Either;
-use hir::PatField;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{
-    struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
-};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
+use rustc_hir::{CoroutineDesugaring, PatField};
 use rustc_hir::{CoroutineKind, CoroutineSource, LangItem};
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::hir::nested_filter::OnlyBodies;
@@ -324,7 +322,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &mut self,
         mpi: MovePathIndex,
         move_span: Span,
-        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        err: &mut DiagnosticBuilder<'_>,
         in_pattern: &mut bool,
         move_spans: UseSpans<'_>,
     ) {
@@ -483,7 +481,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         desired_action: InitializationRequiringAction,
         span: Span,
         use_spans: UseSpans<'tcx>,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         // We need all statements in the body where the binding was assigned to to later find all
         // the branching code paths where the binding *wasn't* assigned to.
         let inits = &self.move_data.init_path_map[mpi];
@@ -873,7 +871,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         location: Location,
         (place, _span): (Place<'tcx>, Span),
         borrow: &BorrowData<'tcx>,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let borrow_spans = self.retrieve_borrow_spans(borrow);
         let borrow_span = borrow_spans.args_or_use();
 
@@ -921,7 +919,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         (place, span): (Place<'tcx>, Span),
         gen_borrow_kind: BorrowKind,
         issued_borrow: &BorrowData<'tcx>,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let issued_spans = self.retrieve_borrow_spans(issued_borrow);
         let issued_span = issued_spans.args_or_use();
 
@@ -2025,7 +2023,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         drop_span: Span,
         borrow_spans: UseSpans<'tcx>,
         explanation: BorrowExplanation<'tcx>,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         debug!(
             "report_local_value_does_not_live_long_enough(\
              {:?}, {:?}, {:?}, {:?}, {:?}\
@@ -2200,7 +2198,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &mut self,
         drop_span: Span,
         borrow_span: Span,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         debug!(
             "report_thread_local_value_does_not_live_long_enough(\
              {:?}, {:?}\
@@ -2228,7 +2226,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         borrow_spans: UseSpans<'tcx>,
         proper_span: Span,
         explanation: BorrowExplanation<'tcx>,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
             explanation
         {
@@ -2395,7 +2393,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         return_span: Span,
         category: ConstraintCategory<'tcx>,
         opt_place_desc: Option<&String>,
-    ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'cx>> {
         let return_kind = match category {
             ConstraintCategory::Return(_) => "return",
             ConstraintCategory::Yield => "yield",
@@ -2490,7 +2488,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         constraint_span: Span,
         captured_var: &str,
         scope: &str,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let tcx = self.infcx.tcx;
         let args_span = use_span.args_or_use();
 
@@ -2516,27 +2514,29 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         };
         let kind = match use_span.coroutine_kind() {
             Some(coroutine_kind) => match coroutine_kind {
-                CoroutineKind::Gen(kind) => match kind {
+                CoroutineKind::Desugared(CoroutineDesugaring::Gen, kind) => match kind {
                     CoroutineSource::Block => "gen block",
                     CoroutineSource::Closure => "gen closure",
                     CoroutineSource::Fn => {
                         bug!("gen block/closure expected, but gen function found.")
                     }
                 },
-                CoroutineKind::AsyncGen(kind) => match kind {
+                CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, kind) => match kind {
                     CoroutineSource::Block => "async gen block",
                     CoroutineSource::Closure => "async gen closure",
                     CoroutineSource::Fn => {
                         bug!("gen block/closure expected, but gen function found.")
                     }
                 },
-                CoroutineKind::Async(async_kind) => match async_kind {
-                    CoroutineSource::Block => "async block",
-                    CoroutineSource::Closure => "async closure",
-                    CoroutineSource::Fn => {
-                        bug!("async block/closure expected, but async function found.")
+                CoroutineKind::Desugared(CoroutineDesugaring::Async, async_kind) => {
+                    match async_kind {
+                        CoroutineSource::Block => "async block",
+                        CoroutineSource::Closure => "async closure",
+                        CoroutineSource::Fn => {
+                            bug!("async block/closure expected, but async function found.")
+                        }
                     }
-                },
+                }
                 CoroutineKind::Coroutine => "coroutine",
             },
             None => "closure",
@@ -2566,7 +2566,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
             ConstraintCategory::CallArgument(_) => {
                 fr_name.highlight_region_name(&mut err);
-                if matches!(use_span.coroutine_kind(), Some(CoroutineKind::Async(_))) {
+                if matches!(
+                    use_span.coroutine_kind(),
+                    Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, _))
+                ) {
                     err.note(
                         "async blocks are not executed immediately and must either take a \
                          reference or ownership of outside variables they use",
@@ -2593,7 +2596,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         upvar_span: Span,
         upvar_name: Symbol,
         escape_span: Span,
-    ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'cx> {
         let tcx = self.infcx.tcx;
 
         let escapes_from = tcx.def_descr(self.mir_def_id().to_def_id());
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 43487b85a7b..f3b21d22c1a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -1,4 +1,4 @@
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty};
 use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
@@ -288,7 +288,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         &mut self,
         place: Place<'tcx>,
         span: Span,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'a> {
         let description = if place.projection.len() == 1 {
             format!("static item {}", self.describe_any_place(place.as_ref()))
         } else {
@@ -310,7 +310,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         deref_target_place: Place<'tcx>,
         span: Span,
         use_spans: Option<UseSpans<'tcx>>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'a> {
         // Inspect the type of the content behind the
         // borrow to provide feedback about why this
         // was a move rather than a copy.
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index c3c1f1293d2..506933c470e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -1,5 +1,5 @@
 use hir::ExprKind;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::Node;
@@ -711,7 +711,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
     fn construct_mut_suggestion_for_local_binding_patterns(
         &self,
-        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        err: &mut DiagnosticBuilder<'_>,
         local: Local,
     ) {
         let local_decl = &self.body.local_decls[local];
@@ -1025,7 +1025,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_using_iter_mut(&self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>) {
+    fn suggest_using_iter_mut(&self, err: &mut DiagnosticBuilder<'_>) {
         let source = self.body.source;
         let hir = self.infcx.tcx.hir();
         if let InstanceDef::Item(def_id) = source.instance
@@ -1067,12 +1067,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_make_local_mut(
-        &self,
-        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
-        local: Local,
-        name: Symbol,
-    ) {
+    fn suggest_make_local_mut(&self, err: &mut DiagnosticBuilder<'_>, local: Local, name: Symbol) {
         let local_decl = &self.body.local_decls[local];
 
         let (pointer_sigil, pointer_desc) =
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 759f5e910f7..f0b773b17ba 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -3,7 +3,7 @@
 //! Error reporting machinery for lifetime errors.
 
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::Res::Def;
 use rustc_hir::def_id::DefId;
@@ -202,7 +202,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     // and the span which bounded to the trait for adding 'static lifetime suggestion
     fn suggest_static_lifetime_for_gat_from_hrtb(
         &self,
-        diag: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        diag: &mut DiagnosticBuilder<'_>,
         lower_bound: RegionVid,
     ) {
         let mut suggestions = vec![];
@@ -573,7 +573,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         &self,
         errci: &ErrorConstraintInfo<'tcx>,
         kind: ReturnConstraint,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
 
         let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty;
@@ -645,7 +645,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     fn report_escaping_data_error(
         &self,
         errci: &ErrorConstraintInfo<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let ErrorConstraintInfo { span, category, .. } = errci;
 
         let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
@@ -744,10 +744,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     ///    |     ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
     ///    |                    is returning data with lifetime `'b`
     /// ```
-    fn report_general_error(
-        &self,
-        errci: &ErrorConstraintInfo<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> DiagnosticBuilder<'tcx> {
         let ErrorConstraintInfo {
             fr,
             fr_is_local,
@@ -1049,7 +1046,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 ..
             }) => {
                 let body = map.body(*body);
-                if !matches!(body.coroutine_kind, Some(hir::CoroutineKind::Async(..))) {
+                if !matches!(
+                    body.coroutine_kind,
+                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _))
+                ) {
                     closure_span = Some(expr.span.shrink_to_lo());
                 }
             }
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 8441dfaa7df..78d84f468e0 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -684,39 +684,46 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
                     hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)),
                 };
                 let mir_description = match hir.body(body).coroutine_kind {
-                    Some(hir::CoroutineKind::Async(src)) => match src {
-                        hir::CoroutineSource::Block => " of async block",
-                        hir::CoroutineSource::Closure => " of async closure",
-                        hir::CoroutineSource::Fn => {
-                            let parent_item =
-                                tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
-                            let output = &parent_item
-                                .fn_decl()
-                                .expect("coroutine lowered from async fn should be in fn")
-                                .output;
-                            span = output.span();
-                            if let hir::FnRetTy::Return(ret) = output {
-                                hir_ty = Some(self.get_future_inner_return_ty(*ret));
+                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, src)) => {
+                        match src {
+                            hir::CoroutineSource::Block => " of async block",
+                            hir::CoroutineSource::Closure => " of async closure",
+                            hir::CoroutineSource::Fn => {
+                                let parent_item =
+                                    tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
+                                let output = &parent_item
+                                    .fn_decl()
+                                    .expect("coroutine lowered from async fn should be in fn")
+                                    .output;
+                                span = output.span();
+                                if let hir::FnRetTy::Return(ret) = output {
+                                    hir_ty = Some(self.get_future_inner_return_ty(*ret));
+                                }
+                                " of async function"
                             }
-                            " of async function"
                         }
-                    },
-                    Some(hir::CoroutineKind::Gen(src)) => match src {
-                        hir::CoroutineSource::Block => " of gen block",
-                        hir::CoroutineSource::Closure => " of gen closure",
-                        hir::CoroutineSource::Fn => {
-                            let parent_item =
-                                tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
-                            let output = &parent_item
-                                .fn_decl()
-                                .expect("coroutine lowered from gen fn should be in fn")
-                                .output;
-                            span = output.span();
-                            " of gen function"
+                    }
+                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, src)) => {
+                        match src {
+                            hir::CoroutineSource::Block => " of gen block",
+                            hir::CoroutineSource::Closure => " of gen closure",
+                            hir::CoroutineSource::Fn => {
+                                let parent_item =
+                                    tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
+                                let output = &parent_item
+                                    .fn_decl()
+                                    .expect("coroutine lowered from gen fn should be in fn")
+                                    .output;
+                                span = output.span();
+                                " of gen function"
+                            }
                         }
-                    },
+                    }
 
-                    Some(hir::CoroutineKind::AsyncGen(src)) => match src {
+                    Some(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::AsyncGen,
+                        src,
+                    )) => match src {
                         hir::CoroutineSource::Block => " of async gen block",
                         hir::CoroutineSource::Closure => " of async gen closure",
                         hir::CoroutineSource::Fn => {
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 43f48f579a3..cf2d2ca7465 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -2407,8 +2407,8 @@ mod error {
         /// when errors in the map are being re-added to the error buffer so that errors with the
         /// same primary span come out in a consistent order.
         buffered_move_errors:
-            BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'tcx, ErrorGuaranteed>)>,
-        buffered_mut_errors: FxIndexMap<Span, (DiagnosticBuilder<'tcx, ErrorGuaranteed>, usize)>,
+            BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>)>,
+        buffered_mut_errors: FxIndexMap<Span, (DiagnosticBuilder<'tcx>, usize)>,
         /// Diagnostics to be reported buffer.
         buffered: Vec<Diagnostic>,
         /// Set to Some if we emit an error during borrowck
@@ -2426,7 +2426,7 @@ mod error {
             }
         }
 
-        pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorGuaranteed>) {
+        pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) {
             if let None = self.tainted_by_errors {
                 self.tainted_by_errors = Some(self.tcx.sess.span_delayed_bug(
                     t.span.clone_ignoring_labels(),
@@ -2446,7 +2446,7 @@ mod error {
     }
 
     impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
-        pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorGuaranteed>) {
+        pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) {
             self.errors.buffer_error(t);
         }
 
@@ -2457,7 +2457,7 @@ mod error {
         pub fn buffer_move_error(
             &mut self,
             move_out_indices: Vec<MoveOutIndex>,
-            place_and_err: (PlaceRef<'tcx>, DiagnosticBuilder<'tcx, ErrorGuaranteed>),
+            place_and_err: (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>),
         ) -> bool {
             if let Some((_, diag)) =
                 self.errors.buffered_move_errors.insert(move_out_indices, place_and_err)
@@ -2473,16 +2473,11 @@ mod error {
         pub fn get_buffered_mut_error(
             &mut self,
             span: Span,
-        ) -> Option<(DiagnosticBuilder<'tcx, ErrorGuaranteed>, usize)> {
+        ) -> Option<(DiagnosticBuilder<'tcx>, usize)> {
             self.errors.buffered_mut_errors.remove(&span)
         }
 
-        pub fn buffer_mut_error(
-            &mut self,
-            span: Span,
-            t: DiagnosticBuilder<'tcx, ErrorGuaranteed>,
-            count: usize,
-        ) {
+        pub fn buffer_mut_error(&mut self, span: Span, t: DiagnosticBuilder<'tcx>, count: usize) {
             self.errors.buffered_mut_errors.insert(span, (t, count));
         }
 
@@ -2517,7 +2512,7 @@ mod error {
         pub fn has_move_error(
             &self,
             move_out_indices: &[MoveOutIndex],
-        ) -> Option<&(PlaceRef<'tcx>, DiagnosticBuilder<'cx, ErrorGuaranteed>)> {
+        ) -> Option<&(PlaceRef<'tcx>, DiagnosticBuilder<'cx>)> {
             self.errors.buffered_move_errors.get(move_out_indices)
         }
     }
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index c5fd535b036..2f23146096f 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -8,9 +8,7 @@ use rustc_ast::{
     FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait,
 };
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{
-    Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult, SingleLabelManySpans,
-};
+use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan, PResult, SingleLabelManySpans};
 use rustc_expand::base::{self, *};
 use rustc_parse_format as parse;
 use rustc_span::symbol::{Ident, Symbol};
@@ -726,7 +724,7 @@ fn report_redundant_format_arguments<'a>(
     args: &FormatArguments,
     used: &[bool],
     placeholders: Vec<(Span, &str)>,
-) -> Option<DiagnosticBuilder<'a, ErrorGuaranteed>> {
+) -> Option<DiagnosticBuilder<'a>> {
     let mut fmt_arg_indices = vec![];
     let mut args_spans = vec![];
     let mut fmt_spans = vec![];
@@ -885,7 +883,6 @@ fn report_invalid_references(
             highlight: SingleLabelManySpans {
                 spans: args.explicit_args().iter().map(|arg| arg.expr.span).collect(),
                 label: "",
-                kind: rustc_errors::LabelKind::Label,
             },
         });
         // Point out `{:.*}` placeholders: those take an extra argument.
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index ec843a3a0cd..d760cea59a7 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -5,7 +5,7 @@ use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, attr, GenericParamKind};
 use rustc_ast_pretty::pprust;
-use rustc_errors::Applicability;
+use rustc_errors::{Applicability, DiagnosticBuilder, Level};
 use rustc_expand::base::*;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Span};
@@ -391,15 +391,14 @@ pub fn expand_test_or_bench(
 fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>) {
     let dcx = cx.sess.dcx();
     let msg = "the `#[test]` attribute may only be used on a non-associated function";
-    let mut err = match item.map(|i| &i.kind) {
+    let level = match item.map(|i| &i.kind) {
         // These were a warning before #92959 and need to continue being that to avoid breaking
         // stable user code (#94508).
-        Some(ast::ItemKind::MacCall(_)) => dcx.struct_span_warn(attr_sp, msg),
-        // `.forget_guarantee()` needed to get these two arms to match types. Because of how
-        // locally close the `.emit()` call is I'm comfortable with it, but if it can be
-        // reworked in the future to not need it, it'd be nice.
-        _ => dcx.struct_span_err(attr_sp, msg).forget_guarantee(),
+        Some(ast::ItemKind::MacCall(_)) => Level::Warning(None),
+        _ => Level::Error { lint: false },
     };
+    let mut err = DiagnosticBuilder::<()>::new(dcx, level, msg);
+    err.set_span(attr_sp);
     if let Some(item) = item {
         err.span_label(
             item.span,
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index df40a5eb475..8d3be19839e 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -179,7 +179,7 @@ pub(crate) fn compile_fn(
                 let early_dcx = rustc_session::EarlyDiagCtxt::new(
                     rustc_session::config::ErrorOutputType::default(),
                 );
-                early_dcx.early_error(format!(
+                early_dcx.early_fatal(format!(
                     "backend implementation limit exceeded while compiling {name}",
                     name = codegened_func.symbol_name
                 ));
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 6b7e38a6cb1..8db97d577ca 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -102,7 +102,7 @@ pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>);
 impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for ParseTargetMachineConfig<'_> {
     fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let diag: DiagnosticBuilder<'_, G> = self.0.into_diagnostic(dcx, level);
-        let (message, _) = diag.styled_message().first().expect("`LlvmError` with no message");
+        let (message, _) = diag.messages().first().expect("`LlvmError` with no message");
         let message = dcx.eagerly_translate_to_string(message.clone(), diag.args());
 
         let mut diag =
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 0442bef8a44..53ae085a721 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -15,7 +15,7 @@ use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::Emitter;
 use rustc_errors::{translation::Translate, DiagCtxt, DiagnosticId, FatalError, Level};
-use rustc_errors::{DiagnosticMessage, Style};
+use rustc_errors::{DiagnosticBuilder, DiagnosticMessage, Style};
 use rustc_fs_util::link_or_copy;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_incremental::{
@@ -986,7 +986,7 @@ pub struct CguMessage;
 type DiagnosticArgName<'source> = Cow<'source, str>;
 
 struct Diagnostic {
-    msg: Vec<(DiagnosticMessage, Style)>,
+    msgs: Vec<(DiagnosticMessage, Style)>,
     args: FxHashMap<DiagnosticArgName<'static>, rustc_errors::DiagnosticArgValue<'static>>,
     code: Option<DiagnosticId>,
     lvl: Level,
@@ -1799,14 +1799,14 @@ impl Emitter for SharedEmitter {
         let args: FxHashMap<Cow<'_, str>, rustc_errors::DiagnosticArgValue<'_>> =
             diag.args().map(|(name, arg)| (name.clone(), arg.clone())).collect();
         drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
-            msg: diag.message.clone(),
+            msgs: diag.messages.clone(),
             args: args.clone(),
             code: diag.code.clone(),
             lvl: diag.level(),
         })));
         for child in &diag.children {
             drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
-                msg: child.message.clone(),
+                msgs: child.messages.clone(),
                 args: args.clone(),
                 code: None,
                 lvl: child.level,
@@ -1838,7 +1838,7 @@ impl SharedEmitterMain {
             match message {
                 Ok(SharedEmitterMessage::Diagnostic(diag)) => {
                     let dcx = sess.dcx();
-                    let mut d = rustc_errors::Diagnostic::new_with_messages(diag.lvl, diag.msg);
+                    let mut d = rustc_errors::Diagnostic::new_with_messages(diag.lvl, diag.msgs);
                     if let Some(code) = diag.code {
                         d.code(code);
                     }
@@ -1846,14 +1846,14 @@ impl SharedEmitterMain {
                     dcx.emit_diagnostic(d);
                 }
                 Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => {
-                    let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string();
-
-                    let mut err = match level {
-                        Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(),
-                        Level::Warning(_) => sess.struct_warn(msg),
-                        Level::Note => sess.struct_note(msg),
+                    let err_level = match level {
+                        Level::Error { lint: false } => rustc_errors::Level::Error { lint: false },
+                        Level::Warning(_) => rustc_errors::Level::Warning(None),
+                        Level::Note => rustc_errors::Level::Note,
                         _ => bug!("Invalid inline asm diagnostic level"),
                     };
+                    let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string();
+                    let mut err = DiagnosticBuilder::<()>::new(sess.dcx(), err_level, msg);
 
                     // If the cookie is 0 then we don't have span information.
                     if cookie != 0 {
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index dda30046bfb..2ecc5ad4fe4 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -15,7 +15,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher};
 use rustc_hir::def_id::DefId;
 use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData};
-use rustc_hir::{CoroutineKind, CoroutineSource, Mutability};
+use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Mutability};
 use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
 use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt};
 use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
@@ -560,15 +560,31 @@ pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &
 
 fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
     match coroutine_kind {
-        Some(CoroutineKind::Gen(CoroutineSource::Block)) => "gen_block",
-        Some(CoroutineKind::Gen(CoroutineSource::Closure)) => "gen_closure",
-        Some(CoroutineKind::Gen(CoroutineSource::Fn)) => "gen_fn",
-        Some(CoroutineKind::Async(CoroutineSource::Block)) => "async_block",
-        Some(CoroutineKind::Async(CoroutineSource::Closure)) => "async_closure",
-        Some(CoroutineKind::Async(CoroutineSource::Fn)) => "async_fn",
-        Some(CoroutineKind::AsyncGen(CoroutineSource::Block)) => "async_gen_block",
-        Some(CoroutineKind::AsyncGen(CoroutineSource::Closure)) => "async_gen_closure",
-        Some(CoroutineKind::AsyncGen(CoroutineSource::Fn)) => "async_gen_fn",
+        Some(CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Block)) => {
+            "gen_block"
+        }
+        Some(CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Closure)) => {
+            "gen_closure"
+        }
+        Some(CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Fn)) => "gen_fn",
+        Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Block)) => {
+            "async_block"
+        }
+        Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Closure)) => {
+            "async_closure"
+        }
+        Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Fn)) => {
+            "async_fn"
+        }
+        Some(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, CoroutineSource::Block)) => {
+            "async_gen_block"
+        }
+        Some(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, CoroutineSource::Closure)) => {
+            "async_gen_closure"
+        }
+        Some(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, CoroutineSource::Fn)) => {
+            "async_gen_fn"
+        }
         Some(CoroutineKind::Coroutine) => "coroutine",
         None => "closure",
     }
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index 8f18cd78d3f..2cafbb9331d 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -6,7 +6,7 @@ use rustc_middle::mir::AssertKind;
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::ty::{layout::LayoutError, ConstInt};
-use rustc_span::{ErrorGuaranteed, Span, Symbol, DUMMY_SP};
+use rustc_span::{Span, Symbol, DUMMY_SP};
 
 use super::{CompileTimeInterpreter, InterpCx};
 use crate::errors::{self, FrameNote, ReportErrorExt};
@@ -133,7 +133,7 @@ pub(super) fn report<'tcx, C, F, E>(
 where
     C: FnOnce() -> (Span, Vec<FrameNote>),
     F: FnOnce(Span, Vec<FrameNote>) -> E,
-    E: IntoDiagnostic<'tcx, ErrorGuaranteed>,
+    E: IntoDiagnostic<'tcx>,
 {
     // Special handling for certain errors
     match error {
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index 949606ed6c9..8a1e5356a15 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -464,8 +464,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
 
             Rvalue::Aggregate(kind, ..) => {
                 if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref()
-                    && let Some(coroutine_kind @ hir::CoroutineKind::Async(..)) =
-                        self.tcx.coroutine_kind(def_id)
+                    && let Some(
+                        coroutine_kind @ hir::CoroutineKind::Desugared(
+                            hir::CoroutineDesugaring::Async,
+                            _,
+                        ),
+                    ) = self.tcx.coroutine_kind(def_id)
                 {
                     self.check_op(ops::Coroutine(coroutine_kind));
                 }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 2de6362b9fe..532cd9c261f 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -2,7 +2,7 @@
 
 use hir::def_id::LocalDefId;
 use hir::{ConstContext, LangItem};
-use rustc_errors::{error_code, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{error_code, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::TyCtxtInferExt;
@@ -48,11 +48,7 @@ pub trait NonConstOp<'tcx>: std::fmt::Debug {
         DiagnosticImportance::Primary
     }
 
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
 }
 
 #[derive(Debug)]
@@ -66,11 +62,7 @@ impl<'tcx> NonConstOp<'tcx> for FloatingPointOp {
         }
     }
 
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         feature_err(
             &ccx.tcx.sess.parse_sess,
             sym::const_fn_floating_point_arithmetic,
@@ -84,11 +76,7 @@ impl<'tcx> NonConstOp<'tcx> for FloatingPointOp {
 #[derive(Debug)]
 pub struct FnCallIndirect;
 impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx.sess.create_err(errors::UnallowedFnPointerCall { span, kind: ccx.const_kind() })
     }
 }
@@ -105,11 +93,7 @@ pub struct FnCallNonConst<'tcx> {
 }
 
 impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        _: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> {
         let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self;
         let ConstCx { tcx, param_env, .. } = *ccx;
 
@@ -331,11 +315,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
 pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
 
 impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         let FnCallUnstable(def_id, feature) = *self;
 
         let mut err = ccx
@@ -359,20 +339,24 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
 pub struct Coroutine(pub hir::CoroutineKind);
 impl<'tcx> NonConstOp<'tcx> for Coroutine {
     fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
-        if let hir::CoroutineKind::Async(hir::CoroutineSource::Block) = self.0 {
+        if let hir::CoroutineKind::Desugared(
+            hir::CoroutineDesugaring::Async,
+            hir::CoroutineSource::Block,
+        ) = self.0
+        {
             Status::Unstable(sym::const_async_blocks)
         } else {
             Status::Forbidden
         }
     }
 
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
-        if let hir::CoroutineKind::Async(hir::CoroutineSource::Block) = self.0 {
+        if let hir::CoroutineKind::Desugared(
+            hir::CoroutineDesugaring::Async,
+            hir::CoroutineSource::Block,
+        ) = self.0
+        {
             ccx.tcx.sess.create_feature_err(
                 errors::UnallowedOpInConstContext { span, msg },
                 sym::const_async_blocks,
@@ -386,11 +370,7 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine {
 #[derive(Debug)]
 pub struct HeapAllocation;
 impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx.sess.create_err(errors::UnallowedHeapAllocations {
             span,
             kind: ccx.const_kind(),
@@ -402,11 +382,7 @@ impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
 #[derive(Debug)]
 pub struct InlineAsm;
 impl<'tcx> NonConstOp<'tcx> for InlineAsm {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx.sess.create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() })
     }
 }
@@ -417,11 +393,7 @@ pub struct LiveDrop<'tcx> {
     pub dropped_ty: Ty<'tcx>,
 }
 impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx.sess.create_err(errors::LiveDrop {
             span,
             dropped_ty: self.dropped_ty,
@@ -444,11 +416,7 @@ impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
         // not additionally emit a feature gate error if activating the feature gate won't work.
         DiagnosticImportance::Secondary
     }
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx
             .sess
             .create_feature_err(errors::InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
@@ -461,11 +429,7 @@ impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
 /// it in the future for static items.
 pub struct CellBorrow;
 impl<'tcx> NonConstOp<'tcx> for CellBorrow {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         // FIXME: Maybe a more elegant solution to this if else case
         if let hir::ConstContext::Static(_) = ccx.const_kind() {
             ccx.tcx.sess.create_err(errors::InteriorMutableDataRefer {
@@ -502,11 +466,7 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
         DiagnosticImportance::Secondary
     }
 
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         match self.0 {
             hir::BorrowKind::Raw => ccx.tcx.sess.create_err(errors::UnallowedMutableRefsRaw {
                 span,
@@ -530,11 +490,7 @@ impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
         Status::Unstable(sym::const_mut_refs)
     }
 
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         let kind = ccx.const_kind();
         match self.0 {
             hir::BorrowKind::Raw => ccx.tcx.sess.create_feature_err(
@@ -561,11 +517,7 @@ impl<'tcx> NonConstOp<'tcx> for MutDeref {
         DiagnosticImportance::Secondary
     }
 
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx.sess.create_feature_err(
             errors::MutDerefErr { span, kind: ccx.const_kind() },
             sym::const_mut_refs,
@@ -577,11 +529,7 @@ impl<'tcx> NonConstOp<'tcx> for MutDeref {
 #[derive(Debug)]
 pub struct PanicNonStr;
 impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx.sess.create_err(errors::PanicNonStrErr { span })
     }
 }
@@ -592,11 +540,7 @@ impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
 #[derive(Debug)]
 pub struct RawPtrComparison;
 impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         // FIXME(const_trait_impl): revert to span_bug?
         ccx.tcx.sess.create_err(errors::RawPtrComparisonErr { span })
     }
@@ -609,11 +553,7 @@ impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
         Status::Unstable(sym::const_mut_refs)
     }
 
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         feature_err(
             &ccx.tcx.sess.parse_sess,
             sym::const_mut_refs,
@@ -629,11 +569,7 @@ impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
 #[derive(Debug)]
 pub struct RawPtrToIntCast;
 impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx.sess.create_err(errors::RawPtrToIntErr { span })
     }
 }
@@ -650,11 +586,7 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
         }
     }
 
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx.sess.create_err(errors::StaticAccessErr {
             span,
             kind: ccx.const_kind(),
@@ -667,11 +599,7 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
 #[derive(Debug)]
 pub struct ThreadLocalAccess;
 impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
-    fn build_error(
-        &self,
-        ccx: &ConstCx<'_, 'tcx>,
-        span: Span,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
         ccx.tcx.sess.create_err(errors::NonConstOpErr { span })
     }
 }
@@ -696,11 +624,7 @@ pub mod ty {
             }
         }
 
-        fn build_error(
-            &self,
-            ccx: &ConstCx<'_, 'tcx>,
-            span: Span,
-        ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+        fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
             feature_err(
                 &ccx.tcx.sess.parse_sess,
                 sym::const_mut_refs,
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index cca5b90abb9..2f538cebaa6 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -8,9 +8,6 @@ use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt, Variance};
-use rustc_mir_dataflow::impls::MaybeStorageLive;
-use rustc_mir_dataflow::storage::always_storage_live_locals;
-use rustc_mir_dataflow::{Analysis, ResultsCursor};
 use rustc_target::abi::{Size, FIRST_VARIANT};
 use rustc_target::spec::abi::Abi;
 
@@ -51,12 +48,6 @@ impl<'tcx> MirPass<'tcx> for Validator {
             Reveal::All => tcx.param_env_reveal_all_normalized(def_id),
         };
 
-        let always_live_locals = always_storage_live_locals(body);
-        let storage_liveness = MaybeStorageLive::new(std::borrow::Cow::Owned(always_live_locals))
-            .into_engine(tcx, body)
-            .iterate_to_fixpoint()
-            .into_results_cursor(body);
-
         let can_unwind = if mir_phase <= MirPhase::Runtime(RuntimePhase::Initial) {
             // In this case `AbortUnwindingCalls` haven't yet been executed.
             true
@@ -83,7 +74,6 @@ impl<'tcx> MirPass<'tcx> for Validator {
             mir_phase,
             unwind_edge_count: 0,
             reachable_blocks: traversal::reachable_as_bitset(body),
-            storage_liveness,
             place_cache: FxHashSet::default(),
             value_cache: FxHashSet::default(),
             can_unwind,
@@ -116,7 +106,6 @@ struct CfgChecker<'a, 'tcx> {
     mir_phase: MirPhase,
     unwind_edge_count: usize,
     reachable_blocks: BitSet<BasicBlock>,
-    storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
     place_cache: FxHashSet<PlaceRef<'tcx>>,
     value_cache: FxHashSet<u128>,
     // If `false`, then the MIR must not contain `UnwindAction::Continue` or
@@ -294,28 +283,13 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
-    fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
+    fn visit_local(&mut self, local: Local, _context: PlaceContext, location: Location) {
         if self.body.local_decls.get(local).is_none() {
             self.fail(
                 location,
                 format!("local {local:?} has no corresponding declaration in `body.local_decls`"),
             );
         }
-
-        if self.reachable_blocks.contains(location.block) && context.is_use() {
-            // We check that the local is live whenever it is used. Technically, violating this
-            // restriction is only UB and not actually indicative of not well-formed MIR. This means
-            // that an optimization which turns MIR that already has UB into MIR that fails this
-            // check is not necessarily wrong. However, we have no such optimizations at the moment,
-            // and so we include this check anyway to help us catch bugs. If you happen to write an
-            // optimization that might cause this to incorrectly fire, feel free to remove this
-            // check.
-            self.storage_liveness.seek_after_primary_effect(location);
-            let locals_with_storage = self.storage_liveness.get();
-            if !locals_with_storage.contains(local) {
-                self.fail(location, format!("use of local {local:?}, which has no storage here"));
-            }
-        }
     }
 
     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
@@ -367,26 +341,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
                     self.fail(location, format!("explicit `{kind:?}` is forbidden"));
                 }
             }
-            StatementKind::StorageLive(local) => {
-                // We check that the local is not live when entering a `StorageLive` for it.
-                // Technically, violating this restriction is only UB and not actually indicative
-                // of not well-formed MIR. This means that an optimization which turns MIR that
-                // already has UB into MIR that fails this check is not necessarily wrong. However,
-                // we have no such optimizations at the moment, and so we include this check anyway
-                // to help us catch bugs. If you happen to write an optimization that might cause
-                // this to incorrectly fire, feel free to remove this check.
-                if self.reachable_blocks.contains(location.block) {
-                    self.storage_liveness.seek_before_primary_effect(location);
-                    let locals_with_storage = self.storage_liveness.get();
-                    if locals_with_storage.contains(*local) {
-                        self.fail(
-                            location,
-                            format!("StorageLive({local:?}) which already has storage here"),
-                        );
-                    }
-                }
-            }
-            StatementKind::StorageDead(_)
+            StatementKind::StorageLive(_)
+            | StatementKind::StorageDead(_)
             | StatementKind::Intrinsic(_)
             | StatementKind::Coverage(_)
             | StatementKind::ConstEvalCounter
diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs
index dc546da7342..b7407f5a508 100644
--- a/compiler/rustc_driver_impl/src/args.rs
+++ b/compiler/rustc_driver_impl/src/args.rs
@@ -28,7 +28,7 @@ pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<Stri
     for arg in at_args {
         match arg_expand(arg.clone()) {
             Ok(arg) => args.extend(arg),
-            Err(err) => early_dcx.early_error(format!("Failed to load argument file: {err}")),
+            Err(err) => early_dcx.early_fatal(format!("Failed to load argument file: {err}")),
         }
     }
     args
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index d67fea7e9a4..c277304fb22 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -345,7 +345,7 @@ fn run_compiler(
         Ok(None) => match matches.free.len() {
             0 => false, // no input: we will exit early
             1 => panic!("make_input should have provided valid inputs"),
-            _ => default_early_dcx.early_error(format!(
+            _ => default_early_dcx.early_fatal(format!(
                 "multiple input filenames provided (first two filenames are `{}` and `{}`)",
                 matches.free[0], matches.free[1],
             )),
@@ -376,7 +376,7 @@ fn run_compiler(
         }
 
         if !has_input {
-            early_dcx.early_error("no input filename given"); // this is fatal
+            early_dcx.early_fatal("no input filename given"); // this is fatal
         }
 
         if !sess.opts.unstable_opts.ls.is_empty() {
@@ -505,9 +505,8 @@ fn make_input(
             if io::stdin().read_to_string(&mut src).is_err() {
                 // Immediately stop compilation if there was an issue reading
                 // the input (for example if the input stream is not UTF-8).
-                let reported = early_dcx.early_error_no_abort(
-                    "couldn't read from stdin, as it did not contain valid UTF-8",
-                );
+                let reported = early_dcx
+                    .early_err("couldn't read from stdin, as it did not contain valid UTF-8");
                 return Err(reported);
             }
             if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
@@ -567,7 +566,7 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col
             }
         }
         Err(InvalidErrorCode) => {
-            early_dcx.early_error(format!("{code} is not a valid error code"));
+            early_dcx.early_fatal(format!("{code} is not a valid error code"));
         }
     }
 }
@@ -685,7 +684,7 @@ fn list_metadata(early_dcx: &EarlyDiagCtxt, sess: &Session, metadata_loader: &dy
             safe_println!("{}", String::from_utf8(v).unwrap());
         }
         Input::Str { .. } => {
-            early_dcx.early_error("cannot list metadata for stdin");
+            early_dcx.early_fatal("cannot list metadata for stdin");
         }
     }
 }
@@ -839,7 +838,7 @@ fn print_crate_info(
                     println_info!("deployment_target={}", format!("{major}.{minor}"))
                 } else {
                     early_dcx
-                        .early_error("only Apple targets currently support deployment version info")
+                        .early_fatal("only Apple targets currently support deployment version info")
                 }
             }
         }
@@ -1182,7 +1181,7 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
                 .map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")),
             _ => None,
         };
-        early_dcx.early_error(msg.unwrap_or_else(|| e.to_string()));
+        early_dcx.early_fatal(msg.unwrap_or_else(|| e.to_string()));
     });
 
     // For all options we just parsed, we check a few aspects:
@@ -1333,7 +1332,7 @@ pub fn install_ice_hook(
                 {
                     // the error code is already going to be reported when the panic unwinds up the stack
                     let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
-                    let _ = early_dcx.early_error_no_abort(msg.clone());
+                    let _ = early_dcx.early_err(msg.clone());
                     return;
                 }
             };
@@ -1481,7 +1480,7 @@ pub fn init_rustc_env_logger(early_dcx: &EarlyDiagCtxt) {
 /// the values directly rather than having to set an environment variable.
 pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) {
     if let Err(error) = rustc_log::init_logger(cfg) {
-        early_dcx.early_error(error.to_string());
+        early_dcx.early_fatal(error.to_string());
     }
 }
 
@@ -1500,7 +1499,7 @@ pub fn main() -> ! {
             .enumerate()
             .map(|(i, arg)| {
                 arg.into_string().unwrap_or_else(|arg| {
-                    early_dcx.early_error(format!("argument {i} is not valid Unicode: {arg:?}"))
+                    early_dcx.early_fatal(format!("argument {i} is not valid Unicode: {arg:?}"))
                 })
             })
             .collect::<Vec<_>>();
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index da266bf9c63..48e48f59a99 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -60,7 +60,7 @@ impl Emitter for AnnotateSnippetEmitterWriter {
 
         self.emit_messages_default(
             &diag.level,
-            &diag.message,
+            &diag.messages,
             &fluent_args,
             &diag.code,
             &primary_span,
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index be506806065..c226b2d41bd 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -103,7 +103,7 @@ pub struct Diagnostic {
     // outside of what methods in this crate themselves allow.
     pub(crate) level: Level,
 
-    pub message: Vec<(DiagnosticMessage, Style)>,
+    pub messages: Vec<(DiagnosticMessage, Style)>,
     pub code: Option<DiagnosticId>,
     pub span: MultiSpan,
     pub children: Vec<SubDiagnostic>,
@@ -161,9 +161,8 @@ pub enum DiagnosticId {
 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
 pub struct SubDiagnostic {
     pub level: Level,
-    pub message: Vec<(DiagnosticMessage, Style)>,
+    pub messages: Vec<(DiagnosticMessage, Style)>,
     pub span: MultiSpan,
-    pub render_span: Option<MultiSpan>,
 }
 
 #[derive(Debug, PartialEq, Eq)]
@@ -216,14 +215,14 @@ impl StringPart {
 impl Diagnostic {
     #[track_caller]
     pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self {
-        Diagnostic::new_with_code(level, None, message)
+        Diagnostic::new_with_messages(level, vec![(message.into(), Style::NoStyle)])
     }
 
     #[track_caller]
     pub fn new_with_messages(level: Level, messages: Vec<(DiagnosticMessage, Style)>) -> Self {
         Diagnostic {
             level,
-            message: messages,
+            messages,
             code: None,
             span: MultiSpan::new(),
             children: vec![],
@@ -235,26 +234,6 @@ impl Diagnostic {
         }
     }
 
-    #[track_caller]
-    pub(crate) fn new_with_code<M: Into<DiagnosticMessage>>(
-        level: Level,
-        code: Option<DiagnosticId>,
-        message: M,
-    ) -> Self {
-        Diagnostic {
-            level,
-            message: vec![(message.into(), Style::NoStyle)],
-            code,
-            span: MultiSpan::new(),
-            children: vec![],
-            suggestions: Ok(vec![]),
-            args: Default::default(),
-            sort_span: DUMMY_SP,
-            is_lint: false,
-            emitted_at: DiagnosticLocation::caller(),
-        }
-    }
-
     #[inline(always)]
     pub fn level(&self) -> Level {
         self.level
@@ -445,7 +424,7 @@ impl Diagnostic {
     /// Add a note attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::Note, msg, MultiSpan::new(), None);
+        self.sub(Level::Note, msg, MultiSpan::new());
         self
     }
 
@@ -453,14 +432,14 @@ impl Diagnostic {
         &mut self,
         msg: Vec<(M, Style)>,
     ) -> &mut Self {
-        self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
+        self.sub_with_highlights(Level::Note, msg, MultiSpan::new());
         self
     }
 
     /// Prints the span with a note above it.
     /// This is like [`Diagnostic::note()`], but it gets its own span.
     pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::OnceNote, msg, MultiSpan::new(), None);
+        self.sub(Level::OnceNote, msg, MultiSpan::new());
         self
     }
 
@@ -472,7 +451,7 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::Note, msg, sp.into(), None);
+        self.sub(Level::Note, msg, sp.into());
         self
     }
 
@@ -483,14 +462,14 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::OnceNote, msg, sp.into(), None);
+        self.sub(Level::OnceNote, msg, sp.into());
         self
     }
 
     /// Add a warning attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::Warning(None), msg, MultiSpan::new(), None);
+        self.sub(Level::Warning(None), msg, MultiSpan::new());
         self
     }
 
@@ -502,27 +481,27 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::Warning(None), msg, sp.into(), None);
+        self.sub(Level::Warning(None), msg, sp.into());
         self
     }
 
     /// Add a help message attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::Help, msg, MultiSpan::new(), None);
+        self.sub(Level::Help, msg, MultiSpan::new());
         self
     }
 
     /// Prints the span with a help above it.
     /// This is like [`Diagnostic::help()`], but it gets its own span.
     pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::OnceHelp, msg, MultiSpan::new(), None);
+        self.sub(Level::OnceHelp, msg, MultiSpan::new());
         self
     }
 
     /// Add a help message attached to this diagnostic with a customizable highlighted message.
     pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
-        self.sub_with_highlights(Level::Help, msg, MultiSpan::new(), None);
+        self.sub_with_highlights(Level::Help, msg, MultiSpan::new());
         self
     }
 
@@ -534,7 +513,7 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::Help, msg, sp.into(), None);
+        self.sub(Level::Help, msg, sp.into());
         self
     }
 
@@ -925,7 +904,7 @@ impl Diagnostic {
     }
 
     pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
-        self.message[0] = (msg.into(), Style::NoStyle);
+        self.messages[0] = (msg.into(), Style::NoStyle);
         self
     }
 
@@ -952,8 +931,8 @@ impl Diagnostic {
         self.args = args;
     }
 
-    pub fn styled_message(&self) -> &[(DiagnosticMessage, Style)] {
-        &self.message
+    pub fn messages(&self) -> &[(DiagnosticMessage, Style)] {
+        &self.messages
     }
 
     /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
@@ -964,7 +943,7 @@ impl Diagnostic {
         attr: impl Into<SubdiagnosticMessage>,
     ) -> DiagnosticMessage {
         let msg =
-            self.message.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
+            self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
         msg.with_subdiagnostic_message(attr.into())
     }
 
@@ -972,21 +951,14 @@ impl Diagnostic {
     /// public methods above.
     ///
     /// Used by `proc_macro_server` for implementing `server::Diagnostic`.
-    pub fn sub(
-        &mut self,
-        level: Level,
-        message: impl Into<SubdiagnosticMessage>,
-        span: MultiSpan,
-        render_span: Option<MultiSpan>,
-    ) {
+    pub fn sub(&mut self, level: Level, message: impl Into<SubdiagnosticMessage>, span: MultiSpan) {
         let sub = SubDiagnostic {
             level,
-            message: vec![(
+            messages: vec![(
                 self.subdiagnostic_message_to_diagnostic_message(message),
                 Style::NoStyle,
             )],
             span,
-            render_span,
         };
         self.children.push(sub);
     }
@@ -996,15 +968,14 @@ impl Diagnostic {
     fn sub_with_highlights<M: Into<SubdiagnosticMessage>>(
         &mut self,
         level: Level,
-        message: Vec<(M, Style)>,
+        messages: Vec<(M, Style)>,
         span: MultiSpan,
-        render_span: Option<MultiSpan>,
     ) {
-        let message = message
+        let messages = messages
             .into_iter()
             .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1))
             .collect();
-        let sub = SubDiagnostic { level, message, span, render_span };
+        let sub = SubDiagnostic { level, messages, span };
         self.children.push(sub);
     }
 
@@ -1022,7 +993,7 @@ impl Diagnostic {
     ) {
         (
             &self.level,
-            &self.message,
+            &self.messages,
             self.args().collect(),
             &self.code,
             &self.span,
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 828560ec452..4703e71523d 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -15,7 +15,7 @@ use std::ops::{Deref, DerefMut};
 use std::panic;
 use std::thread::panicking;
 
-/// Trait implemented by error types. This should not be implemented manually. Instead, use
+/// Trait implemented by error types. This is rarely implemented manually. Instead, use
 /// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
 #[rustc_diagnostic_item = "IntoDiagnostic"]
 pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
@@ -43,24 +43,7 @@ where
 /// extending `DiagCtxtFlags`.
 #[must_use]
 #[derive(Clone)]
-pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> {
-    inner: DiagnosticBuilderInner<'a>,
-    _marker: PhantomData<G>,
-}
-
-/// This type exists only for `DiagnosticBuilder::forget_guarantee`, because it:
-/// 1. lacks the `G` parameter and therefore `DiagnosticBuilder<G1>` can be
-///    converted into `DiagnosticBuilder<G2>` while reusing the `inner` field
-/// 2. can implement the `Drop` "bomb" instead of `DiagnosticBuilder`, as it
-///    contains all of the data (`state` + `diagnostic`) of `DiagnosticBuilder`
-///
-/// The `diagnostic` field is not `Copy` and can't be moved out of whichever
-/// type implements the `Drop` "bomb", but because of the above two facts, that
-/// never needs to happen - instead, the whole `inner: DiagnosticBuilderInner`
-/// can be moved out of a `DiagnosticBuilder` and into another.
-#[must_use]
-#[derive(Clone)]
-struct DiagnosticBuilderInner<'a> {
+pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> {
     state: DiagnosticBuilderState<'a>,
 
     /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a
@@ -68,6 +51,8 @@ struct DiagnosticBuilderInner<'a> {
     /// In theory, return value optimization (RVO) should avoid unnecessary
     /// copying. In practice, it does not (at the time of writing).
     diagnostic: Box<Diagnostic>,
+
+    _marker: PhantomData<G>,
 }
 
 #[derive(Clone)]
@@ -83,7 +68,7 @@ enum DiagnosticBuilderState<'a> {
     /// assumed that `.emit()` was previously called, to end up in this state.
     ///
     /// While this is also used by `.cancel()`, this state is only observed by
-    /// the `Drop` `impl` of `DiagnosticBuilderInner`, as `.cancel()` takes
+    /// the `Drop` `impl` of `DiagnosticBuilder`, because `.cancel()` takes
     /// `self` by-value specifically to prevent any attempts to `.emit()`.
     ///
     // FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`,
@@ -115,12 +100,11 @@ pub trait EmissionGuarantee: Sized {
 impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// Most `emit_producing_guarantee` functions use this as a starting point.
     fn emit_producing_nothing(&mut self) {
-        match self.inner.state {
+        match self.state {
             // First `.emit()` call, the `&DiagCtxt` is still available.
             DiagnosticBuilderState::Emittable(dcx) => {
-                self.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
-
-                dcx.emit_diagnostic_without_consuming(&mut self.inner.diagnostic);
+                self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+                dcx.emit_diagnostic_without_consuming(&mut self.diagnostic);
             }
             // `.emit()` was previously called, disallowed from repeating it.
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -128,34 +112,24 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     }
 }
 
-impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
-    /// Discard the guarantee `.emit()` would return, in favor of having the
-    /// type `DiagnosticBuilder<'a, ()>`. This may be necessary whenever there
-    /// is a common codepath handling both errors and warnings.
-    pub fn forget_guarantee(self) -> DiagnosticBuilder<'a, ()> {
-        DiagnosticBuilder { inner: self.inner, _marker: PhantomData }
-    }
-}
-
 // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`.
 impl EmissionGuarantee for ErrorGuaranteed {
     fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
         // Contrast this with `emit_producing_nothing`.
-        match db.inner.state {
+        match db.state {
             // First `.emit()` call, the `&DiagCtxt` is still available.
             DiagnosticBuilderState::Emittable(dcx) => {
-                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
-
-                let guar = dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
+                db.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+                let guar = dcx.emit_diagnostic_without_consuming(&mut db.diagnostic);
 
                 // Only allow a guarantee if the `level` wasn't switched to a
                 // non-error - the field isn't `pub`, but the whole `Diagnostic`
                 // can be overwritten with a new one, thanks to `DerefMut`.
                 assert!(
-                    db.inner.diagnostic.is_error(),
+                    db.diagnostic.is_error(),
                     "emitted non-error ({:?}) diagnostic \
                      from `DiagnosticBuilder<ErrorGuaranteed>`",
-                    db.inner.diagnostic.level,
+                    db.diagnostic.level,
                 );
                 guar.unwrap()
             }
@@ -167,10 +141,10 @@ impl EmissionGuarantee for ErrorGuaranteed {
                 // non-error - the field isn't `pub`, but the whole `Diagnostic`
                 // can be overwritten with a new one, thanks to `DerefMut`.
                 assert!(
-                    db.inner.diagnostic.is_error(),
+                    db.diagnostic.is_error(),
                     "`DiagnosticBuilder<ErrorGuaranteed>`'s diagnostic \
                      became non-error ({:?}), after original `.emit()`",
-                    db.inner.diagnostic.level,
+                    db.diagnostic.level,
                 );
                 #[allow(deprecated)]
                 ErrorGuaranteed::unchecked_claim_error_was_emitted()
@@ -238,7 +212,7 @@ macro_rules! forward {
         $(#[$attrs])*
         #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
         pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
-            self.inner.diagnostic.$n($($name),*);
+            self.diagnostic.$n($($name),*);
             self
         }
     };
@@ -248,13 +222,13 @@ impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
     type Target = Diagnostic;
 
     fn deref(&self) -> &Diagnostic {
-        &self.inner.diagnostic
+        &self.diagnostic
     }
 }
 
 impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
     fn deref_mut(&mut self) -> &mut Diagnostic {
-        &mut self.inner.diagnostic
+        &mut self.diagnostic
     }
 }
 
@@ -271,10 +245,8 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diagnostic: Diagnostic) -> Self {
         debug!("Created new diagnostic");
         Self {
-            inner: DiagnosticBuilderInner {
-                state: DiagnosticBuilderState::Emittable(dcx),
-                diagnostic: Box::new(diagnostic),
-            },
+            state: DiagnosticBuilderState::Emittable(dcx),
+            diagnostic: Box::new(diagnostic),
             _marker: PhantomData,
         }
     }
@@ -306,7 +278,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// which may be expected to *guarantee* the emission of an error, either
     /// at the time of the call, or through a prior `.emit()` call.
     pub fn cancel(mut self) {
-        self.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+        self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
         drop(self);
     }
 
@@ -324,7 +296,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// Converts the builder to a `Diagnostic` for later emission,
     /// unless dcx has disabled such buffering, or `.emit()` was called.
     pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> {
-        let dcx = match self.inner.state {
+        let dcx = match self.state {
             // No `.emit()` calls, the `&DiagCtxt` is still available.
             DiagnosticBuilderState::Emittable(dcx) => dcx,
             // `.emit()` was previously called, nothing we can do.
@@ -342,7 +314,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
 
         // Take the `Diagnostic` by replacing it with a dummy.
         let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::from(""));
-        let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy);
+        let diagnostic = std::mem::replace(&mut *self.diagnostic, dummy);
 
         // Disable the ICE on `Drop`.
         self.cancel();
@@ -356,7 +328,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
 
     /// Retrieves the [`DiagCtxt`] if available
     pub fn dcx(&self) -> Option<&DiagCtxt> {
-        match self.inner.state {
+        match self.state {
             DiagnosticBuilderState::Emittable(dcx) => Some(dcx),
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None,
         }
@@ -544,13 +516,13 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
 
 impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.inner.diagnostic.fmt(f)
+        self.diagnostic.fmt(f)
     }
 }
 
 /// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled
 /// or we emit a bug.
-impl Drop for DiagnosticBuilderInner<'_> {
+impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> {
     fn drop(&mut self) {
         match self.state {
             // No `.emit()` or `.cancel()` calls.
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index ccc951543d8..29cb304e8b5 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -312,25 +312,13 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetDataLayoutErrors<'_>
 pub struct SingleLabelManySpans {
     pub spans: Vec<Span>,
     pub label: &'static str,
-    pub kind: LabelKind,
 }
 impl AddToDiagnostic for SingleLabelManySpans {
     fn add_to_diagnostic_with<F>(self, diag: &mut crate::Diagnostic, _: F) {
-        match self.kind {
-            LabelKind::Note => diag.span_note(self.spans, self.label),
-            LabelKind::Label => diag.span_labels(self.spans, self.label),
-            LabelKind::Help => diag.span_help(self.spans, self.label),
-        };
+        diag.span_labels(self.spans, self.label);
     }
 }
 
-/// The kind of label to attach when using [`SingleLabelManySpans`]
-pub enum LabelKind {
-    Note,
-    Label,
-    Help,
-}
-
 #[derive(Subdiagnostic)]
 #[label(errors_expected_lifetime_parameter)]
 pub struct ExpectedLifetimeParameter {
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 3fb993c3651..546159c9d13 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -350,9 +350,8 @@ pub trait Emitter: Translate {
 
                 children.push(SubDiagnostic {
                     level: Level::Note,
-                    message: vec![(DiagnosticMessage::from(msg), Style::NoStyle)],
+                    messages: vec![(DiagnosticMessage::from(msg), Style::NoStyle)],
                     span: MultiSpan::new(),
-                    render_span: None,
                 });
             }
         }
@@ -533,7 +532,7 @@ impl Emitter for EmitterWriter {
 
         self.emit_messages_default(
             &diag.level,
-            &diag.message,
+            &diag.messages,
             &fluent_args,
             &diag.code,
             &primary_span,
@@ -1228,10 +1227,10 @@ impl EmitterWriter {
 
     /// Adds a left margin to every line but the first, given a padding length and the label being
     /// displayed, keeping the provided highlighting.
-    fn msg_to_buffer(
+    fn msgs_to_buffer(
         &self,
         buffer: &mut StyledBuffer,
-        msg: &[(DiagnosticMessage, Style)],
+        msgs: &[(DiagnosticMessage, Style)],
         args: &FluentArgs<'_>,
         padding: usize,
         label: &str,
@@ -1267,7 +1266,7 @@ impl EmitterWriter {
 
         // Provided the following diagnostic message:
         //
-        //     let msg = vec![
+        //     let msgs = vec![
         //       ("
         //       ("highlighted multiline\nstring to\nsee how it ", Style::NoStyle),
         //       ("looks", Style::Highlight),
@@ -1284,7 +1283,7 @@ impl EmitterWriter {
         //                see how it *looks* with
         //                very *weird* formats
         //                see?
-        for (text, style) in msg.iter() {
+        for (text, style) in msgs.iter() {
             let text = self.translate_message(text, args).map_err(Report::new).unwrap();
             let text = &normalize_whitespace(&text);
             let lines = text.split('\n').collect::<Vec<_>>();
@@ -1303,10 +1302,10 @@ impl EmitterWriter {
     }
 
     #[instrument(level = "trace", skip(self, args), ret)]
-    fn emit_message_default(
+    fn emit_messages_default_inner(
         &mut self,
         msp: &MultiSpan,
-        msg: &[(DiagnosticMessage, Style)],
+        msgs: &[(DiagnosticMessage, Style)],
         args: &FluentArgs<'_>,
         code: &Option<DiagnosticId>,
         level: &Level,
@@ -1327,7 +1326,7 @@ impl EmitterWriter {
                 buffer.append(0, level.to_str(), Style::MainHeaderMsg);
                 buffer.append(0, ": ", Style::NoStyle);
             }
-            self.msg_to_buffer(&mut buffer, msg, args, max_line_num_len, "note", None);
+            self.msgs_to_buffer(&mut buffer, msgs, args, max_line_num_len, "note", None);
         } else {
             let mut label_width = 0;
             // The failure note level itself does not provide any useful diagnostic information
@@ -1360,7 +1359,7 @@ impl EmitterWriter {
                 buffer.append(0, ": ", header_style);
                 label_width += 2;
             }
-            for (text, _) in msg.iter() {
+            for (text, _) in msgs.iter() {
                 let text = self.translate_message(text, args).map_err(Report::new).unwrap();
                 // Account for newlines to align output to its label.
                 for (line, text) in normalize_whitespace(&text).lines().enumerate() {
@@ -1747,7 +1746,7 @@ impl EmitterWriter {
         buffer.append(0, level.to_str(), Style::Level(*level));
         buffer.append(0, ": ", Style::HeaderMsg);
 
-        self.msg_to_buffer(
+        self.msgs_to_buffer(
             &mut buffer,
             &[(suggestion.msg.to_owned(), Style::NoStyle)],
             args,
@@ -2074,7 +2073,7 @@ impl EmitterWriter {
     fn emit_messages_default(
         &mut self,
         level: &Level,
-        message: &[(DiagnosticMessage, Style)],
+        messages: &[(DiagnosticMessage, Style)],
         args: &FluentArgs<'_>,
         code: &Option<DiagnosticId>,
         span: &MultiSpan,
@@ -2089,9 +2088,9 @@ impl EmitterWriter {
             num_decimal_digits(n)
         };
 
-        match self.emit_message_default(
+        match self.emit_messages_default_inner(
             span,
-            message,
+            messages,
             args,
             code,
             level,
@@ -2118,10 +2117,10 @@ impl EmitterWriter {
                 }
                 if !self.short_message {
                     for child in children {
-                        let span = child.render_span.as_ref().unwrap_or(&child.span);
-                        if let Err(err) = self.emit_message_default(
+                        let span = &child.span;
+                        if let Err(err) = self.emit_messages_default_inner(
                             span,
-                            &child.message,
+                            &child.messages,
                             args,
                             &None,
                             &child.level,
@@ -2138,7 +2137,7 @@ impl EmitterWriter {
                                 // do not display this suggestion, it is meant only for tools
                             }
                             SuggestionStyle::HideCodeAlways => {
-                                if let Err(e) = self.emit_message_default(
+                                if let Err(e) = self.emit_messages_default_inner(
                                     &MultiSpan::new(),
                                     &[(sugg.msg.to_owned(), Style::HeaderMsg)],
                                     args,
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index aa3749334d9..52fcb50e9fb 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -398,7 +398,7 @@ impl Diagnostic {
         let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
         let output = String::from_utf8(output).unwrap();
 
-        let translated_message = je.translate_messages(&diag.message, &args);
+        let translated_message = je.translate_messages(&diag.messages, &args);
         Diagnostic {
             message: translated_message.to_string(),
             code: DiagnosticCode::map_opt_string(diag.code.clone(), je),
@@ -419,16 +419,12 @@ impl Diagnostic {
         args: &FluentArgs<'_>,
         je: &JsonEmitter,
     ) -> Diagnostic {
-        let translated_message = je.translate_messages(&diag.message, args);
+        let translated_message = je.translate_messages(&diag.messages, args);
         Diagnostic {
             message: translated_message.to_string(),
             code: None,
             level: diag.level.to_str(),
-            spans: diag
-                .render_span
-                .as_ref()
-                .map(|sp| DiagnosticSpan::from_multispan(sp, args, je))
-                .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, args, je)),
+            spans: DiagnosticSpan::from_multispan(&diag.span, args, je),
             children: vec![],
             rendered: None,
         }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 518a2ddb71d..7a1faac04d3 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -27,26 +27,42 @@ extern crate tracing;
 
 extern crate self as rustc_errors;
 
+pub use diagnostic::{
+    AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
+    DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
+};
+pub use diagnostic_builder::{
+    BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort, IntoDiagnostic,
+};
+pub use diagnostic_impls::{
+    DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
+    IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, SingleLabelManySpans,
+};
 pub use emitter::ColorConfig;
+pub use rustc_error_messages::{
+    fallback_fluent_bundle, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle,
+    LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage,
+};
+pub use rustc_lint_defs::{pluralize, Applicability};
+pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
+pub use rustc_span::ErrorGuaranteed;
+pub use snippet::Style;
 
-use rustc_lint_defs::LintExpectationId;
-use Level::*;
+// Used by external projects such as `rust-gpu`.
+// See https://github.com/rust-lang/rust/pull/115393.
+pub use termcolor::{Color, ColorSpec, WriteColor};
 
+use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline};
 use emitter::{is_case_difference, DynEmitter, Emitter, EmitterWriter};
 use registry::Registry;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::{Hash128, StableHasher};
 use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_data_structures::AtomicRef;
-pub use rustc_error_messages::{
-    fallback_fluent_bundle, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle,
-    LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage,
-};
-pub use rustc_lint_defs::{pluralize, Applicability};
+use rustc_lint_defs::LintExpectationId;
 use rustc_span::source_map::SourceMap;
-pub use rustc_span::ErrorGuaranteed;
 use rustc_span::{Loc, Span, DUMMY_SP};
-
+use std::backtrace::{Backtrace, BacktraceStatus};
 use std::borrow::Cow;
 use std::error::Report;
 use std::fmt;
@@ -56,9 +72,7 @@ use std::num::NonZeroUsize;
 use std::panic;
 use std::path::{Path, PathBuf};
 
-// Used by external projects such as `rust-gpu`.
-// See https://github.com/rust-lang/rust/pull/115393.
-pub use termcolor::{Color, ColorSpec, WriteColor};
+use Level::*;
 
 pub mod annotate_snippet_emitter_writer;
 mod diagnostic;
@@ -76,10 +90,7 @@ mod styled_buffer;
 mod tests;
 pub mod translation;
 
-pub use diagnostic_builder::IntoDiagnostic;
-pub use snippet::Style;
-
-pub type PErr<'a> = DiagnosticBuilder<'a, ErrorGuaranteed>;
+pub type PErr<'a> = DiagnosticBuilder<'a>;
 pub type PResult<'a, T> = Result<T, PErr<'a>>;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
@@ -163,7 +174,7 @@ pub struct SubstitutionPart {
 /// Used to translate between `Span`s and byte positions within a single output line in highlighted
 /// code of structured suggestions.
 #[derive(Debug, Clone, Copy)]
-pub struct SubstitutionHighlight {
+pub(crate) struct SubstitutionHighlight {
     start: usize,
     end: usize,
 }
@@ -190,7 +201,7 @@ impl SubstitutionPart {
 impl CodeSuggestion {
     /// Returns the assembled code suggestions, whether they should be shown with an underline
     /// and whether the substitution only differs in capitalization.
-    pub fn splice_lines(
+    pub(crate) fn splice_lines(
         &self,
         sm: &SourceMap,
     ) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, bool)> {
@@ -387,8 +398,6 @@ impl CodeSuggestion {
     }
 }
 
-pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
-
 /// Signifies that the compiler died with an explicit call to `.bug`
 /// or `.span_bug` rather than a failed assertion, etc.
 pub struct ExplicitBug;
@@ -397,20 +406,7 @@ pub struct ExplicitBug;
 /// rather than a failed assertion, etc.
 pub struct DelayedBugPanic;
 
-use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline};
-pub use diagnostic::{
-    AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
-    DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
-};
-pub use diagnostic_builder::{BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort};
-pub use diagnostic_impls::{
-    DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
-    IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, LabelKind,
-    SingleLabelManySpans,
-};
-use std::backtrace::{Backtrace, BacktraceStatus};
-
-/// A handler deals with errors and other compiler output.
+/// A `DiagCtxt` deals with errors and other compiler output.
 /// Certain errors (fatal, bug, unimpl) may cause immediate exit,
 /// others log errors for later reporting.
 pub struct DiagCtxt {
@@ -447,7 +443,7 @@ struct DiagCtxtInner {
     emitted_diagnostic_codes: FxIndexSet<DiagnosticId>,
 
     /// This set contains a hash of every diagnostic that has been emitted by
-    /// this handler. These hashes is used to avoid emitting the same error
+    /// this `DiagCtxt`. These hashes is used to avoid emitting the same error
     /// twice.
     emitted_diagnostics: FxHashSet<Hash128>,
 
@@ -677,7 +673,7 @@ impl DiagCtxt {
         let key = (span.with_parent(None), key);
 
         if diag.is_error() {
-            if matches!(diag.level, Level::Error { lint: true }) {
+            if matches!(diag.level, Error { lint: true }) {
                 inner.lint_err_count += 1;
             } else {
                 inner.err_count += 1;
@@ -701,7 +697,7 @@ impl DiagCtxt {
         let key = (span.with_parent(None), key);
         let diag = inner.stashed_diagnostics.remove(&key)?;
         if diag.is_error() {
-            if matches!(diag.level, Level::Error { lint: true }) {
+            if matches!(diag.level, Error { lint: true }) {
                 inner.lint_err_count -= 1;
             } else {
                 inner.err_count -= 1;
@@ -741,37 +737,6 @@ impl DiagCtxt {
     }
 
     /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
-    /// The `id` is used for lint emissions which should also fulfill a lint expectation.
-    ///
-    /// Attempting to `.emit()` the builder will only emit if either:
-    /// * `can_emit_warnings` is `true`
-    /// * `is_force_warn` was set in `DiagnosticId::Lint`
-    #[track_caller]
-    pub fn struct_span_warn_with_expectation(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-        id: LintExpectationId,
-    ) -> DiagnosticBuilder<'_, ()> {
-        let mut result = self.struct_warn_with_expectation(msg, id);
-        result.set_span(span);
-        result
-    }
-
-    /// Construct a builder at the `Allow` level at the given `span` and with the `msg`.
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_span_allow(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ()> {
-        let mut result = self.struct_allow(msg);
-        result.set_span(span);
-        result
-    }
-
-    /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
     /// Also include a code.
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -794,29 +759,14 @@ impl DiagCtxt {
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Warning(None), msg)
-    }
-
-    /// Construct a builder at the `Warning` level with the `msg`. The `id` is used for
-    /// lint emissions which should also fulfill a lint expectation.
-    ///
-    /// Attempting to `.emit()` the builder will only emit if either:
-    /// * `can_emit_warnings` is `true`
-    /// * `is_force_warn` was set in `DiagnosticId::Lint`
-    #[track_caller]
-    pub fn struct_warn_with_expectation(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-        id: LintExpectationId,
-    ) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Warning(Some(id)), msg)
+        DiagnosticBuilder::new(self, Warning(None), msg)
     }
 
     /// Construct a builder at the `Allow` level with the `msg`.
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Allow, msg)
+        DiagnosticBuilder::new(self, Allow, msg)
     }
 
     /// Construct a builder at the `Expect` level with the `msg`.
@@ -827,7 +777,7 @@ impl DiagCtxt {
         msg: impl Into<DiagnosticMessage>,
         id: LintExpectationId,
     ) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Expect(id), msg)
+        DiagnosticBuilder::new(self, Expect(id), msg)
     }
 
     /// Construct a builder at the `Error` level at the given `span` and with the `msg`.
@@ -837,7 +787,7 @@ impl DiagCtxt {
         &self,
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         let mut result = self.struct_err(msg);
         result.set_span(span);
         result
@@ -851,7 +801,7 @@ impl DiagCtxt {
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         let mut result = self.struct_span_err(span, msg);
         result.code(code);
         result
@@ -861,18 +811,8 @@ impl DiagCtxt {
     // FIXME: This method should be removed (every error should have an associated error code).
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_err(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        DiagnosticBuilder::new(self, Level::Error { lint: false }, msg)
-    }
-
-    /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
-    #[doc(hidden)]
-    #[track_caller]
-    pub fn struct_err_lint(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Error { lint: true }, msg)
+    pub fn struct_err(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_> {
+        DiagnosticBuilder::new(self, Error { lint: false }, msg)
     }
 
     /// Construct a builder at the `Error` level with the `msg` and the `code`.
@@ -882,7 +822,7 @@ impl DiagCtxt {
         &self,
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         let mut result = self.struct_err(msg);
         result.code(code);
         result
@@ -935,7 +875,7 @@ impl DiagCtxt {
         &self,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, FatalAbort> {
-        DiagnosticBuilder::new(self, Level::Fatal, msg)
+        DiagnosticBuilder::new(self, Fatal, msg)
     }
 
     /// Construct a builder at the `Fatal` level with the `msg`, that doesn't abort.
@@ -945,27 +885,27 @@ impl DiagCtxt {
         &self,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, FatalError> {
-        DiagnosticBuilder::new(self, Level::Fatal, msg)
+        DiagnosticBuilder::new(self, Fatal, msg)
     }
 
     /// Construct a builder at the `Help` level with the `msg`.
     #[rustc_lint_diagnostics]
     pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Help, msg)
+        DiagnosticBuilder::new(self, Help, msg)
     }
 
     /// Construct a builder at the `Note` level with the `msg`.
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_note(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Note, msg)
+        DiagnosticBuilder::new(self, Note, msg)
     }
 
     /// Construct a builder at the `Bug` level with the `msg`.
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_bug(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, BugAbort> {
-        DiagnosticBuilder::new(self, Level::Bug, msg)
+        DiagnosticBuilder::new(self, Bug, msg)
     }
 
     /// Construct a builder at the `Bug` level at the given `span` with the `msg`.
@@ -1037,7 +977,7 @@ impl DiagCtxt {
     }
 
     pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
-        self.inner.borrow_mut().span_bug(span, msg)
+        self.struct_span_bug(span, msg).emit()
     }
 
     /// For documentation on this, see `Session::span_delayed_bug`.
@@ -1050,20 +990,14 @@ impl DiagCtxt {
         sp: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
     ) -> ErrorGuaranteed {
-        let mut inner = self.inner.borrow_mut();
-
-        // This is technically `self.treat_err_as_bug()` but `span_delayed_bug` is called before
-        // incrementing `err_count` by one, so we need to +1 the comparing.
-        // FIXME: Would be nice to increment err_count in a more coherent way.
-        if inner.flags.treat_err_as_bug.is_some_and(|c| {
-            inner.err_count + inner.lint_err_count + inner.delayed_bug_count() + 1 >= c.get()
-        }) {
+        let treat_next_err_as_bug = self.inner.borrow().treat_next_err_as_bug();
+        if treat_next_err_as_bug {
             // FIXME: don't abort here if report_delayed_bugs is off
-            inner.span_bug(sp, msg);
+            self.span_bug(sp, msg);
         }
-        let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
+        let mut diagnostic = Diagnostic::new(DelayedBug, msg);
         diagnostic.set_span(sp);
-        inner.emit_diagnostic(diagnostic).unwrap()
+        self.emit_diagnostic(diagnostic).unwrap()
     }
 
     // FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's
@@ -1071,7 +1005,7 @@ impl DiagCtxt {
     pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
         let mut inner = self.inner.borrow_mut();
 
-        let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
+        let mut diagnostic = Diagnostic::new(DelayedBug, msg);
         if inner.flags.report_delayed_bugs {
             inner.emit_diagnostic_without_consuming(&mut diagnostic);
         }
@@ -1184,10 +1118,9 @@ impl DiagCtxt {
 
         match (errors.len(), warnings.len()) {
             (0, 0) => return,
-            (0, _) => inner.emitter.emit_diagnostic(&Diagnostic::new(
-                Level::Warning(None),
-                DiagnosticMessage::Str(warnings),
-            )),
+            (0, _) => inner
+                .emitter
+                .emit_diagnostic(&Diagnostic::new(Warning(None), DiagnosticMessage::Str(warnings))),
             (_, 0) => {
                 inner.emit_diagnostic(Diagnostic::new(Fatal, errors));
             }
@@ -1219,14 +1152,12 @@ impl DiagCtxt {
                         if error_codes.len() > 9 { "..." } else { "." }
                     ));
                     inner.failure_note(format!(
-                        "For more information about an error, try \
-                         `rustc --explain {}`.",
+                        "For more information about an error, try `rustc --explain {}`.",
                         &error_codes[0]
                     ));
                 } else {
                     inner.failure_note(format!(
-                        "For more information about this error, try \
-                         `rustc --explain {}`.",
+                        "For more information about this error, try `rustc --explain {}`.",
                         &error_codes[0]
                     ));
                 }
@@ -1277,18 +1208,15 @@ impl DiagCtxt {
     }
 
     #[track_caller]
-    pub fn create_err<'a>(
-        &'a self,
-        err: impl IntoDiagnostic<'a>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        err.into_diagnostic(self, Level::Error { lint: false })
+    pub fn create_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> DiagnosticBuilder<'a> {
+        err.into_diagnostic(self, Error { lint: false })
     }
 
     pub fn create_warning<'a>(
         &'a self,
         warning: impl IntoDiagnostic<'a, ()>,
     ) -> DiagnosticBuilder<'a, ()> {
-        warning.into_diagnostic(self, Level::Warning(None))
+        warning.into_diagnostic(self, Warning(None))
     }
 
     pub fn emit_warning<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) {
@@ -1299,7 +1227,7 @@ impl DiagCtxt {
         &'a self,
         fatal: impl IntoDiagnostic<'a, FatalError>,
     ) -> DiagnosticBuilder<'a, FatalError> {
-        fatal.into_diagnostic(self, Level::Fatal)
+        fatal.into_diagnostic(self, Fatal)
     }
 
     pub fn emit_almost_fatal<'a>(
@@ -1313,7 +1241,7 @@ impl DiagCtxt {
         &'a self,
         fatal: impl IntoDiagnostic<'a, FatalAbort>,
     ) -> DiagnosticBuilder<'a, FatalAbort> {
-        fatal.into_diagnostic(self, Level::Fatal)
+        fatal.into_diagnostic(self, Fatal)
     }
 
     pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, FatalAbort>) -> ! {
@@ -1324,7 +1252,7 @@ impl DiagCtxt {
         &'a self,
         bug: impl IntoDiagnostic<'a, BugAbort>,
     ) -> DiagnosticBuilder<'a, BugAbort> {
-        bug.into_diagnostic(self, Level::Bug)
+        bug.into_diagnostic(self, Bug)
     }
 
     pub fn emit_bug<'a>(&'a self, bug: impl IntoDiagnostic<'a, BugAbort>) -> ! {
@@ -1339,7 +1267,7 @@ impl DiagCtxt {
         &'a self,
         note: impl IntoDiagnostic<'a, ()>,
     ) -> DiagnosticBuilder<'a, ()> {
-        note.into_diagnostic(self, Level::Note)
+        note.into_diagnostic(self, Note)
     }
 
     pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
@@ -1426,7 +1354,7 @@ impl DiagCtxtInner {
         for diag in diags {
             // Decrement the count tracking the stash; emitting will increment it.
             if diag.is_error() {
-                if matches!(diag.level, Level::Error { lint: true }) {
+                if matches!(diag.level, Error { lint: true }) {
                     self.lint_err_count -= 1;
                 } else {
                     self.err_count -= 1;
@@ -1457,9 +1385,8 @@ impl DiagCtxtInner {
         &mut self,
         diagnostic: &mut Diagnostic,
     ) -> Option<ErrorGuaranteed> {
-        if matches!(diagnostic.level, Level::Error { .. } | Level::Fatal) && self.treat_err_as_bug()
-        {
-            diagnostic.level = Level::Bug;
+        if matches!(diagnostic.level, Error { .. } | Fatal) && self.treat_err_as_bug() {
+            diagnostic.level = Bug;
         }
 
         // The `LintExpectationId` can be stable or unstable depending on when it was created.
@@ -1471,7 +1398,7 @@ impl DiagCtxtInner {
             return None;
         }
 
-        if diagnostic.level == Level::DelayedBug {
+        if diagnostic.level == DelayedBug {
             // FIXME(eddyb) this should check for `has_errors` and stop pushing
             // once *any* errors were emitted (and truncate `span_delayed_bugs`
             // when an error is first emitted, also), but maybe there's a case
@@ -1487,7 +1414,7 @@ impl DiagCtxtInner {
         }
 
         if diagnostic.has_future_breakage() {
-            // Future breakages aren't emitted if they're Level::Allowed,
+            // Future breakages aren't emitted if they're Level::Allow,
             // but they still need to be constructed and stashed below,
             // so they'll trigger the good-path bug check.
             self.suppressed_expected_diag = true;
@@ -1509,7 +1436,7 @@ impl DiagCtxtInner {
             return None;
         }
 
-        if matches!(diagnostic.level, Level::Expect(_) | Level::Allow) {
+        if matches!(diagnostic.level, Expect(_) | Allow) {
             (*TRACK_DIAGNOSTICS)(diagnostic, &mut |_| {});
             return None;
         }
@@ -1534,7 +1461,7 @@ impl DiagCtxtInner {
                 debug!(?self.emitted_diagnostics);
                 let already_emitted_sub = |sub: &mut SubDiagnostic| {
                     debug!(?sub);
-                    if sub.level != Level::OnceNote && sub.level != Level::OnceHelp {
+                    if sub.level != OnceNote && sub.level != OnceHelp {
                         return false;
                     }
                     let mut hasher = StableHasher::new();
@@ -1559,7 +1486,7 @@ impl DiagCtxtInner {
                 }
             }
             if diagnostic.is_error() {
-                if matches!(diagnostic.level, Level::Error { lint: true }) {
+                if matches!(diagnostic.level, Error { lint: true }) {
                     self.bump_lint_err_count();
                 } else {
                     self.bump_err_count();
@@ -1583,6 +1510,13 @@ impl DiagCtxtInner {
         })
     }
 
+    // Use this one before incrementing `err_count`.
+    fn treat_next_err_as_bug(&self) -> bool {
+        self.flags.treat_err_as_bug.is_some_and(|c| {
+            self.err_count + self.lint_err_count + self.delayed_bug_count() + 1 >= c.get()
+        })
+    }
+
     fn delayed_bug_count(&self) -> usize {
         self.span_delayed_bugs.len() + self.good_path_delayed_bugs.len()
     }
@@ -1591,27 +1525,22 @@ impl DiagCtxtInner {
         self.err_count > 0
     }
 
-    #[track_caller]
-    fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
-        let mut diag = Diagnostic::new(Bug, msg);
-        diag.set_span(sp);
-        self.emit_diagnostic(diag);
-        panic::panic_any(ExplicitBug);
-    }
-
     fn failure_note(&mut self, msg: impl Into<DiagnosticMessage>) {
         self.emit_diagnostic(Diagnostic::new(FailureNote, msg));
     }
 
     fn flush_delayed(
         &mut self,
-        bugs: impl IntoIterator<Item = DelayedDiagnostic>,
+        bugs: Vec<DelayedDiagnostic>,
         explanation: impl Into<DiagnosticMessage> + Copy,
     ) {
-        let mut no_bugs = true;
+        if bugs.is_empty() {
+            return;
+        }
+
         // If backtraces are enabled, also print the query stack
         let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0");
-        for bug in bugs {
+        for (i, bug) in bugs.into_iter().enumerate() {
             if let Some(file) = self.ice_file.as_ref()
                 && let Ok(mut out) = std::fs::File::options().create(true).append(true).open(file)
             {
@@ -1619,25 +1548,25 @@ impl DiagCtxtInner {
                     &mut out,
                     "delayed span bug: {}\n{}\n",
                     bug.inner
-                        .styled_message()
+                        .messages()
                         .iter()
                         .filter_map(|(msg, _)| msg.as_str())
                         .collect::<String>(),
                     &bug.note
                 );
             }
-            let mut bug =
-                if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
 
-            if no_bugs {
+            if i == 0 {
                 // Put the overall explanation before the `DelayedBug`s, to
                 // frame them better (e.g. separate warnings from them).
                 self.emit_diagnostic(Diagnostic::new(Bug, explanation));
-                no_bugs = false;
             }
 
+            let mut bug =
+                if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
+
             // "Undelay" the `DelayedBug`s (into plain `Bug`s).
-            if bug.level != Level::DelayedBug {
+            if bug.level != DelayedBug {
                 // NOTE(eddyb) not panicking here because we're already producing
                 // an ICE, and the more information the merrier.
                 bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
@@ -1645,15 +1574,13 @@ impl DiagCtxtInner {
                     level: bug.level,
                 });
             }
-            bug.level = Level::Bug;
+            bug.level = Bug;
 
             self.emit_diagnostic(bug);
         }
 
         // Panic with `DelayedBugPanic` to avoid "unexpected panic" messages.
-        if !no_bugs {
-            panic::panic_any(DelayedBugPanic);
-        }
+        panic::panic_any(DelayedBugPanic);
     }
 
     fn bump_lint_err_count(&mut self) {
@@ -1731,25 +1658,80 @@ impl DelayedDiagnostic {
 
 #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
 pub enum Level {
+    /// For bugs in the compiler. Manifests as an ICE (internal compiler error) panic.
+    ///
+    /// Its `EmissionGuarantee` is `BugAbort`.
     Bug,
+
+    /// This is a strange one: lets you register an error without emitting it. If compilation ends
+    /// without any other errors occurring, this will be emitted as a bug. Otherwise, it will be
+    /// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths
+    /// that should only be reached when compiling erroneous code.
+    ///
+    /// Its `EmissionGuarantee` is `ErrorGuaranteed`.
     DelayedBug,
+
+    /// An error that causes an immediate abort. Used for things like configuration errors,
+    /// internal overflows, some file operation errors.
+    ///
+    /// Its `EmissionGuarantee` is `FatalAbort`, except in the non-aborting "almost fatal" case
+    /// that is occasionaly used, where it is `FatalError`.
     Fatal,
+
+    /// An error in the code being compiled, which prevents compilation from finishing. This is the
+    /// most common case.
+    ///
+    /// Its `EmissionGuarantee` is `ErrorGuaranteed`.
     Error {
-        /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called.
+        /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is
+        /// called.
         lint: bool,
     },
+
+    /// A warning about the code being compiled. Does not prevent compilation from finishing.
+    ///
     /// This [`LintExpectationId`] is used for expected lint diagnostics, which should
     /// also emit a warning due to the `force-warn` flag. In all other cases this should
     /// be `None`.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Warning(Option<LintExpectationId>),
+
+    /// A message giving additional context. Rare, because notes are more commonly attached to other
+    /// diagnostics such as errors.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Note,
-    /// A note that is only emitted once.
+
+    /// A note that is only emitted once. Rare, mostly used in circumstances relating to lints.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     OnceNote,
+
+    /// A message suggesting how to fix something. Rare, because help messages are more commonly
+    /// attached to other diagnostics such as errors.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Help,
-    /// A help that is only emitted once.
+
+    /// A help that is only emitted once. Rare.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     OnceHelp,
+
+    /// Similar to `Note`, but used in cases where compilation has failed. Rare.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     FailureNote,
+
+    /// Only used for lints.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Allow,
+
+    /// Only used for lints.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Expect(LintExpectationId),
 }
 
@@ -1789,8 +1771,7 @@ impl Level {
             Note | OnceNote => "note",
             Help | OnceHelp => "help",
             FailureNote => "failure-note",
-            Allow => panic!("Shouldn't call on allowed error"),
-            Expect(_) => panic!("Shouldn't call on expected error"),
+            Allow | Expect(_) => unreachable!(),
         }
     }
 
@@ -1800,7 +1781,7 @@ impl Level {
 
     pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
         match self {
-            Level::Expect(id) | Level::Warning(Some(id)) => Some(*id),
+            Expect(id) | Warning(Some(id)) => Some(*id),
             _ => None,
         }
     }
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index ee0b10edfda..1fd4d2d55dd 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1118,15 +1118,12 @@ impl<'a> ExtCtxt<'a> {
         &self,
         sp: S,
         msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'a> {
         self.sess.dcx().struct_span_err(sp, msg)
     }
 
     #[track_caller]
-    pub fn create_err(
-        &self,
-        err: impl IntoDiagnostic<'a>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    pub fn create_err(&self, err: impl IntoDiagnostic<'a>) -> DiagnosticBuilder<'a> {
         self.sess.create_err(err)
     }
 
@@ -1230,7 +1227,7 @@ pub fn expr_to_spanned_string<'a>(
     cx: &'a mut ExtCtxt<'_>,
     expr: P<ast::Expr>,
     err_msg: &'static str,
-) -> Result<(Symbol, ast::StrStyle, Span), Option<(DiagnosticBuilder<'a, ErrorGuaranteed>, bool)>> {
+) -> Result<(Symbol, ast::StrStyle, Span), Option<(DiagnosticBuilder<'a>, bool)>> {
     // Perform eager expansion on the expression.
     // We want to be able to handle e.g., `concat!("foo", "bar")`.
     let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index 8f80e6e2927..b6718ec8c41 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -215,7 +215,7 @@ impl<'matcher> Tracker<'matcher> for FailureForwarder {
 }
 
 pub(super) fn emit_frag_parse_err(
-    mut e: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed>,
+    mut e: DiagnosticBuilder<'_>,
     parser: &Parser<'_>,
     orig_parser: &mut Parser<'_>,
     site_span: Span,
@@ -224,11 +224,11 @@ pub(super) fn emit_frag_parse_err(
 ) {
     // FIXME(davidtwco): avoid depending on the error message text
     if parser.token == token::Eof
-        && let DiagnosticMessage::Str(message) = &e.message[0].0
+        && let DiagnosticMessage::Str(message) = &e.messages[0].0
         && message.ends_with(", found `<eof>`")
     {
-        let msg = &e.message[0];
-        e.message[0] = (
+        let msg = &e.messages[0];
+        e.messages[0] = (
             DiagnosticMessage::from(format!(
                 "macro expansion ends with an incomplete expression: {}",
                 message.replace(", found `<eof>`", ""),
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 80fd82e0302..ac5f3fb325d 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -9,8 +9,8 @@ use rustc_ast::mut_visit::{self, MutVisitor};
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
 use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::DiagnosticBuilder;
 use rustc_errors::{pluralize, PResult};
-use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
 use rustc_span::hygiene::{LocalExpnId, Transparency};
 use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent};
 use rustc_span::Span;
@@ -528,7 +528,7 @@ fn out_of_bounds_err<'a>(
     max: usize,
     span: Span,
     ty: &str,
-) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'a> {
     let msg = if max == 0 {
         format!(
             "meta-variable expression `{ty}` with depth parameter \
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index a0dec89d631..cd59ea9092c 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -43,7 +43,7 @@ pub enum ModError<'a> {
     ModInBlock(Option<Ident>),
     FileNotFound(Ident, PathBuf, PathBuf),
     MultipleCandidates(Ident, PathBuf, PathBuf),
-    ParserError(DiagnosticBuilder<'a, ErrorGuaranteed>),
+    ParserError(DiagnosticBuilder<'a>),
 }
 
 pub(crate) fn parse_external_mod(
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index c412b064ce9..5eb6aed7253 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -499,12 +499,7 @@ impl server::FreeFunctions for Rustc<'_, '_> {
             rustc_errors::Diagnostic::new(diagnostic.level.to_internal(), diagnostic.message);
         diag.set_span(MultiSpan::from_spans(diagnostic.spans));
         for child in diagnostic.children {
-            diag.sub(
-                child.level.to_internal(),
-                child.message,
-                MultiSpan::from_spans(child.spans),
-                None,
-            );
+            diag.sub(child.level.to_internal(), child.message, MultiSpan::from_spans(child.spans));
         }
         self.sess().dcx.emit_diagnostic(diag);
     }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 26430dcf965..452f5d0b7ac 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1351,15 +1351,8 @@ impl<'hir> Body<'hir> {
 /// The type of source expression that caused this coroutine to be created.
 #[derive(Clone, PartialEq, Eq, Debug, Copy, Hash, HashStable_Generic, Encodable, Decodable)]
 pub enum CoroutineKind {
-    /// An explicit `async` block or the body of an `async` function.
-    Async(CoroutineSource),
-
-    /// An explicit `gen` block or the body of a `gen` function.
-    Gen(CoroutineSource),
-
-    /// An explicit `async gen` block or the body of an `async gen` function,
-    /// which is able to both `yield` and `.await`.
-    AsyncGen(CoroutineSource),
+    /// A coroutine that comes from a desugaring.
+    Desugared(CoroutineDesugaring, CoroutineSource),
 
     /// A coroutine literal created via a `yield` inside a closure.
     Coroutine,
@@ -1368,31 +1361,11 @@ pub enum CoroutineKind {
 impl fmt::Display for CoroutineKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            CoroutineKind::Async(k) => {
-                if f.alternate() {
-                    f.write_str("`async` ")?;
-                } else {
-                    f.write_str("async ")?
-                }
+            CoroutineKind::Desugared(d, k) => {
+                d.fmt(f)?;
                 k.fmt(f)
             }
             CoroutineKind::Coroutine => f.write_str("coroutine"),
-            CoroutineKind::Gen(k) => {
-                if f.alternate() {
-                    f.write_str("`gen` ")?;
-                } else {
-                    f.write_str("gen ")?
-                }
-                k.fmt(f)
-            }
-            CoroutineKind::AsyncGen(k) => {
-                if f.alternate() {
-                    f.write_str("`async gen` ")?;
-                } else {
-                    f.write_str("async gen ")?
-                }
-                k.fmt(f)
-            }
         }
     }
 }
@@ -1425,6 +1398,49 @@ impl fmt::Display for CoroutineSource {
     }
 }
 
+#[derive(Clone, PartialEq, Eq, Debug, Copy, Hash, HashStable_Generic, Encodable, Decodable)]
+pub enum CoroutineDesugaring {
+    /// An explicit `async` block or the body of an `async` function.
+    Async,
+
+    /// An explicit `gen` block or the body of a `gen` function.
+    Gen,
+
+    /// An explicit `async gen` block or the body of an `async gen` function,
+    /// which is able to both `yield` and `.await`.
+    AsyncGen,
+}
+
+impl fmt::Display for CoroutineDesugaring {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            CoroutineDesugaring::Async => {
+                if f.alternate() {
+                    f.write_str("`async` ")?;
+                } else {
+                    f.write_str("async ")?
+                }
+            }
+            CoroutineDesugaring::Gen => {
+                if f.alternate() {
+                    f.write_str("`gen` ")?;
+                } else {
+                    f.write_str("gen ")?
+                }
+            }
+            CoroutineDesugaring::AsyncGen => {
+                if f.alternate() {
+                    f.write_str("`async gen` ")?;
+                } else {
+                    f.write_str("async gen ")?
+                }
+            }
+        }
+
+        Ok(())
+    }
+}
+
 #[derive(Copy, Clone, Debug)]
 pub enum BodyOwnerKind {
     /// Functions and methods.
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 7691cd11c4f..3f3b57ba94f 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -308,6 +308,7 @@ language_item_table! {
     FuturePoll,              sym::poll,                future_poll_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
 
     AsyncIteratorPollNext,   sym::async_iterator_poll_next, async_iterator_poll_next, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0);
+    IntoAsyncIterIntoIter,   sym::into_async_iter_into_iter, into_async_iter_into_iter, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0);
 
     Option,                  sym::Option,              option_type,                Target::Enum,           GenericRequirement::None;
     OptionSome,              sym::Some,                option_some_variant,        Target::Variant,        GenericRequirement::None;
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 6f8e80172dd..d1f99f3b799 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -2686,7 +2686,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         &self,
         constrained_regions: FxHashSet<ty::BoundRegionKind>,
         referenced_regions: FxHashSet<ty::BoundRegionKind>,
-        generate_err: impl Fn(&str) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+        generate_err: impl Fn(&str) -> DiagnosticBuilder<'tcx>,
     ) {
         for br in referenced_regions.difference(&constrained_regions) {
             let br_name = match *br {
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index b2ff7959106..cc34dbfd9b9 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1912,11 +1912,7 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error
     res.and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id)))
 }
 
-fn error_392(
-    tcx: TyCtxt<'_>,
-    span: Span,
-    param_name: Symbol,
-) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+fn error_392(tcx: TyCtxt<'_>, span: Span, param_name: Symbol) -> DiagnosticBuilder<'_> {
     let mut err = struct_span_err!(tcx.sess, span, E0392, "parameter `{param_name}` is never used");
     err.span_label(span, "unused parameter");
     err
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index d48535c82f5..d43b4adfe39 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -181,7 +181,7 @@ pub(crate) fn placeholder_type_error_diag<'tcx>(
     suggest: bool,
     hir_ty: Option<&hir::Ty<'_>>,
     kind: &'static str,
-) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'tcx> {
     if placeholder_types.is_empty() {
         return bad_placeholder(tcx, additional_spans, kind);
     }
@@ -333,7 +333,7 @@ fn bad_placeholder<'tcx>(
     tcx: TyCtxt<'tcx>,
     mut spans: Vec<Span>,
     kind: &'static str,
-) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'tcx> {
     let kind = if kind.ends_with('s') { format!("{kind}es") } else { format!("{kind}s") };
 
     spans.sort();
diff --git a/compiler/rustc_hir_analysis/src/structured_errors.rs b/compiler/rustc_hir_analysis/src/structured_errors.rs
index 0b46fce1735..04d04304e70 100644
--- a/compiler/rustc_hir_analysis/src/structured_errors.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors.rs
@@ -6,7 +6,7 @@ pub use self::{
     missing_cast_for_variadic_arg::*, sized_unsized_cast::*, wrong_number_of_generic_args::*,
 };
 
-use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed};
+use rustc_errors::{DiagnosticBuilder, DiagnosticId};
 use rustc_session::Session;
 
 pub trait StructuredDiagnostic<'tcx> {
@@ -14,7 +14,7 @@ pub trait StructuredDiagnostic<'tcx> {
 
     fn code(&self) -> DiagnosticId;
 
-    fn diagnostic(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn diagnostic(&self) -> DiagnosticBuilder<'tcx> {
         let err = self.diagnostic_common();
 
         if self.session().teach(&self.code()) {
@@ -24,19 +24,13 @@ pub trait StructuredDiagnostic<'tcx> {
         }
     }
 
-    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx>;
 
-    fn diagnostic_regular(
-        &self,
-        err: DiagnosticBuilder<'tcx, ErrorGuaranteed>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn diagnostic_regular(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
         err
     }
 
-    fn diagnostic_extended(
-        &self,
-        err: DiagnosticBuilder<'tcx, ErrorGuaranteed>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn diagnostic_extended(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
         err
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
index c37dff61b72..7cc4982820b 100644
--- a/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
@@ -1,5 +1,5 @@
 use crate::{errors, structured_errors::StructuredDiagnostic};
-use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed};
+use rustc_errors::{DiagnosticBuilder, DiagnosticId};
 use rustc_middle::ty::{Ty, TypeVisitableExt};
 use rustc_session::Session;
 use rustc_span::Span;
@@ -20,7 +20,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for MissingCastForVariadicArg<'tcx, '_> {
         rustc_errors::error_code!(E0617)
     }
 
-    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> {
         let (sugg_span, replace, help) =
             if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.span) {
                 (Some(self.span), format!("{} as {}", snippet, self.cast_ty), None)
@@ -44,10 +44,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for MissingCastForVariadicArg<'tcx, '_> {
         err
     }
 
-    fn diagnostic_extended(
-        &self,
-        mut err: DiagnosticBuilder<'tcx, ErrorGuaranteed>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn diagnostic_extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
         err.note(format!(
             "certain types, like `{}`, must be casted before passing them to a \
                 variadic function, because of arcane ABI rules dictated by the C \
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
index 910417abe6e..6ba27f49744 100644
--- a/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
@@ -1,5 +1,5 @@
 use crate::{errors, structured_errors::StructuredDiagnostic};
-use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed};
+use rustc_errors::{DiagnosticBuilder, DiagnosticId};
 use rustc_middle::ty::{Ty, TypeVisitableExt};
 use rustc_session::Session;
 use rustc_span::Span;
@@ -20,7 +20,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCast<'tcx> {
         rustc_errors::error_code!(E0607)
     }
 
-    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> {
         let mut err = self.sess.create_err(errors::CastThinPointerToFatPointer {
             span: self.span,
             expr_ty: self.expr_ty,
@@ -34,10 +34,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCast<'tcx> {
         err
     }
 
-    fn diagnostic_extended(
-        &self,
-        mut err: DiagnosticBuilder<'tcx, ErrorGuaranteed>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn diagnostic_extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
         err.help(
             "Thin pointers are \"simple\" pointers: they are purely a reference to a
 memory address.
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
index fab841e3679..c7818d80dbf 100644
--- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
@@ -1,7 +1,6 @@
 use crate::structured_errors::StructuredDiagnostic;
 use rustc_errors::{
-    pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed,
-    MultiSpan,
+    pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, MultiSpan,
 };
 use rustc_hir as hir;
 use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
@@ -521,7 +520,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> {
         let span = self.path_segment.ident.span;
         let msg = self.create_error_message();
 
@@ -1113,7 +1112,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> {
         rustc_errors::error_code!(E0107)
     }
 
-    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> {
         let mut err = self.start_diagnostics();
 
         self.notify(&mut err);
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 5e6b54950b3..2146effd84f 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -305,8 +305,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ) = (parent_node, callee_node)
         {
             let fn_decl_span = if hir.body(body).coroutine_kind
-                == Some(hir::CoroutineKind::Async(hir::CoroutineSource::Closure))
-            {
+                == Some(hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Async,
+                    hir::CoroutineSource::Closure,
+                )) {
                 // Actually need to unwrap one more layer of HIR to get to
                 // the _real_ closure...
                 let async_closure = hir.parent_id(parent_hir_id);
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 0de0365364c..9783fe79a90 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -187,7 +187,7 @@ fn make_invalid_casting_error<'a, 'tcx>(
     expr_ty: Ty<'tcx>,
     cast_ty: Ty<'tcx>,
     fcx: &FnCtxt<'a, 'tcx>,
-) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'a> {
     type_error_struct!(
         sess,
         span,
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 2855cea80b2..8e2af402918 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -59,7 +59,8 @@ pub(super) fn check_fn<'a, 'tcx>(
         && can_be_coroutine.is_some()
     {
         let yield_ty = match kind {
-            hir::CoroutineKind::Gen(..) | hir::CoroutineKind::Coroutine => {
+            hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
+            | hir::CoroutineKind::Coroutine => {
                 let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
                     kind: TypeVariableOriginKind::TypeInference,
                     span,
@@ -71,7 +72,7 @@ pub(super) fn check_fn<'a, 'tcx>(
             // guide inference on the yield type so that we can handle `AsyncIterator`
             // in this block in projection correctly. In the new trait solver, it is
             // not a problem.
-            hir::CoroutineKind::AsyncGen(..) => {
+            hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
                 let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
                     kind: TypeVariableOriginKind::TypeInference,
                     span,
@@ -89,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>(
                     .into()]),
                 )
             }
-            hir::CoroutineKind::Async(..) => Ty::new_unit(tcx),
+            hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => Ty::new_unit(tcx),
         };
 
         // Resume type defaults to `()` if the coroutine has no argument.
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index d19d304128a..cd42be28e6f 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -634,7 +634,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // In the case of the async block that we create for a function body,
                 // we expect the return type of the block to match that of the enclosing
                 // function.
-                Some(hir::CoroutineKind::Async(hir::CoroutineSource::Fn)) => {
+                Some(hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Async,
+                    hir::CoroutineSource::Fn,
+                )) => {
                     debug!("closure is async fn body");
                     let def_id = self.tcx.hir().body_owner_def_id(body.id());
                     self.deduce_future_output_from_obligations(expr_def_id, def_id).unwrap_or_else(
@@ -651,9 +654,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     )
                 }
                 // All `gen {}` and `async gen {}` must return unit.
-                Some(hir::CoroutineKind::Gen(_) | hir::CoroutineKind::AsyncGen(_)) => {
-                    self.tcx.types.unit
-                }
+                Some(
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
+                    | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _),
+                ) => self.tcx.types.unit,
 
                 _ => astconv.ty_infer(None, decl.output.span()),
             },
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 61236c07135..aed2dbb4505 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -36,9 +36,7 @@
 //! ```
 
 use crate::FnCtxt;
-use rustc_errors::{
-    struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
-};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -1772,7 +1770,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         id: hir::HirId,
         expression: Option<&'tcx hir::Expr<'tcx>>,
         blk_id: Option<hir::HirId>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'a> {
         let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err);
 
         let parent_id = fcx.tcx.hir().parent_id(id);
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 8b666c63425..6e5bd740b2e 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1,6 +1,6 @@
 use crate::FnCtxt;
 use rustc_errors::MultiSpan;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::intravisit::Visitor;
@@ -168,7 +168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         sp: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
     }
 
@@ -178,7 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         cause: &ObligationCause<'tcx>,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         match self.at(cause, self.param_env).sup(DefineOpaqueTypes::Yes, expected, actual) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
@@ -199,7 +199,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         sp: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
     }
 
@@ -208,7 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         cause: &ObligationCause<'tcx>,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         match self.at(cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, actual) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
@@ -246,7 +246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
         allow_two_phase: AllowTwoPhase,
-    ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>) {
+    ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
         let expected = self.resolve_vars_with_obligations(expected);
 
         let e = match self.coerce(expr, checked_ty, expected, allow_two_phase, None) {
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 5d5b5f39ccf..c4755b852bd 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -966,7 +966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn check_for_missing_semi(
         &self,
         expr: &'tcx hir::Expr<'tcx>,
-        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        err: &mut DiagnosticBuilder<'_>,
     ) -> bool {
         if let hir::ExprKind::Binary(binop, lhs, rhs) = expr.kind
             && let hir::BinOpKind::Mul = binop.node
@@ -2738,7 +2738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         field: Ident,
         expr_t: Ty<'tcx>,
         id: HirId,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         let span = field.span;
         debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t);
 
@@ -2821,11 +2821,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err
     }
 
-    fn private_field_err(
-        &self,
-        field: Ident,
-        base_did: DefId,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    fn private_field_err(&self, field: Ident, base_did: DefId) -> DiagnosticBuilder<'_> {
         let struct_path = self.tcx().def_path_str(base_did);
         let kind_name = self.tcx().def_descr(base_did);
         let mut err = struct_span_err!(
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 4caa0df58b6..752401845cd 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1258,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty: Ty<'tcx>,
         provided_ty: Ty<'tcx>,
         arg: &hir::Expr<'tcx>,
-        err: &mut rustc_errors::DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+        err: &mut rustc_errors::DiagnosticBuilder<'tcx>,
     ) {
         if let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Mut, .. }) = expected_ty.kind()
             && let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Not, .. }) =
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 668e547571f..d2917b25c54 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -17,8 +17,8 @@ use rustc_hir::def::Res;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{
-    CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node, Path, QPath, Stmt,
-    StmtKind, TyKind, WherePredicate,
+    CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node,
+    Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
 };
 use rustc_hir_analysis::astconv::AstConv;
 use rustc_infer::traits::{self, StatementAsExpression};
@@ -549,7 +549,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ty::Coroutine(def_id, ..)
                     if matches!(
                         self.tcx.coroutine_kind(def_id),
-                        Some(CoroutineKind::Async(CoroutineSource::Closure))
+                        Some(CoroutineKind::Desugared(
+                            CoroutineDesugaring::Async,
+                            CoroutineSource::Closure
+                        ))
                     ) =>
                 {
                     errors::SuggestBoxing::AsyncBody
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 7595f21d9f1..9327161eccb 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -13,8 +13,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::StashKey;
 use rustc_errors::{
-    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
-    MultiSpan,
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan,
 };
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -120,7 +119,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         args: Option<&'tcx [hir::Expr<'tcx>]>,
         expected: Expectation<'tcx>,
         trait_missing_method: bool,
-    ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'_>> {
         // Avoid suggestions when we don't know what's going on.
         if rcvr_ty.references_error() {
             return None;
@@ -261,7 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         rcvr_ty: Ty<'tcx>,
         rcvr_expr: &hir::Expr<'tcx>,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         let mut file = None;
         let ty_str = self.tcx.short_ty_string(rcvr_ty, &mut file);
         let mut err = struct_span_err!(
@@ -299,7 +298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         no_match_data: &mut NoMatchData<'tcx>,
         expected: Expectation<'tcx>,
         trait_missing_method: bool,
-    ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'_>> {
         let mode = no_match_data.mode;
         let tcx = self.tcx;
         let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 56a420fab4f..0c0af51cb0d 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -99,7 +99,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
         ti: TopInfo<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         let mut diag =
             self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?;
         if let Some(expr) = ti.origin_expr {
@@ -967,7 +967,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn emit_bad_pat_path(
         &self,
-        mut e: DiagnosticBuilder<'_, ErrorGuaranteed>,
+        mut e: DiagnosticBuilder<'_>,
         pat: &hir::Pat<'tcx>,
         res: Res,
         pat_res: Res,
@@ -1508,7 +1508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         variant: &VariantDef,
         pat: &'_ Pat<'_>,
         fields: &[hir::PatField<'_>],
-    ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'_>> {
         // if this is a tuple struct, then all field names will be numbers
         // so if any fields in a struct pattern use shorthand syntax, they will
         // be invalid identifiers (for example, Foo { 0, 1 }).
@@ -1584,7 +1584,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat: &'tcx Pat<'tcx>,
         variant: &ty::VariantDef,
         args: &'tcx ty::List<ty::GenericArg<'tcx>>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let tcx = self.tcx;
         let (field_names, t, plural) = if let [field] = inexistent_fields {
             (format!("a field named `{}`", field.ident), "this", "")
@@ -1689,7 +1689,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat: &Pat<'_>,
         fields: &'tcx [hir::PatField<'tcx>],
         variant: &ty::VariantDef,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         if let (Some(CtorKind::Fn), PatKind::Struct(qpath, pattern_fields, ..)) =
             (variant.ctor_kind(), &pat.kind)
         {
@@ -1775,7 +1775,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         pat: &Pat<'_>,
         fields: &'tcx [hir::PatField<'tcx>],
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let mut err = self
             .tcx
             .sess
@@ -1867,7 +1867,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         unmentioned_fields: &[(&ty::FieldDef, Ident)],
         have_inaccessible_fields: bool,
         fields: &'tcx [hir::PatField<'tcx>],
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let inaccessible = if have_inaccessible_fields { " and inaccessible fields" } else { "" };
         let field_names = if let [(_, field)] = unmentioned_fields {
             format!("field `{field}`{inaccessible}")
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index 3ea1a52ae28..d730ef58deb 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -502,6 +502,7 @@ impl<T: Idx> ChunkedBitSet<T> {
                     };
                     #[cfg(not(feature = "nightly"))]
                     let mut words = {
+                        // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291).
                         let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed();
                         // SAFETY: `words` can safely be all zeroes.
                         let words = unsafe { words.assume_init() };
@@ -567,6 +568,7 @@ impl<T: Idx> ChunkedBitSet<T> {
                     };
                     #[cfg(not(feature = "nightly"))]
                     let mut words = {
+                        // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291).
                         let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed();
                         // SAFETY: `words` can safely be all zeroes.
                         let words = unsafe { words.assume_init() };
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index d396c41007b..d77c224f3c8 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -306,7 +306,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
     hidden_ty: Ty<'tcx>,
     hidden_region: ty::Region<'tcx>,
     opaque_ty_key: ty::OpaqueTypeKey<'tcx>,
-) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'tcx> {
     let mut err = tcx.sess.create_err(errors::OpaqueCapturesLifetime {
         span,
         opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args),
@@ -2171,7 +2171,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         trace: TypeTrace<'tcx>,
         terr: TypeError<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
 
         let span = trace.cause.span();
@@ -2319,7 +2319,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         origin: Option<SubregionOrigin<'tcx>>,
         bound_kind: GenericKind<'tcx>,
         sub: Region<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         if let Some(SubregionOrigin::CompareImplItemObligation {
             span,
             impl_item_def_id,
@@ -2732,7 +2732,7 @@ impl<'tcx> InferCtxt<'tcx> {
     fn report_inference_failure(
         &self,
         var_origin: RegionVariableOrigin,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let br_string = |br: ty::BoundRegionKind| {
             let mut s = match br {
                 ty::BrNamed(_, name) => name.to_string(),
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index 700fb5d3510..fd38e52cfde 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -5,7 +5,7 @@ use crate::errors::{
 use crate::infer::error_reporting::TypeErrCtxt;
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::InferCtxt;
-use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg};
+use rustc_errors::{DiagnosticBuilder, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
@@ -358,7 +358,7 @@ impl<'tcx> InferCtxt<'tcx> {
         span: Span,
         arg_data: InferenceDiagnosticsData,
         error_code: TypeAnnotationNeeded,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let source_kind = "other";
         let source_name = "";
         let failure_span = None;
@@ -406,7 +406,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         arg: GenericArg<'tcx>,
         error_code: TypeAnnotationNeeded,
         should_label_span: bool,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let arg = self.resolve_vars_if_possible(arg);
         let arg_data = self.extract_inference_diagnostics_data(arg, None);
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
index 2a70c4673b0..e13d1d38a89 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
@@ -50,7 +50,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
         self.cx.tcx
     }
 
-    pub fn try_report_from_nll(&self) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    pub fn try_report_from_nll(&self) -> Option<DiagnosticBuilder<'tcx>> {
         // Due to the improved diagnostics returned by the MIR borrow checker, only a subset of
         // the nice region errors are required when running under the MIR borrow checker.
         self.try_report_named_anon_conflict()
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
index 1b43022f8f7..a5a69b77a05 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -5,16 +5,14 @@ use crate::{
     errors::ExplicitLifetimeRequired,
     infer::error_reporting::nice_region_error::find_anon_type::find_anon_type,
 };
-use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::DiagnosticBuilder;
 use rustc_middle::ty;
 use rustc_span::symbol::kw;
 
 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     /// When given a `ConcreteFailure` for a function with parameters containing a named region and
     /// an anonymous region, emit an descriptive diagnostic error.
-    pub(super) fn try_report_named_anon_conflict(
-        &self,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    pub(super) fn try_report_named_anon_conflict(&self) -> Option<DiagnosticBuilder<'tcx>> {
         let (span, sub, sup) = self.regions()?;
 
         debug!(
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
index d98ca995d71..45affb538a3 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -8,7 +8,7 @@ use crate::infer::ValuePairs;
 use crate::infer::{SubregionOrigin, TypeTrace};
 use crate::traits::{ObligationCause, ObligationCauseCode};
 use rustc_data_structures::intern::Interned;
-use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg};
+use rustc_errors::{DiagnosticBuilder, IntoDiagnosticArg};
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::error::ExpectedFound;
@@ -57,9 +57,7 @@ where
 impl<'tcx> NiceRegionError<'_, 'tcx> {
     /// When given a `ConcreteFailure` for a function with arguments containing a named region and
     /// an anonymous region, emit a descriptive diagnostic error.
-    pub(super) fn try_report_placeholder_conflict(
-        &self,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    pub(super) fn try_report_placeholder_conflict(&self) -> Option<DiagnosticBuilder<'tcx>> {
         match &self.error {
             ///////////////////////////////////////////////////////////////////////////
             // NB. The ordering of cases in this match is very
@@ -195,7 +193,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
         sub_placeholder: Option<Region<'tcx>>,
         sup_placeholder: Option<Region<'tcx>>,
         value_pairs: &ValuePairs<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         let (expected_args, found_args, trait_def_id) = match value_pairs {
             ValuePairs::PolyTraitRefs(ExpectedFound { expected, found })
                 if expected.def_id() == found.def_id() =>
@@ -238,7 +236,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
         trait_def_id: DefId,
         expected_args: GenericArgsRef<'tcx>,
         actual_args: GenericArgsRef<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let span = cause.span();
 
         let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) =
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs
index f5b8912532b..51b65a9dd2d 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs
@@ -5,14 +5,12 @@ use crate::{
     },
 };
 use rustc_data_structures::intern::Interned;
-use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::DiagnosticBuilder;
 use rustc_middle::ty::{self, RePlaceholder, Region};
 
 impl<'tcx> NiceRegionError<'_, 'tcx> {
     /// Emitted wwhen given a `ConcreteFailure` when relating two placeholders.
-    pub(super) fn try_report_placeholder_relation(
-        &self,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    pub(super) fn try_report_placeholder_relation(&self) -> Option<DiagnosticBuilder<'tcx>> {
         match &self.error {
             Some(RegionResolutionError::ConcreteFailure(
                 SubregionOrigin::RelateRegionParamBound(span),
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 1f1c2bc20e2..73139d8e720 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -5,7 +5,7 @@ use crate::errors::{
 use crate::fluent_generated as fluent;
 use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
 use crate::infer::{self, SubregionOrigin};
-use rustc_errors::{AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{AddToDiagnostic, Diagnostic, DiagnosticBuilder};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
@@ -78,7 +78,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         origin: SubregionOrigin<'tcx>,
         sub: Region<'tcx>,
         sup: Region<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let mut err = match origin {
             infer::Subtype(box trace) => {
                 let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
@@ -350,7 +350,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         placeholder_origin: SubregionOrigin<'tcx>,
         sub: Region<'tcx>,
         sup: Region<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         // I can't think how to do better than this right now. -nikomatsakis
         debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
         match placeholder_origin {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index e092bbbfdd6..a36735b7600 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1740,9 +1740,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         sp: Span,
         mk_diag: M,
         actual_ty: Ty<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>
+    ) -> DiagnosticBuilder<'tcx>
     where
-        M: FnOnce(String) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+        M: FnOnce(String) -> DiagnosticBuilder<'tcx>,
     {
         let actual_ty = self.resolve_vars_if_possible(actual_ty);
         debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty);
@@ -1763,7 +1763,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
         err: TypeError<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err)
     }
 
@@ -1773,7 +1773,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         expected: ty::Const<'tcx>,
         actual: ty::Const<'tcx>,
         err: TypeError<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err)
     }
 }
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
index b3cfd843ace..99fb4b33f24 100644
--- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -2,7 +2,7 @@ use super::ObjectSafetyViolation;
 
 use crate::infer::InferCtxt;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{struct_span_err, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -18,7 +18,7 @@ impl<'tcx> InferCtxt<'tcx> {
         impl_item_def_id: LocalDefId,
         trait_item_def_id: DefId,
         requirement: &dyn fmt::Display,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let mut err = struct_span_err!(
             self.tcx.sess,
             error_span,
@@ -44,7 +44,7 @@ pub fn report_object_safety_error<'tcx>(
     span: Span,
     trait_def_id: DefId,
     violations: &[ObjectSafetyViolation],
-) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'tcx> {
     let trait_str = tcx.def_path_str(trait_def_id);
     let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node {
         hir::Node::Item(item) => Some(item.ident.span),
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index d58d60fc8be..6c000380e71 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -347,7 +347,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
             ) {
                 Ok(bundle) => bundle,
                 Err(e) => {
-                    early_dcx.early_error(format!("failed to load fluent bundle: {e}"));
+                    early_dcx.early_fatal(format!("failed to load fluent bundle: {e}"));
                 }
             };
 
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index bffa743218f..f230c5b172c 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -164,13 +164,13 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
 fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn {
     let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
         let err = format!("couldn't load codegen backend {path:?}: {err}");
-        early_dcx.early_error(err);
+        early_dcx.early_fatal(err);
     });
 
     let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
         .unwrap_or_else(|e| {
             let err = format!("couldn't load codegen backend: {e}");
-            early_dcx.early_error(err);
+            early_dcx.early_fatal(err);
         });
 
     // Intentionally leak the dynamic library. We can't ever unload it
@@ -271,7 +271,7 @@ fn get_codegen_sysroot(
             "failed to find a `codegen-backends` folder \
                            in the sysroot candidates:\n* {candidates}"
         );
-        early_dcx.early_error(err);
+        early_dcx.early_fatal(err);
     });
     info!("probing {} for a codegen backend", sysroot.display());
 
@@ -282,7 +282,7 @@ fn get_codegen_sysroot(
             sysroot.display(),
             e
         );
-        early_dcx.early_error(err);
+        early_dcx.early_fatal(err);
     });
 
     let mut file: Option<PathBuf> = None;
@@ -310,7 +310,7 @@ fn get_codegen_sysroot(
                 prev.display(),
                 path.display()
             );
-            early_dcx.early_error(err);
+            early_dcx.early_fatal(err);
         }
         file = Some(path.clone());
     }
@@ -319,7 +319,7 @@ fn get_codegen_sysroot(
         Some(ref s) => load_backend_from_dylib(early_dcx, s),
         None => {
             let err = format!("unsupported builtin codegen backend `{backend_name}`");
-            early_dcx.early_error(err);
+            early_dcx.early_fatal(err);
         }
     }
 }
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index 0f5d4d9476d..667fc301991 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -206,16 +206,16 @@ fixed_size_enum! {
 
 fixed_size_enum! {
     hir::CoroutineKind {
-        ( Coroutine                               )
-        ( Gen(hir::CoroutineSource::Block)        )
-        ( Gen(hir::CoroutineSource::Fn)           )
-        ( Gen(hir::CoroutineSource::Closure)      )
-        ( Async(hir::CoroutineSource::Block)      )
-        ( Async(hir::CoroutineSource::Fn)         )
-        ( Async(hir::CoroutineSource::Closure)    )
-        ( AsyncGen(hir::CoroutineSource::Block)   )
-        ( AsyncGen(hir::CoroutineSource::Fn)      )
-        ( AsyncGen(hir::CoroutineSource::Closure) )
+        ( Coroutine                                                                    )
+        ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Block)        )
+        ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Fn)           )
+        ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Closure)      )
+        ( Desugared(hir::CoroutineDesugaring::Async, hir::CoroutineSource::Block)      )
+        ( Desugared(hir::CoroutineDesugaring::Async, hir::CoroutineSource::Fn)         )
+        ( Desugared(hir::CoroutineDesugaring::Async, hir::CoroutineSource::Closure)    )
+        ( Desugared(hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Block)   )
+        ( Desugared(hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Fn)      )
+        ( Desugared(hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Closure) )
     }
 }
 
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index c49c4ee819c..d34d9160d55 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -293,19 +293,16 @@ pub fn struct_lint_level(
             },
         );
 
-        let mut err = match (level, span) {
-            (Level::Allow, span) => {
+        // Convert lint level to error level.
+        let err_level = match level {
+            Level::Allow => {
                 if has_future_breakage {
-                    if let Some(span) = span {
-                        sess.struct_span_allow(span, "")
-                    } else {
-                        sess.struct_allow("")
-                    }
+                    rustc_errors::Level::Allow
                 } else {
                     return;
                 }
             }
-            (Level::Expect(expect_id), _) => {
+            Level::Expect(expect_id) => {
                 // This case is special as we actually allow the lint itself in this context, but
                 // we can't return early like in the case for `Level::Allow` because we still
                 // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
@@ -313,23 +310,16 @@ pub fn struct_lint_level(
                 // We can also not mark the lint expectation as fulfilled here right away, as it
                 // can still be cancelled in the decorate function. All of this means that we simply
                 // create a `DiagnosticBuilder` and continue as we would for warnings.
-                sess.struct_expect("", expect_id)
+                rustc_errors::Level::Expect(expect_id)
             }
-            (Level::ForceWarn(Some(expect_id)), Some(span)) => {
-                sess.struct_span_warn_with_expectation(span, "", expect_id)
-            }
-            (Level::ForceWarn(Some(expect_id)), None) => {
-                sess.struct_warn_with_expectation("", expect_id)
-            }
-            (Level::Warn | Level::ForceWarn(None), Some(span)) => sess.struct_span_warn(span, ""),
-            (Level::Warn | Level::ForceWarn(None), None) => sess.struct_warn(""),
-            (Level::Deny | Level::Forbid, Some(span)) => {
-                let mut builder = sess.dcx().struct_err_lint("");
-                builder.set_span(span);
-                builder
-            }
-            (Level::Deny | Level::Forbid, None) => sess.dcx().struct_err_lint(""),
+            Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::Warning(Some(expect_id)),
+            Level::Warn | Level::ForceWarn(None) => rustc_errors::Level::Warning(None),
+            Level::Deny | Level::Forbid => rustc_errors::Level::Error { lint: true },
         };
+        let mut err = DiagnosticBuilder::new(sess.dcx(), err_level, "");
+        if let Some(span) = span {
+            err.set_span(span);
+        }
 
         err.set_is_lint();
 
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 41386793987..2cd2fcca9d5 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -90,10 +90,7 @@ pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
 /// This is needed in `thir::pattern::lower_inline_const`.
 pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
 
-pub fn struct_error<'tcx>(
-    tcx: TyCtxtAt<'tcx>,
-    msg: &str,
-) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
     struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
 }
 
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 1e5a7401c6f..5c425fef27e 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -17,7 +17,7 @@ use rustc_data_structures::captures::Captures;
 use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
-use rustc_hir::{self, CoroutineKind, ImplicitSelfKind};
+use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind};
 use rustc_hir::{self as hir, HirId};
 use rustc_session::Session;
 use rustc_target::abi::{FieldIdx, VariantIdx};
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 98e3a1f604e..e0c9def0379 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -148,19 +148,23 @@ impl<O> AssertKind<O> {
             DivisionByZero(_) => "attempt to divide by zero",
             RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
             ResumedAfterReturn(CoroutineKind::Coroutine) => "coroutine resumed after completion",
-            ResumedAfterReturn(CoroutineKind::Async(_)) => "`async fn` resumed after completion",
-            ResumedAfterReturn(CoroutineKind::AsyncGen(_)) => {
+            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
+                "`async fn` resumed after completion"
+            }
+            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
                 "`async gen fn` resumed after completion"
             }
-            ResumedAfterReturn(CoroutineKind::Gen(_)) => {
+            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
                 "`gen fn` should just keep returning `None` after completion"
             }
             ResumedAfterPanic(CoroutineKind::Coroutine) => "coroutine resumed after panicking",
-            ResumedAfterPanic(CoroutineKind::Async(_)) => "`async fn` resumed after panicking",
-            ResumedAfterPanic(CoroutineKind::AsyncGen(_)) => {
+            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
+                "`async fn` resumed after panicking"
+            }
+            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
                 "`async gen fn` resumed after panicking"
             }
-            ResumedAfterPanic(CoroutineKind::Gen(_)) => {
+            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
                 "`gen fn` should just keep returning `None` after panicking"
             }
 
@@ -249,17 +253,27 @@ impl<O> AssertKind<O> {
             OverflowNeg(_) => middle_assert_overflow_neg,
             DivisionByZero(_) => middle_assert_divide_by_zero,
             RemainderByZero(_) => middle_assert_remainder_by_zero,
-            ResumedAfterReturn(CoroutineKind::Async(_)) => middle_assert_async_resume_after_return,
-            ResumedAfterReturn(CoroutineKind::AsyncGen(_)) => todo!(),
-            ResumedAfterReturn(CoroutineKind::Gen(_)) => {
+            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
+                middle_assert_async_resume_after_return
+            }
+            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
+                todo!()
+            }
+            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
                 bug!("gen blocks can be resumed after they return and will keep returning `None`")
             }
             ResumedAfterReturn(CoroutineKind::Coroutine) => {
                 middle_assert_coroutine_resume_after_return
             }
-            ResumedAfterPanic(CoroutineKind::Async(_)) => middle_assert_async_resume_after_panic,
-            ResumedAfterPanic(CoroutineKind::AsyncGen(_)) => todo!(),
-            ResumedAfterPanic(CoroutineKind::Gen(_)) => middle_assert_gen_resume_after_panic,
+            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
+                middle_assert_async_resume_after_panic
+            }
+            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
+                todo!()
+            }
+            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
+                middle_assert_gen_resume_after_panic
+            }
             ResumedAfterPanic(CoroutineKind::Coroutine) => {
                 middle_assert_coroutine_resume_after_panic
             }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index b5ca700c2cd..655dde1d9c9 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -849,7 +849,10 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Returns `true` if the node pointed to by `def_id` is a coroutine for an async construct.
     pub fn coroutine_is_async(self, def_id: DefId) -> bool {
-        matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Async(_)))
+        matches!(
+            self.coroutine_kind(def_id),
+            Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _))
+        )
     }
 
     /// Returns `true` if the node pointed to by `def_id` is a general coroutine that implements `Coroutine`.
@@ -860,12 +863,18 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Returns `true` if the node pointed to by `def_id` is a coroutine for a `gen` construct.
     pub fn coroutine_is_gen(self, def_id: DefId) -> bool {
-        matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Gen(_)))
+        matches!(
+            self.coroutine_kind(def_id),
+            Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _))
+        )
     }
 
     /// Returns `true` if the node pointed to by `def_id` is a coroutine for a `async gen` construct.
     pub fn coroutine_is_async_gen(self, def_id: DefId) -> bool {
-        matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::AsyncGen(_)))
+        matches!(
+            self.coroutine_kind(def_id),
+            Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _))
+        )
     }
 
     pub fn stability(self) -> &'tcx stability::Index {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 35c135830c3..36b8d387d69 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1481,7 +1481,7 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
         other: &Self,
         opaque_def_id: LocalDefId,
         tcx: TyCtxt<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         if let Some(diag) = tcx
             .sess
             .dcx()
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 8b2b76764e6..ab2488f2e91 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -728,10 +728,16 @@ impl<'tcx> TyCtxt<'tcx> {
             DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "method",
             DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
                 match coroutine_kind {
-                    rustc_hir::CoroutineKind::Async(..) => "async closure",
-                    rustc_hir::CoroutineKind::AsyncGen(..) => "async gen closure",
-                    rustc_hir::CoroutineKind::Coroutine => "coroutine",
-                    rustc_hir::CoroutineKind::Gen(..) => "gen closure",
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => {
+                        "async closure"
+                    }
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
+                        "async gen closure"
+                    }
+                    hir::CoroutineKind::Coroutine => "coroutine",
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => {
+                        "gen closure"
+                    }
                 }
             }
             _ => def_kind.descr(def_id),
@@ -749,10 +755,10 @@ impl<'tcx> TyCtxt<'tcx> {
             DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "a",
             DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
                 match coroutine_kind {
-                    rustc_hir::CoroutineKind::Async(..) => "an",
-                    rustc_hir::CoroutineKind::AsyncGen(..) => "an",
-                    rustc_hir::CoroutineKind::Coroutine => "a",
-                    rustc_hir::CoroutineKind::Gen(..) => "a",
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, ..) => "an",
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, ..) => "an",
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, ..) => "a",
+                    hir::CoroutineKind::Coroutine => "a",
                 }
             }
             _ => def_kind.article(),
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 22d0a665489..8677cba6a7c 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -2,7 +2,7 @@ use crate::fluent_generated as fluent;
 use rustc_errors::DiagnosticArgValue;
 use rustc_errors::{
     error_code, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
-    ErrorGuaranteed, IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessage,
+    IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessage,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{self, Ty};
@@ -461,11 +461,7 @@ pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
 }
 
 impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
-    fn into_diagnostic(
-        self,
-        dcx: &'a DiagCtxt,
-        level: Level,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'_> {
         let mut diag = DiagnosticBuilder::new(
             dcx,
             level,
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index d67dc59c618..fcccdf105f6 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -54,11 +54,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
     visitor.error
 }
 
-fn create_e0004(
-    sess: &Session,
-    sp: Span,
-    error_message: String,
-) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
     struct_span_err!(sess, sp, E0004, "{}", &error_message)
 }
 
@@ -860,7 +856,7 @@ fn report_arm_reachability<'p, 'tcx>(
     for (arm, is_useful) in report.arm_usefulness.iter() {
         match is_useful {
             Usefulness::Redundant => {
-                report_unreachable_pattern(*arm.pat.data(), arm.arm_data, catchall)
+                report_unreachable_pattern(*arm.pat.data().unwrap(), arm.arm_data, catchall)
             }
             Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {}
             // The arm is reachable, but contains redundant subpatterns (from or-patterns).
@@ -869,12 +865,12 @@ fn report_arm_reachability<'p, 'tcx>(
                 // Emit lints in the order in which they occur in the file.
                 redundant_subpats.sort_unstable_by_key(|pat| pat.data());
                 for pat in redundant_subpats {
-                    report_unreachable_pattern(*pat.data(), arm.arm_data, None);
+                    report_unreachable_pattern(*pat.data().unwrap(), arm.arm_data, None);
                 }
             }
         }
         if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
-            catchall = Some(*arm.pat.data());
+            catchall = Some(*arm.pat.data().unwrap());
         }
     }
 }
diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
index 646c70eb88f..595c2ff5bf7 100644
--- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
@@ -81,17 +81,17 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
 }
 
 #[derive(Clone)]
-pub struct MaybeStorageDead {
-    always_live_locals: BitSet<Local>,
+pub struct MaybeStorageDead<'a> {
+    always_live_locals: Cow<'a, BitSet<Local>>,
 }
 
-impl MaybeStorageDead {
-    pub fn new(always_live_locals: BitSet<Local>) -> Self {
+impl<'a> MaybeStorageDead<'a> {
+    pub fn new(always_live_locals: Cow<'a, BitSet<Local>>) -> Self {
         MaybeStorageDead { always_live_locals }
     }
 }
 
-impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageDead {
+impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageDead<'a> {
     type Domain = BitSet<Local>;
 
     const NAME: &'static str = "maybe_storage_dead";
@@ -112,7 +112,7 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageDead {
     }
 }
 
-impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
+impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageDead<'a> {
     type Idx = Local;
 
     fn domain_size(&self, body: &Body<'tcx>) -> usize {
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index d7dd44af7d2..6da102dcb1c 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -59,7 +59,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::pluralize;
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::CoroutineKind;
+use rustc_hir::{CoroutineDesugaring, CoroutineKind};
 use rustc_index::bit_set::{BitMatrix, BitSet, GrowableBitSet};
 use rustc_index::{Idx, IndexVec};
 use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
@@ -254,10 +254,12 @@ impl<'tcx> TransformVisitor<'tcx> {
         let source_info = SourceInfo::outermost(body.span);
 
         let none_value = match self.coroutine_kind {
-            CoroutineKind::Async(_) => span_bug!(body.span, "`Future`s are not fused inherently"),
+            CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => {
+                span_bug!(body.span, "`Future`s are not fused inherently")
+            }
             CoroutineKind::Coroutine => span_bug!(body.span, "`Coroutine`s cannot be fused"),
             // `gen` continues return `None`
-            CoroutineKind::Gen(_) => {
+            CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
                 let option_def_id = self.tcx.require_lang_item(LangItem::Option, None);
                 Rvalue::Aggregate(
                     Box::new(AggregateKind::Adt(
@@ -271,7 +273,7 @@ impl<'tcx> TransformVisitor<'tcx> {
                 )
             }
             // `async gen` continues to return `Poll::Ready(None)`
-            CoroutineKind::AsyncGen(_) => {
+            CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _) => {
                 let ty::Adt(_poll_adt, args) = *self.old_yield_ty.kind() else { bug!() };
                 let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else { bug!() };
                 let yield_ty = args.type_at(0);
@@ -316,7 +318,7 @@ impl<'tcx> TransformVisitor<'tcx> {
         statements: &mut Vec<Statement<'tcx>>,
     ) {
         let rvalue = match self.coroutine_kind {
-            CoroutineKind::Async(_) => {
+            CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => {
                 let poll_def_id = self.tcx.require_lang_item(LangItem::Poll, None);
                 let args = self.tcx.mk_args(&[self.old_ret_ty.into()]);
                 if is_return {
@@ -345,7 +347,7 @@ impl<'tcx> TransformVisitor<'tcx> {
                     )
                 }
             }
-            CoroutineKind::Gen(_) => {
+            CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
                 let option_def_id = self.tcx.require_lang_item(LangItem::Option, None);
                 let args = self.tcx.mk_args(&[self.old_yield_ty.into()]);
                 if is_return {
@@ -374,7 +376,7 @@ impl<'tcx> TransformVisitor<'tcx> {
                     )
                 }
             }
-            CoroutineKind::AsyncGen(_) => {
+            CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _) => {
                 if is_return {
                     let ty::Adt(_poll_adt, args) = *self.old_yield_ty.kind() else { bug!() };
                     let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else { bug!() };
@@ -1426,10 +1428,11 @@ fn create_coroutine_resume_function<'tcx>(
 
     if can_return {
         let block = match coroutine_kind {
-            CoroutineKind::Async(_) | CoroutineKind::Coroutine => {
+            CoroutineKind::Desugared(CoroutineDesugaring::Async, _) | CoroutineKind::Coroutine => {
                 insert_panic_block(tcx, body, ResumedAfterReturn(coroutine_kind))
             }
-            CoroutineKind::AsyncGen(_) | CoroutineKind::Gen(_) => {
+            CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)
+            | CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
                 transform.insert_none_ret_block(body)
             }
         };
@@ -1443,7 +1446,7 @@ fn create_coroutine_resume_function<'tcx>(
     match coroutine_kind {
         // Iterator::next doesn't accept a pinned argument,
         // unlike for all other coroutine kinds.
-        CoroutineKind::Gen(_) => {}
+        CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {}
         _ => {
             make_coroutine_state_argument_pinned(tcx, body);
         }
@@ -1609,25 +1612,34 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
             }
         };
 
-        let is_async_kind = matches!(body.coroutine_kind(), Some(CoroutineKind::Async(_)));
-        let is_async_gen_kind = matches!(body.coroutine_kind(), Some(CoroutineKind::AsyncGen(_)));
-        let is_gen_kind = matches!(body.coroutine_kind(), Some(CoroutineKind::Gen(_)));
+        let is_async_kind = matches!(
+            body.coroutine_kind(),
+            Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, _))
+        );
+        let is_async_gen_kind = matches!(
+            body.coroutine_kind(),
+            Some(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _))
+        );
+        let is_gen_kind = matches!(
+            body.coroutine_kind(),
+            Some(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _))
+        );
         let new_ret_ty = match body.coroutine_kind().unwrap() {
-            CoroutineKind::Async(_) => {
+            CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => {
                 // Compute Poll<return_ty>
                 let poll_did = tcx.require_lang_item(LangItem::Poll, None);
                 let poll_adt_ref = tcx.adt_def(poll_did);
                 let poll_args = tcx.mk_args(&[old_ret_ty.into()]);
                 Ty::new_adt(tcx, poll_adt_ref, poll_args)
             }
-            CoroutineKind::Gen(_) => {
+            CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
                 // Compute Option<yield_ty>
                 let option_did = tcx.require_lang_item(LangItem::Option, None);
                 let option_adt_ref = tcx.adt_def(option_did);
                 let option_args = tcx.mk_args(&[old_yield_ty.into()]);
                 Ty::new_adt(tcx, option_adt_ref, option_args)
             }
-            CoroutineKind::AsyncGen(_) => {
+            CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _) => {
                 // The yield ty is already `Poll<Option<yield_ty>>`
                 old_yield_ty
             }
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 89e897191e8..98d4d96d0c7 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -86,6 +86,7 @@ pub mod inline;
 mod instsimplify;
 mod jump_threading;
 mod large_enums;
+mod lint;
 mod lower_intrinsics;
 mod lower_slice_len;
 mod match_branches;
diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs
new file mode 100644
index 00000000000..3940d0ddbf3
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/lint.rs
@@ -0,0 +1,119 @@
+//! This pass statically detects code which has undefined behaviour or is likely to be erroneous.
+//! It can be used to locate problems in MIR building or optimizations. It assumes that all code
+//! can be executed, so it has false positives.
+use rustc_index::bit_set::BitSet;
+use rustc_middle::mir::visit::{PlaceContext, Visitor};
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive};
+use rustc_mir_dataflow::storage::always_storage_live_locals;
+use rustc_mir_dataflow::{Analysis, ResultsCursor};
+use std::borrow::Cow;
+
+pub fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) {
+    let reachable_blocks = traversal::reachable_as_bitset(body);
+    let always_live_locals = &always_storage_live_locals(body);
+
+    let maybe_storage_live = MaybeStorageLive::new(Cow::Borrowed(always_live_locals))
+        .into_engine(tcx, body)
+        .iterate_to_fixpoint()
+        .into_results_cursor(body);
+
+    let maybe_storage_dead = MaybeStorageDead::new(Cow::Borrowed(always_live_locals))
+        .into_engine(tcx, body)
+        .iterate_to_fixpoint()
+        .into_results_cursor(body);
+
+    Lint {
+        tcx,
+        when,
+        body,
+        is_fn_like: tcx.def_kind(body.source.def_id()).is_fn_like(),
+        always_live_locals,
+        reachable_blocks,
+        maybe_storage_live,
+        maybe_storage_dead,
+    }
+    .visit_body(body);
+}
+
+struct Lint<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    when: String,
+    body: &'a Body<'tcx>,
+    is_fn_like: bool,
+    always_live_locals: &'a BitSet<Local>,
+    reachable_blocks: BitSet<BasicBlock>,
+    maybe_storage_live: ResultsCursor<'a, 'tcx, MaybeStorageLive<'a>>,
+    maybe_storage_dead: ResultsCursor<'a, 'tcx, MaybeStorageDead<'a>>,
+}
+
+impl<'a, 'tcx> Lint<'a, 'tcx> {
+    #[track_caller]
+    fn fail(&self, location: Location, msg: impl AsRef<str>) {
+        let span = self.body.source_info(location).span;
+        self.tcx.sess.dcx().span_delayed_bug(
+            span,
+            format!(
+                "broken MIR in {:?} ({}) at {:?}:\n{}",
+                self.body.source.instance,
+                self.when,
+                location,
+                msg.as_ref()
+            ),
+        );
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
+    fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
+        if self.reachable_blocks.contains(location.block) && context.is_use() {
+            self.maybe_storage_dead.seek_after_primary_effect(location);
+            if self.maybe_storage_dead.get().contains(local) {
+                self.fail(location, format!("use of local {local:?}, which has no storage here"));
+            }
+        }
+    }
+
+    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
+        match statement.kind {
+            StatementKind::StorageLive(local) => {
+                if self.reachable_blocks.contains(location.block) {
+                    self.maybe_storage_live.seek_before_primary_effect(location);
+                    if self.maybe_storage_live.get().contains(local) {
+                        self.fail(
+                            location,
+                            format!("StorageLive({local:?}) which already has storage here"),
+                        );
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        self.super_statement(statement, location);
+    }
+
+    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+        match terminator.kind {
+            TerminatorKind::Return => {
+                if self.is_fn_like && self.reachable_blocks.contains(location.block) {
+                    self.maybe_storage_live.seek_after_primary_effect(location);
+                    for local in self.maybe_storage_live.get().iter() {
+                        if !self.always_live_locals.contains(local) {
+                            self.fail(
+                                location,
+                                format!(
+                                    "local {local:?} still has storage when returning from function"
+                                ),
+                            );
+                        }
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        self.super_terminator(terminator, location);
+    }
+}
diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs
index c4eca18ff27..1da1c1920b2 100644
--- a/compiler/rustc_mir_transform/src/pass_manager.rs
+++ b/compiler/rustc_mir_transform/src/pass_manager.rs
@@ -2,7 +2,7 @@ use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 
-use crate::{validate, MirPass};
+use crate::{lint::lint_body, validate, MirPass};
 
 /// Just like `MirPass`, except it cannot mutate `Body`.
 pub trait MirLint<'tcx> {
@@ -109,6 +109,7 @@ fn run_passes_inner<'tcx>(
     phase_change: Option<MirPhase>,
     validate_each: bool,
 ) {
+    let lint = tcx.sess.opts.unstable_opts.lint_mir & !body.should_skip();
     let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir & !body.should_skip();
     let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes;
     trace!(?overridden_passes);
@@ -131,6 +132,9 @@ fn run_passes_inner<'tcx>(
             if validate {
                 validate_body(tcx, body, format!("before pass {name}"));
             }
+            if lint {
+                lint_body(tcx, body, format!("before pass {name}"));
+            }
 
             if let Some(prof_arg) = &prof_arg {
                 tcx.sess
@@ -147,6 +151,9 @@ fn run_passes_inner<'tcx>(
             if validate {
                 validate_body(tcx, body, format!("after pass {name}"));
             }
+            if lint {
+                lint_body(tcx, body, format!("after pass {name}"));
+            }
 
             body.pass_count += 1;
         }
@@ -164,6 +171,9 @@ fn run_passes_inner<'tcx>(
         if validate || new_phase == MirPhase::Runtime(RuntimePhase::Optimized) {
             validate_body(tcx, body, format!("after phase change to {}", new_phase.name()));
         }
+        if lint {
+            lint_body(tcx, body, format!("after phase change to {}", new_phase.name()));
+        }
 
         body.pass_count = 1;
     }
diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs
index f13ab5b0f1f..05a3ac3cc75 100644
--- a/compiler/rustc_mir_transform/src/ref_prop.rs
+++ b/compiler/rustc_mir_transform/src/ref_prop.rs
@@ -7,6 +7,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_mir_dataflow::impls::MaybeStorageDead;
 use rustc_mir_dataflow::storage::always_storage_live_locals;
 use rustc_mir_dataflow::Analysis;
+use std::borrow::Cow;
 
 use crate::ssa::{SsaLocals, StorageLiveLocals};
 
@@ -120,7 +121,7 @@ fn compute_replacement<'tcx>(
 
     // Compute `MaybeStorageDead` dataflow to check that we only replace when the pointee is
     // definitely live.
-    let mut maybe_dead = MaybeStorageDead::new(always_live_locals)
+    let mut maybe_dead = MaybeStorageDead::new(Cow::Owned(always_live_locals))
         .into_engine(tcx, body)
         .iterate_to_fixpoint()
         .into_results_cursor(body);
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 8ccfcb625a6..53cce9e2883 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -3,8 +3,8 @@ use std::borrow::Cow;
 use rustc_ast::token::Token;
 use rustc_ast::{Path, Visibility};
 use rustc_errors::{
-    AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
-    Level, SubdiagnosticMessage,
+    AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, IntoDiagnostic, Level,
+    SubdiagnosticMessage,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::errors::ExprParenthesesNeeded;
@@ -1043,11 +1043,7 @@ pub(crate) struct ExpectedIdentifier {
 
 impl<'a> IntoDiagnostic<'a> for ExpectedIdentifier {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &'a DiagCtxt,
-        level: Level,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a> {
         let token_descr = TokenDescription::from_token(&self.token);
 
         let mut diag = DiagnosticBuilder::new(
@@ -1107,11 +1103,7 @@ pub(crate) struct ExpectedSemi {
 
 impl<'a> IntoDiagnostic<'a> for ExpectedSemi {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &'a DiagCtxt,
-        level: Level,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a> {
         let token_descr = TokenDescription::from_token(&self.token);
 
         let mut diag = DiagnosticBuilder::new(
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index f19d420bb49..4d557e495d8 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -35,7 +35,7 @@ use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
     pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
-    DiagnosticMessage, ErrorGuaranteed, FatalError, MultiSpan, PResult,
+    DiagnosticMessage, FatalError, MultiSpan, PResult,
 };
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
@@ -245,7 +245,7 @@ impl<'a> Parser<'a> {
         &self,
         sp: S,
         m: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'a> {
         self.dcx().struct_span_err(sp, m)
     }
 
@@ -413,7 +413,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub(super) fn expected_ident_found_err(&mut self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    pub(super) fn expected_ident_found_err(&mut self) -> DiagnosticBuilder<'a> {
         self.expected_ident_found(false).unwrap_err()
     }
 
@@ -958,7 +958,7 @@ impl<'a> Parser<'a> {
 
     pub(super) fn recover_closure_body(
         &mut self,
-        mut err: DiagnosticBuilder<'a, ErrorGuaranteed>,
+        mut err: DiagnosticBuilder<'a>,
         before: token::Token,
         prev: token::Token,
         token: token::Token,
@@ -1189,7 +1189,7 @@ impl<'a> Parser<'a> {
     /// encounter a parse error when encountering the first `,`.
     pub(super) fn check_mistyped_turbofish_with_multiple_type_params(
         &mut self,
-        mut e: DiagnosticBuilder<'a, ErrorGuaranteed>,
+        mut e: DiagnosticBuilder<'a>,
         expr: &mut P<Expr>,
     ) -> PResult<'a, ()> {
         if let ExprKind::Binary(binop, _, _) = &expr.kind
@@ -1234,10 +1234,7 @@ impl<'a> Parser<'a> {
 
     /// Suggest add the missing `let` before the identifier in stmt
     /// `a: Ty = 1` -> `let a: Ty = 1`
-    pub(super) fn suggest_add_missing_let_for_stmt(
-        &mut self,
-        err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>,
-    ) {
+    pub(super) fn suggest_add_missing_let_for_stmt(&mut self, err: &mut DiagnosticBuilder<'a>) {
         if self.token == token::Colon {
             let prev_span = self.prev_token.span.shrink_to_lo();
             let snapshot = self.create_snapshot_for_diagnostic();
@@ -2320,7 +2317,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a> {
         let (span, msg) = match (&self.token.kind, self.subparser_name) {
             (&token::Eof, Some(origin)) => {
                 let sp = self.prev_token.span.shrink_to_hi();
@@ -2572,7 +2569,7 @@ impl<'a> Parser<'a> {
     pub fn recover_const_arg(
         &mut self,
         start: Span,
-        mut err: DiagnosticBuilder<'a, ErrorGuaranteed>,
+        mut err: DiagnosticBuilder<'a>,
     ) -> PResult<'a, GenericArg> {
         let is_op_or_dot = AssocOp::from_token(&self.token)
             .and_then(|op| {
@@ -2674,7 +2671,7 @@ impl<'a> Parser<'a> {
     /// Creates a dummy const argument, and reports that the expression must be enclosed in braces
     pub fn dummy_const_arg_needs_braces(
         &self,
-        mut err: DiagnosticBuilder<'a, ErrorGuaranteed>,
+        mut err: DiagnosticBuilder<'a>,
         span: Span,
     ) -> GenericArg {
         err.multipart_suggestion(
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index e653d4064c8..a6783eaf8d4 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -26,8 +26,7 @@ use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
-    AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult,
-    StashKey,
+    AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, PResult, StashKey,
 };
 use rustc_macros::Subdiagnostic;
 use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded};
@@ -1691,7 +1690,7 @@ impl<'a> Parser<'a> {
         &self,
         lifetime: Ident,
         mk_lit_char: impl FnOnce(Symbol, Span) -> L,
-        err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>,
+        err: impl FnOnce(&Self) -> DiagnosticBuilder<'a>,
     ) -> L {
         if let Some(mut diag) = self.dcx().steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar)
         {
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 0ab2adb404a..47c337ad913 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -32,7 +32,7 @@ use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::PResult;
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan};
+use rustc_errors::{Applicability, DiagnosticBuilder, FatalError, MultiSpan};
 use rustc_session::parse::ParseSess;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
@@ -908,7 +908,7 @@ impl<'a> Parser<'a> {
     fn recover_missing_braces_around_closure_body(
         &mut self,
         closure_spans: ClosureSpans,
-        mut expect_err: DiagnosticBuilder<'_, ErrorGuaranteed>,
+        mut expect_err: DiagnosticBuilder<'_>,
     ) -> PResult<'a, ()> {
         let initial_semicolon = self.token.span;
 
@@ -1490,7 +1490,7 @@ impl<'a> Parser<'a> {
 pub(crate) fn make_unclosed_delims_error(
     unmatched: UnmatchedDelim,
     sess: &ParseSess,
-) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
+) -> Option<DiagnosticBuilder<'_>> {
     // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
     // `unmatched_delims` only for error recovery in the `Parser`.
     let found_delim = unmatched.found_delim?;
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index d32582fd43d..56a3f5ce3c2 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -18,7 +18,7 @@ use rustc_ast::{
     PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
 };
 use rustc_ast_pretty::pprust;
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
+use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::{respan, Spanned};
 use rustc_span::symbol::{kw, sym, Ident};
@@ -687,7 +687,7 @@ impl<'a> Parser<'a> {
 
     fn fatal_unexpected_non_pat(
         &mut self,
-        err: DiagnosticBuilder<'a, ErrorGuaranteed>,
+        err: DiagnosticBuilder<'a>,
         expected: Option<Expected>,
     ) -> PResult<'a, P<Pat>> {
         err.cancel();
@@ -969,7 +969,7 @@ impl<'a> Parser<'a> {
         let mut fields = ThinVec::new();
         let mut etc = false;
         let mut ate_comma = true;
-        let mut delayed_err: Option<DiagnosticBuilder<'a, ErrorGuaranteed>> = None;
+        let mut delayed_err: Option<DiagnosticBuilder<'a>> = None;
         let mut first_etc_and_maybe_comma_span = None;
         let mut last_non_comma_dotdot_span = None;
 
@@ -1135,7 +1135,7 @@ impl<'a> Parser<'a> {
     fn recover_misplaced_pattern_modifiers(
         &self,
         fields: &ThinVec<PatField>,
-        err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>,
+        err: &mut DiagnosticBuilder<'a>,
     ) {
         if let Some(last) = fields.iter().last()
             && last.is_shorthand
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 1ee5a96d5dc..1ac5aba212c 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -19,7 +19,7 @@ use rustc_ast::util::classify;
 use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
 use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt};
 use rustc_ast::{StmtKind, DUMMY_NODE_ID};
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
+use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{BytePos, Span};
 
@@ -442,7 +442,7 @@ impl<'a> Parser<'a> {
     fn error_block_no_opening_brace_msg(
         &mut self,
         msg: Cow<'static, str>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'a> {
         let sp = self.token.span;
         let mut e = self.struct_span_err(sp, msg);
         let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml
index 908d00cf105..2152f9fda00 100644
--- a/compiler/rustc_pattern_analysis/Cargo.toml
+++ b/compiler/rustc_pattern_analysis/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
+derivative = "2.2.0"
 rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena", optional = true }
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index af0a7497a34..b688051ca9c 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -642,7 +642,8 @@ impl OpaqueId {
 /// `specialize_constructor` returns the list of fields corresponding to a pattern, given a
 /// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and
 /// `Fields`.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = ""), Clone(bound = ""), PartialEq(bound = ""))]
 pub enum Constructor<Cx: TypeCx> {
     /// Tuples and structs.
     Struct,
diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs
index f00e6f18617..a1c9b157666 100644
--- a/compiler/rustc_pattern_analysis/src/lib.rs
+++ b/compiler/rustc_pattern_analysis/src/lib.rs
@@ -49,7 +49,7 @@ impl<'a, T: ?Sized> Captures<'a> for T {}
 /// Context that provides type information about constructors.
 ///
 /// Most of the crate is parameterized on a type that implements this trait.
-pub trait TypeCx: Sized + Clone + fmt::Debug {
+pub trait TypeCx: Sized + fmt::Debug {
     /// The type of a pattern.
     type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy
     /// The index of an enum variant.
@@ -58,9 +58,8 @@ pub trait TypeCx: Sized + Clone + fmt::Debug {
     type StrLit: Clone + PartialEq + fmt::Debug;
     /// Extra data to store in a match arm.
     type ArmData: Copy + Clone + fmt::Debug;
-    /// Extra data to store in a pattern. `Default` needed when we create fictitious wildcard
-    /// patterns during analysis.
-    type PatData: Clone + Default;
+    /// Extra data to store in a pattern.
+    type PatData: Clone;
 
     /// FIXME(Nadrieril): `Cx` should only give us revealed types.
     fn reveal_opaque_ty(&self, ty: Self::Ty) -> Self::Ty;
@@ -86,7 +85,8 @@ pub trait TypeCx: Sized + Clone + fmt::Debug {
 }
 
 /// Context that provides information global to a match.
-#[derive(Clone)]
+#[derive(derivative::Derivative)]
+#[derivative(Clone(bound = ""), Copy(bound = ""))]
 pub struct MatchCtxt<'a, 'p, Cx: TypeCx> {
     /// The context for type information.
     pub tycx: &'a Cx,
@@ -94,18 +94,16 @@ pub struct MatchCtxt<'a, 'p, Cx: TypeCx> {
     pub wildcard_arena: &'a TypedArena<DeconstructedPat<'p, Cx>>,
 }
 
-impl<'a, 'p, Cx: TypeCx> Copy for MatchCtxt<'a, 'p, Cx> {}
-
 /// The arm of a match expression.
-#[derive(Clone, Debug)]
+#[derive(Debug)]
+#[derive(derivative::Derivative)]
+#[derivative(Clone(bound = ""), Copy(bound = ""))]
 pub struct MatchArm<'p, Cx: TypeCx> {
     pub pat: &'p DeconstructedPat<'p, Cx>,
     pub has_guard: bool,
     pub arm_data: Cx::ArmData,
 }
 
-impl<'p, Cx: TypeCx> Copy for MatchArm<'p, Cx> {}
-
 /// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are
 /// useful, and runs some lints.
 #[cfg(feature = "rustc")]
diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs
index 450a5cb0a10..2be6e8e3db3 100644
--- a/compiler/rustc_pattern_analysis/src/lints.rs
+++ b/compiler/rustc_pattern_analysis/src/lints.rs
@@ -203,7 +203,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
                 };
 
                 use rustc_errors::DecorateLint;
-                let mut err = rcx.tcx.sess.struct_span_warn(*arm.pat.data(), "");
+                let mut err = rcx.tcx.sess.struct_span_warn(*arm.pat.data().unwrap(), "");
                 err.set_primary_message(decorator.msg());
                 decorator.decorate_lint(&mut err);
                 err.emit();
@@ -253,8 +253,8 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
                 let mut suffixes: SmallVec<[_; 1]> = Default::default();
                 // Iterate on patterns that contained `overlap`.
                 for pat in column.iter() {
-                    let this_span = *pat.data();
                     let Constructor::IntRange(this_range) = pat.ctor() else { continue };
+                    let this_span = *pat.data().unwrap();
                     if this_range.is_singleton() {
                         // Don't lint when one of the ranges is a singleton.
                         continue;
diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs
index 0cc8477b7cd..9efd3e864da 100644
--- a/compiler/rustc_pattern_analysis/src/pat.rs
+++ b/compiler/rustc_pattern_analysis/src/pat.rs
@@ -26,14 +26,16 @@ pub struct DeconstructedPat<'p, Cx: TypeCx> {
     ctor: Constructor<Cx>,
     fields: &'p [DeconstructedPat<'p, Cx>],
     ty: Cx::Ty,
-    data: Cx::PatData,
+    /// Extra data to store in a pattern. `None` if the pattern is a wildcard that does not
+    /// correspond to a user-supplied pattern.
+    data: Option<Cx::PatData>,
     /// Whether removing this arm would change the behavior of the match expression.
     useful: Cell<bool>,
 }
 
 impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
-    pub fn wildcard(ty: Cx::Ty, data: Cx::PatData) -> Self {
-        Self::new(Wildcard, &[], ty, data)
+    pub fn wildcard(ty: Cx::Ty) -> Self {
+        DeconstructedPat { ctor: Wildcard, fields: &[], ty, data: None, useful: Cell::new(false) }
     }
 
     pub fn new(
@@ -42,7 +44,7 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
         ty: Cx::Ty,
         data: Cx::PatData,
     ) -> Self {
-        DeconstructedPat { ctor, fields, ty, data, useful: Cell::new(false) }
+        DeconstructedPat { ctor, fields, ty, data: Some(data), useful: Cell::new(false) }
     }
 
     pub(crate) fn is_or_pat(&self) -> bool {
@@ -63,8 +65,10 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
     pub fn ty(&self) -> Cx::Ty {
         self.ty
     }
-    pub fn data(&self) -> &Cx::PatData {
-        &self.data
+    /// Returns the extra data stored in a pattern. Returns `None` if the pattern is a wildcard that
+    /// does not correspond to a user-supplied pattern.
+    pub fn data(&self) -> Option<&Cx::PatData> {
+        self.data.as_ref()
     }
 
     pub fn iter_fields<'a>(
@@ -83,7 +87,7 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
         let wildcard_sub_tys = || {
             let tys = pcx.ctor_sub_tys(other_ctor);
             tys.iter()
-                .map(|ty| DeconstructedPat::wildcard(*ty, Cx::PatData::default()))
+                .map(|ty| DeconstructedPat::wildcard(*ty))
                 .map(|pat| pcx.mcx.wildcard_arena.alloc(pat) as &_)
                 .collect()
         };
@@ -160,7 +164,8 @@ impl<'p, Cx: TypeCx> fmt::Debug for DeconstructedPat<'p, Cx> {
 
 /// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics
 /// purposes. As such they don't use interning and can be cloned.
-#[derive(Debug, Clone)]
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = ""), Clone(bound = ""))]
 pub struct WitnessPat<Cx: TypeCx> {
     ctor: Constructor<Cx>,
     pub(crate) fields: Vec<WitnessPat<Cx>>,
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index d2cfb9a8b06..adaa2ecbe4f 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -416,7 +416,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                     ty::Tuple(fs) => {
                         ctor = Struct;
                         let mut wilds: SmallVec<[_; 2]> =
-                            fs.iter().map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect();
+                            fs.iter().map(|ty| DeconstructedPat::wildcard(ty)).collect();
                         for pat in subpatterns {
                             wilds[pat.field.index()] = self.lower_pat(&pat.pattern);
                         }
@@ -439,7 +439,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                         let pat = if let Some(pat) = pattern {
                             self.lower_pat(&pat.pattern)
                         } else {
-                            DeconstructedPat::wildcard(args.type_at(0), pat.span)
+                            DeconstructedPat::wildcard(args.type_at(0))
                         };
                         ctor = Struct;
                         fields = singleton(pat);
@@ -464,7 +464,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                                 ty
                             });
                         let mut wilds: SmallVec<[_; 2]> =
-                            tys.map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect();
+                            tys.map(|ty| DeconstructedPat::wildcard(ty)).collect();
                         for pat in subpatterns {
                             if let Some(i) = field_id_to_id[pat.field.index()] {
                                 wilds[i] = self.lower_pat(&pat.pattern);
diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs
index 6b9fbd73003..a7cd944c0f1 100644
--- a/compiler/rustc_pattern_analysis/src/usefulness.rs
+++ b/compiler/rustc_pattern_analysis/src/usefulness.rs
@@ -569,8 +569,10 @@ pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R {
 }
 
 /// Context that provides information local to a place under investigation.
-#[derive(Clone)]
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = ""), Clone(bound = ""), Copy(bound = ""))]
 pub(crate) struct PlaceCtxt<'a, 'p, Cx: TypeCx> {
+    #[derivative(Debug = "ignore")]
     pub(crate) mcx: MatchCtxt<'a, 'p, Cx>,
     /// Type of the place under investigation.
     pub(crate) ty: Cx::Ty,
@@ -596,14 +598,6 @@ impl<'a, 'p, Cx: TypeCx> PlaceCtxt<'a, 'p, Cx> {
     }
 }
 
-impl<'a, 'p, Cx: TypeCx> Copy for PlaceCtxt<'a, 'p, Cx> {}
-
-impl<'a, 'p, Cx: TypeCx> fmt::Debug for PlaceCtxt<'a, 'p, Cx> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("PlaceCtxt").field("ty", &self.ty).finish()
-    }
-}
-
 /// Serves two purposes:
 /// - in a wildcard, tracks whether the wildcard matches only valid values (i.e. is a binding `_a`)
 ///     or also invalid values (i.e. is a true `_` pattern).
@@ -670,7 +664,8 @@ impl fmt::Display for ValidityConstraint {
 // - 'a allocated by us
 // - 'p coming from the input
 // - Cx global compilation context
-#[derive(Clone)]
+#[derive(derivative::Derivative)]
+#[derivative(Clone(bound = ""))]
 struct PatStack<'a, 'p, Cx: TypeCx> {
     // Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well.
     pats: SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]>,
@@ -845,8 +840,7 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> {
         scrut_ty: Cx::Ty,
         scrut_validity: ValidityConstraint,
     ) -> Self {
-        let wild_pattern =
-            wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Default::default()));
+        let wild_pattern = wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty));
         let wildcard_row = PatStack::from_pattern(wild_pattern);
         let mut matrix = Matrix {
             rows: Vec::with_capacity(arms.len()),
@@ -1022,7 +1016,8 @@ impl<'a, 'p, Cx: TypeCx> fmt::Debug for Matrix<'a, 'p, Cx> {
 /// The final `Pair(Some(_), true)` is then the resulting witness.
 ///
 /// See the top of the file for more detailed explanations and examples.
-#[derive(Debug, Clone)]
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = ""), Clone(bound = ""))]
 struct WitnessStack<Cx: TypeCx>(Vec<WitnessPat<Cx>>);
 
 impl<Cx: TypeCx> WitnessStack<Cx> {
@@ -1069,7 +1064,8 @@ impl<Cx: TypeCx> WitnessStack<Cx> {
 ///
 /// Just as the `Matrix` starts with a single column, by the end of the algorithm, this has a single
 /// column, which contains the patterns that are missing for the match to be exhaustive.
-#[derive(Debug, Clone)]
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = ""), Clone(bound = ""))]
 struct WitnessMatrix<Cx: TypeCx>(Vec<WitnessStack<Cx>>);
 
 impl<Cx: TypeCx> WitnessMatrix<Cx> {
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 0561fd147a9..982b9ee94da 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -4,7 +4,7 @@ use crate::query::plumbing::CycleError;
 use crate::query::DepKind;
 use crate::query::{QueryContext, QueryStackFrame};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{DiagCtxt, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Level};
+use rustc_errors::{DiagCtxt, Diagnostic, DiagnosticBuilder, Level};
 use rustc_hir::def::DefKind;
 use rustc_session::Session;
 use rustc_span::Span;
@@ -559,7 +559,7 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
 pub(crate) fn report_cycle<'a>(
     sess: &'a Session,
     CycleError { usage, cycle: stack }: &CycleError,
-) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'a> {
     assert!(!stack.is_empty());
 
     let span = stack[0].query.default_span(stack[1 % stack.len()].span);
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 41638b38c74..3e29574c871 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -19,7 +19,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::sync::Lock;
 #[cfg(parallel_compiler)]
 use rustc_data_structures::{outline, sync};
-use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError, StashKey};
+use rustc_errors::{DiagnosticBuilder, FatalError, StashKey};
 use rustc_span::{Span, DUMMY_SP};
 use std::cell::Cell;
 use std::collections::hash_map::Entry;
@@ -112,7 +112,7 @@ fn handle_cycle_error<Q, Qcx>(
     query: Q,
     qcx: Qcx,
     cycle_error: &CycleError,
-    mut error: DiagnosticBuilder<'_, ErrorGuaranteed>,
+    mut error: DiagnosticBuilder<'_>,
 ) -> Q::Value
 where
     Q: QueryConfig<Qcx>,
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 542aff69e34..af0c5b56d73 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -551,7 +551,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         &mut self,
         span: Span,
         resolution_error: ResolutionError<'a>,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         match resolution_error {
             ResolutionError::GenericParamsFromOuterItem(outer_res, has_generic_params) => {
                 use errs::GenericParamsFromOuterItemLabel as Label;
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index bd4a2998e2d..4bef67be3ef 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -3693,7 +3693,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             let mut parent_err = this.r.into_struct_error(parent_err.span, parent_err.node);
 
             // overwrite all properties with the parent's error message
-            err.message = take(&mut parent_err.message);
+            err.messages = take(&mut parent_err.messages);
             err.code = take(&mut parent_err.code);
             swap(&mut err.span, &mut parent_err.span);
             err.children = take(&mut parent_err.children);
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 5a02e7f3269..e410e76abf4 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -424,7 +424,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         span: Span,
         source: PathSource<'_>,
         res: Option<Res>,
-    ) -> (DiagnosticBuilder<'tcx, ErrorGuaranteed>, Vec<ImportSuggestion>) {
+    ) -> (DiagnosticBuilder<'tcx>, Vec<ImportSuggestion>) {
         debug!(?res, ?source);
         let base_error = self.make_base_error(path, span, source, res);
 
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 75ec594eb9b..a14f3d494fb 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -37,7 +37,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{FreezeReadGuard, Lrc};
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
 use rustc_feature::BUILTIN_ATTRIBUTES;
 use rustc_hir::def::Namespace::{self, *};
@@ -704,7 +704,7 @@ struct PrivacyError<'a> {
 
 #[derive(Debug)]
 struct UseError<'a> {
-    err: DiagnosticBuilder<'a, ErrorGuaranteed>,
+    err: DiagnosticBuilder<'a>,
     /// Candidates which user could `use` to access the missing type.
     candidates: Vec<ImportSuggestion>,
     /// The `DefId` of the module to place the use-statements in.
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 0c21e4eb43e..0d731e330f3 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1594,7 +1594,7 @@ pub(super) fn build_target_config(
         |t| Ok((t, TargetWarnings::empty())),
     );
     let (target, target_warnings) = target_result.unwrap_or_else(|e| {
-        early_dcx.early_error(format!(
+        early_dcx.early_fatal(format!(
             "Error loading target specification: {e}. \
                  Run `rustc --print target-list` for a list of built-in targets"
         ))
@@ -1604,7 +1604,7 @@ pub(super) fn build_target_config(
     }
 
     if !matches!(target.pointer_width, 16 | 32 | 64) {
-        early_dcx.early_error(format!(
+        early_dcx.early_fatal(format!(
             "target specification was invalid: unrecognized target-pointer-width {}",
             target.pointer_width
         ))
@@ -1869,7 +1869,7 @@ pub fn get_cmd_lint_options(
 
     let lint_cap = matches.opt_str("cap-lints").map(|cap| {
         lint::Level::from_str(&cap)
-            .unwrap_or_else(|| early_dcx.early_error(format!("unknown lint level: `{cap}`")))
+            .unwrap_or_else(|| early_dcx.early_fatal(format!("unknown lint level: `{cap}`")))
     });
 
     (lint_opts, describe_lints, lint_cap)
@@ -1884,7 +1884,7 @@ pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Col
 
         None => ColorConfig::Auto,
 
-        Some(arg) => early_dcx.early_error(format!(
+        Some(arg) => early_dcx.early_fatal(format!(
             "argument for `--color` must be auto, \
                  always or never (instead was `{arg}`)"
         )),
@@ -1942,7 +1942,7 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json
         // won't actually be emitting any colors and anything colorized is
         // embedded in a diagnostic message anyway.
         if matches.opt_str("color").is_some() {
-            early_dcx.early_error("cannot specify the `--color` option with `--json`");
+            early_dcx.early_fatal("cannot specify the `--color` option with `--json`");
         }
 
         for sub_option in option.split(',') {
@@ -1953,7 +1953,7 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json
                 "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
                 "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
                 "future-incompat" => json_future_incompat = true,
-                s => early_dcx.early_error(format!("unknown `--json` option `{s}`")),
+                s => early_dcx.early_fatal(format!("unknown `--json` option `{s}`")),
             }
         }
     }
@@ -1993,7 +1993,7 @@ pub fn parse_error_format(
                 early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable(
                     HumanReadableErrorType::Default(color),
                 ));
-                early_dcx.early_error(format!(
+                early_dcx.early_fatal(format!(
                     "argument for `--error-format` must be `human`, `json` or \
                      `short` (instead was `{arg}`)"
                 ))
@@ -2010,7 +2010,7 @@ pub fn parse_error_format(
         // `--error-format=json`. This means that `--json` is specified we
         // should actually be emitting JSON blobs.
         _ if !matches.opt_strs("json").is_empty() => {
-            early_dcx.early_error("using `--json` requires also using `--error-format=json`");
+            early_dcx.early_fatal("using `--json` requires also using `--error-format=json`");
         }
 
         _ => {}
@@ -2022,7 +2022,7 @@ pub fn parse_error_format(
 pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Edition {
     let edition = match matches.opt_str("edition") {
         Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| {
-            early_dcx.early_error(format!(
+            early_dcx.early_fatal(format!(
                 "argument for `--edition` must be one of: \
                      {EDITION_NAME_LIST}. (instead was `{arg}`)"
             ))
@@ -2039,7 +2039,7 @@ pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches
         } else {
             format!("edition {edition} is unstable and only available with -Z unstable-options")
         };
-        early_dcx.early_error(msg)
+        early_dcx.early_fatal(msg)
     }
 
     edition
@@ -2057,7 +2057,7 @@ fn check_error_format_stability(
                 pretty: false,
                 json_rendered,
             });
-            early_dcx.early_error("`--error-format=pretty-json` is unstable");
+            early_dcx.early_fatal("`--error-format=pretty-json` is unstable");
         }
         if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) =
             error_format
@@ -2066,7 +2066,7 @@ fn check_error_format_stability(
                 pretty: false,
                 json_rendered,
             });
-            early_dcx.early_error("`--error-format=human-annotate-rs` is unstable");
+            early_dcx.early_fatal("`--error-format=human-annotate-rs` is unstable");
         }
     }
 }
@@ -2082,7 +2082,7 @@ fn parse_output_types(
             for output_type in list.split(',') {
                 let (shorthand, path) = split_out_file_name(output_type);
                 let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
-                    early_dcx.early_error(format!(
+                    early_dcx.early_fatal(format!(
                         "unknown emission type: `{shorthand}` - expected one of: {display}",
                         display = OutputType::shorthands_display(),
                     ))
@@ -2144,7 +2144,7 @@ fn should_override_cgus_and_disable_thinlto(
     }
 
     if codegen_units == Some(0) {
-        early_dcx.early_error("value for codegen units must be a positive non-zero integer");
+        early_dcx.early_fatal("value for codegen units must be a positive non-zero integer");
     }
 
     (disable_local_thinlto, codegen_units)
@@ -2204,7 +2204,7 @@ fn collect_print_requests(
                 if unstable_opts.unstable_options {
                     PrintKind::TargetSpec
                 } else {
-                    early_dcx.early_error(
+                    early_dcx.early_fatal(
                         "the `-Z unstable-options` flag must also be passed to \
                          enable the target-spec-json print option",
                     );
@@ -2214,7 +2214,7 @@ fn collect_print_requests(
                 if unstable_opts.unstable_options {
                     PrintKind::AllTargetSpecs
                 } else {
-                    early_dcx.early_error(
+                    early_dcx.early_fatal(
                         "the `-Z unstable-options` flag must also be passed to \
                          enable the all-target-specs-json print option",
                     );
@@ -2225,7 +2225,7 @@ fn collect_print_requests(
                 let prints =
                     PRINT_KINDS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>();
                 let prints = prints.join(", ");
-                early_dcx.early_error(format!(
+                early_dcx.early_fatal(format!(
                     "unknown print request `{req}`. Valid print requests are: {prints}"
                 ));
             }
@@ -2234,7 +2234,7 @@ fn collect_print_requests(
         let out = out.unwrap_or(OutFileName::Stdout);
         if let OutFileName::Real(path) = &out {
             if !printed_paths.insert(path.clone()) {
-                early_dcx.early_error(format!(
+                early_dcx.early_fatal(format!(
                     "cannot print multiple outputs to the same path: {}",
                     path.display(),
                 ));
@@ -2252,7 +2252,7 @@ pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches
         Some(target) if target.ends_with(".json") => {
             let path = Path::new(&target);
             TargetTriple::from_path(path).unwrap_or_else(|_| {
-                early_dcx.early_error(format!("target file {path:?} does not exist"))
+                early_dcx.early_fatal(format!("target file {path:?} does not exist"))
             })
         }
         Some(target) => TargetTriple::TargetTriple(target),
@@ -2291,7 +2291,7 @@ fn parse_opt_level(
             "s" => OptLevel::Size,
             "z" => OptLevel::SizeMin,
             arg => {
-                early_dcx.early_error(format!(
+                early_dcx.early_fatal(format!(
                     "optimization level needs to be \
                             between 0-3, s or z (instead was `{arg}`)"
                 ));
@@ -2321,7 +2321,7 @@ fn parse_assert_incr_state(
         Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded),
         Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded),
         Some(s) => {
-            early_dcx.early_error(format!("unexpected incremental state assertion value: {s}"))
+            early_dcx.early_fatal(format!("unexpected incremental state assertion value: {s}"))
         }
         None => None,
     }
@@ -2348,11 +2348,11 @@ fn parse_native_lib_kind(
                 } else {
                     ", the `-Z unstable-options` flag must also be passed to use it"
                 };
-                early_dcx.early_error(format!("library kind `link-arg` is unstable{why}"))
+                early_dcx.early_fatal(format!("library kind `link-arg` is unstable{why}"))
             }
             NativeLibKind::LinkArg
         }
-        _ => early_dcx.early_error(format!(
+        _ => early_dcx.early_fatal(format!(
             "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
         )),
     };
@@ -2372,7 +2372,7 @@ fn parse_native_lib_modifiers(
     for modifier in modifiers.split(',') {
         let (modifier, value) = match modifier.strip_prefix(['+', '-']) {
             Some(m) => (m, modifier.starts_with('+')),
-            None => early_dcx.early_error(
+            None => early_dcx.early_fatal(
                 "invalid linking modifier syntax, expected '+' or '-' prefix \
                  before one of: bundle, verbatim, whole-archive, as-needed",
             ),
@@ -2385,20 +2385,20 @@ fn parse_native_lib_modifiers(
                 } else {
                     ", the `-Z unstable-options` flag must also be passed to use it"
                 };
-                early_dcx.early_error(format!("linking modifier `{modifier}` is unstable{why}"))
+                early_dcx.early_fatal(format!("linking modifier `{modifier}` is unstable{why}"))
             }
         };
         let assign_modifier = |dst: &mut Option<bool>| {
             if dst.is_some() {
                 let msg = format!("multiple `{modifier}` modifiers in a single `-l` option");
-                early_dcx.early_error(msg)
+                early_dcx.early_fatal(msg)
             } else {
                 *dst = Some(value);
             }
         };
         match (modifier, &mut kind) {
             ("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle),
-            ("bundle", _) => early_dcx.early_error(
+            ("bundle", _) => early_dcx.early_fatal(
                 "linking modifier `bundle` is only compatible with `static` linking kind",
             ),
 
@@ -2407,7 +2407,7 @@ fn parse_native_lib_modifiers(
             ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
                 assign_modifier(whole_archive)
             }
-            ("whole-archive", _) => early_dcx.early_error(
+            ("whole-archive", _) => early_dcx.early_fatal(
                 "linking modifier `whole-archive` is only compatible with `static` linking kind",
             ),
 
@@ -2416,14 +2416,14 @@ fn parse_native_lib_modifiers(
                 report_unstable_modifier();
                 assign_modifier(as_needed)
             }
-            ("as-needed", _) => early_dcx.early_error(
+            ("as-needed", _) => early_dcx.early_fatal(
                 "linking modifier `as-needed` is only compatible with \
                  `dylib` and `framework` linking kinds",
             ),
 
             // Note: this error also excludes the case with empty modifier
             // string, like `modifiers = ""`.
-            _ => early_dcx.early_error(format!(
+            _ => early_dcx.early_fatal(format!(
                 "unknown linking modifier `{modifier}`, expected one \
                      of: bundle, verbatim, whole-archive, as-needed"
             )),
@@ -2457,7 +2457,7 @@ fn parse_libs(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Vec<Nati
                 Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
             };
             if name.is_empty() {
-                early_dcx.early_error("library name must not be empty");
+                early_dcx.early_fatal("library name must not be empty");
             }
             NativeLib { name, new_name, kind, verbatim }
         })
@@ -2493,7 +2493,7 @@ pub fn parse_externs(
         };
 
         if !is_ascii_ident(&name) {
-            let mut error = early_dcx.early_struct_error(format!(
+            let mut error = early_dcx.early_struct_fatal(format!(
                 "crate name `{name}` passed to `--extern` is not a valid ASCII identifier"
             ));
             let adjusted_name = name.replace('-', "_");
@@ -2555,7 +2555,7 @@ pub fn parse_externs(
         let mut force = false;
         if let Some(opts) = options {
             if !is_unstable_enabled {
-                early_dcx.early_error(
+                early_dcx.early_fatal(
                     "the `-Z unstable-options` flag must also be passed to \
                      enable `--extern` options",
                 );
@@ -2567,14 +2567,14 @@ pub fn parse_externs(
                         if let ExternLocation::ExactPaths(_) = &entry.location {
                             add_prelude = false;
                         } else {
-                            early_dcx.early_error(
+                            early_dcx.early_fatal(
                                 "the `noprelude` --extern option requires a file path",
                             );
                         }
                     }
                     "nounused" => nounused_dep = true,
                     "force" => force = true,
-                    _ => early_dcx.early_error(format!("unknown --extern option `{opt}`")),
+                    _ => early_dcx.early_fatal(format!("unknown --extern option `{opt}`")),
                 }
             }
         }
@@ -2602,7 +2602,7 @@ fn parse_remap_path_prefix(
         .into_iter()
         .map(|remap| match remap.rsplit_once('=') {
             None => {
-                early_dcx.early_error("--remap-path-prefix must contain '=' between FROM and TO")
+                early_dcx.early_fatal("--remap-path-prefix must contain '=' between FROM and TO")
             }
             Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
         })
@@ -2627,7 +2627,7 @@ fn parse_logical_env(
         if let Some((name, val)) = arg.split_once('=') {
             vars.insert(name.to_string(), val.to_string());
         } else {
-            early_dcx.early_error(format!("`--env`: specify value for variable `{arg}`"));
+            early_dcx.early_fatal(format!("`--env`: specify value for variable `{arg}`"));
         }
     }
 
@@ -2653,12 +2653,12 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     early_dcx.abort_if_error_and_set_error_format(error_format);
 
     let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| {
-        early_dcx.early_error("`--diagnostic-width` must be an positive integer");
+        early_dcx.early_fatal("`--diagnostic-width` must be an positive integer");
     });
 
     let unparsed_crate_types = matches.opt_strs("crate-type");
     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
-        .unwrap_or_else(|e| early_dcx.early_error(e));
+        .unwrap_or_else(|e| early_dcx.early_fatal(e));
 
     let mut unstable_opts = UnstableOptions::build(early_dcx, matches);
     let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
@@ -2666,7 +2666,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     check_error_format_stability(early_dcx, &unstable_opts, error_format, json_rendered);
 
     if !unstable_opts.unstable_options && json_unused_externs.is_enabled() {
-        early_dcx.early_error(
+        early_dcx.early_fatal(
             "the `-Z unstable-options` flag must also be passed to enable \
             the flag `--json=unused-externs`",
         );
@@ -2683,15 +2683,15 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     );
 
     if unstable_opts.threads == 0 {
-        early_dcx.early_error("value for threads must be a positive non-zero integer");
+        early_dcx.early_fatal("value for threads must be a positive non-zero integer");
     }
 
     let fuel = unstable_opts.fuel.is_some() || unstable_opts.print_fuel.is_some();
     if fuel && unstable_opts.threads > 1 {
-        early_dcx.early_error("optimization fuel is incompatible with multiple threads");
+        early_dcx.early_fatal("optimization fuel is incompatible with multiple threads");
     }
     if fuel && cg.incremental.is_some() {
-        early_dcx.early_error("optimization fuel is incompatible with incremental compilation");
+        early_dcx.early_fatal("optimization fuel is incompatible with incremental compilation");
     }
 
     let incremental = cg.incremental.as_ref().map(PathBuf::from);
@@ -2699,25 +2699,25 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state);
 
     if unstable_opts.profile && incremental.is_some() {
-        early_dcx.early_error("can't instrument with gcov profiling when compiling incrementally");
+        early_dcx.early_fatal("can't instrument with gcov profiling when compiling incrementally");
     }
     if unstable_opts.profile {
         match codegen_units {
             Some(1) => {}
             None => codegen_units = Some(1),
             Some(_) => early_dcx
-                .early_error("can't instrument with gcov profiling with multiple codegen units"),
+                .early_fatal("can't instrument with gcov profiling with multiple codegen units"),
         }
     }
 
     if cg.profile_generate.enabled() && cg.profile_use.is_some() {
-        early_dcx.early_error("options `-C profile-generate` and `-C profile-use` are exclusive");
+        early_dcx.early_fatal("options `-C profile-generate` and `-C profile-use` are exclusive");
     }
 
     if unstable_opts.profile_sample_use.is_some()
         && (cg.profile_generate.enabled() || cg.profile_use.is_some())
     {
-        early_dcx.early_error(
+        early_dcx.early_fatal(
             "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
         );
     }
@@ -2730,7 +2730,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
         // Unstable values:
         Some(SymbolManglingVersion::Legacy) => {
             if !unstable_opts.unstable_options {
-                early_dcx.early_error(
+                early_dcx.early_fatal(
                     "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
                 );
             }
@@ -2747,7 +2747,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
         | InstrumentCoverage::ExceptUnusedFunctions
         | InstrumentCoverage::ExceptUnusedGenerics => {
             if !unstable_opts.unstable_options {
-                early_dcx.early_error(
+                early_dcx.early_fatal(
                     "`-C instrument-coverage=branch` and `-C instrument-coverage=except-*` \
                     require `-Z unstable-options`",
                 );
@@ -2757,7 +2757,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
     if cg.instrument_coverage != InstrumentCoverage::Off {
         if cg.profile_generate.enabled() || cg.profile_use.is_some() {
-            early_dcx.early_error(
+            early_dcx.early_fatal(
                 "option `-C instrument-coverage` is not compatible with either `-C profile-use` \
                 or `-C profile-generate`",
             );
@@ -2787,7 +2787,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
         match cg.lto {
             LtoCli::No | LtoCli::Unspecified => {}
             LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => {
-                early_dcx.early_error("options `-C embed-bitcode=no` and `-C lto` are incompatible")
+                early_dcx.early_fatal("options `-C embed-bitcode=no` and `-C lto` are incompatible")
             }
         }
     }
@@ -2799,7 +2799,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
         let uses_unstable_self_contained_option =
             cg.link_self_contained.are_unstable_variants_set();
         if uses_unstable_self_contained_option {
-            early_dcx.early_error(
+            early_dcx.early_fatal(
                 "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off` are stable, \
                 the `-Z unstable-options` flag must also be passed to use the unstable values",
             );
@@ -2807,7 +2807,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
         if let Some(flavor) = cg.linker_flavor {
             if flavor.is_unstable() {
-                early_dcx.early_error(format!(
+                early_dcx.early_fatal(format!(
                     "the linker flavor `{}` is unstable, the `-Z unstable-options` \
                         flag must also be passed to use the unstable values",
                     flavor.desc()
@@ -2824,7 +2824,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
             .map(|c| c.as_str().unwrap())
             .intersperse(", ")
             .collect();
-        early_dcx.early_error(format!(
+        early_dcx.early_fatal(format!(
             "some `-C link-self-contained` components were both enabled and disabled: {names}"
         ));
     }
@@ -2871,7 +2871,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
     // query-dep-graph is required if dump-dep-graph is given #106736
     if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph {
-        early_dcx.early_error("can't dump dependency graph without `-Z query-dep-graph`");
+        early_dcx.early_fatal("can't dump dependency graph without `-Z query-dep-graph`");
     }
 
     let logical_env = parse_logical_env(early_dcx, matches);
@@ -2905,7 +2905,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     };
 
     let working_dir = std::env::current_dir().unwrap_or_else(|e| {
-        early_dcx.early_error(format!("Current directory is invalid: {e}"));
+        early_dcx.early_fatal(format!("Current directory is invalid: {e}"));
     });
 
     let remap = file_path_mapping(remap_path_prefix.clone(), &unstable_opts);
@@ -2980,7 +2980,7 @@ fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> O
         "mir" => Mir,
         "stable-mir" => StableMir,
         "mir-cfg" => MirCFG,
-        name => early_dcx.early_error(format!(
+        name => early_dcx.early_fatal(format!(
             "argument to `unpretty` must be one of `normal`, `identified`, \
                             `expanded`, `expanded,identified`, `expanded,hygiene`, \
                             `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \
@@ -3060,7 +3060,7 @@ pub mod nightly_options {
                 continue;
             }
             if opt.name != "Z" && !has_z_unstable_option {
-                early_dcx.early_error(format!(
+                early_dcx.early_fatal(format!(
                     "the `-Z unstable-options` flag must also be passed to enable \
                          the flag `{}`",
                     opt.name
@@ -3076,7 +3076,7 @@ pub mod nightly_options {
                         "the option `{}` is only accepted on the nightly compiler",
                         opt.name
                     );
-                    let _ = early_dcx.early_error_no_abort(msg);
+                    let _ = early_dcx.early_err(msg);
                 }
                 OptionStability::Stable => {}
             }
@@ -3086,7 +3086,7 @@ pub mod nightly_options {
                 .early_help("consider switching to a nightly toolchain: `rustup default nightly`");
             early_dcx.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
             early_dcx.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>");
-            early_dcx.early_error(format!(
+            early_dcx.early_fatal(format!(
                 "{} nightly option{} were parsed",
                 nightly_options_on_stable,
                 if nightly_options_on_stable > 1 { "s" } else { "" }
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 8ab31f6f382..9f7d918d98b 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -4,8 +4,7 @@ use crate::parse::ParseSess;
 use rustc_ast::token;
 use rustc_ast::util::literal::LitError;
 use rustc_errors::{
-    error_code, DiagCtxt, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, IntoDiagnostic,
-    Level, MultiSpan,
+    error_code, DiagCtxt, DiagnosticBuilder, DiagnosticMessage, IntoDiagnostic, Level, MultiSpan,
 };
 use rustc_macros::Diagnostic;
 use rustc_span::{BytePos, Span, Symbol};
@@ -18,11 +17,7 @@ pub struct FeatureGateError {
 
 impl<'a> IntoDiagnostic<'a> for FeatureGateError {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &'a DiagCtxt,
-        level: Level,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a> {
         let mut diag = DiagnosticBuilder::new(dcx, level, self.explain);
         diag.set_span(self.span);
         diag.code(error_code!(E0658));
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 06b554e8e63..10a4bdb94d4 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -337,12 +337,12 @@ fn build_options<O: Default>(
             Some((_, setter, type_desc, _)) => {
                 if !setter(&mut op, value) {
                     match value {
-                        None => early_dcx.early_error(
+                        None => early_dcx.early_fatal(
                             format!(
                                 "{outputname} option `{key}` requires {type_desc} ({prefix} {key}=<value>)"
                             ),
                         ),
-                        Some(value) => early_dcx.early_error(
+                        Some(value) => early_dcx.early_fatal(
                             format!(
                                 "incorrect value `{value}` for {outputname} option `{key}` - {type_desc} was expected"
                             ),
@@ -350,7 +350,7 @@ fn build_options<O: Default>(
                     }
                 }
             }
-            None => early_dcx.early_error(format!("unknown {outputname} option: `{key}`")),
+            None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")),
         }
     }
     return op;
@@ -1694,6 +1694,8 @@ options! {
         "link native libraries in the linker invocation (default: yes)"),
     link_only: bool = (false, parse_bool, [TRACKED],
         "link the `.rlink` file generated by `-Z no-link` (default: no)"),
+    lint_mir: bool = (false, parse_bool, [UNTRACKED],
+        "lint MIR before and after each transformation"),
     llvm_module_flag: Vec<(String, u32, String)> = (Vec::new(), parse_llvm_module_flag, [TRACKED],
         "a list of module flags to pass to LLVM (space separated)"),
     llvm_plugins: Vec<String> = (Vec::new(), parse_list, [TRACKED],
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index b33cc83f9cf..52a637b74e7 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -83,7 +83,7 @@ pub fn feature_err(
     feature: Symbol,
     span: impl Into<MultiSpan>,
     explain: impl Into<DiagnosticMessage>,
-) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'_> {
     feature_err_issue(sess, feature, span, GateIssue::Language, explain)
 }
 
@@ -98,7 +98,7 @@ pub fn feature_err_issue(
     span: impl Into<MultiSpan>,
     issue: GateIssue,
     explain: impl Into<DiagnosticMessage>,
-) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+) -> DiagnosticBuilder<'_> {
     let span = span.into();
 
     // Cancel an earlier warning for this same error, if it exists.
@@ -318,10 +318,7 @@ impl ParseSess {
     }
 
     #[track_caller]
-    pub fn create_err<'a>(
-        &'a self,
-        err: impl IntoDiagnostic<'a>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    pub fn create_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> DiagnosticBuilder<'a> {
         err.into_diagnostic(&self.dcx, Level::Error { lint: false })
     }
 
@@ -371,10 +368,7 @@ impl ParseSess {
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_err(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    pub fn struct_err(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_> {
         self.dcx.struct_err(msg)
     }
 
diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs
index 8ed50f6a14f..9b913c76998 100644
--- a/compiler/rustc_session/src/search_paths.rs
+++ b/compiler/rustc_session/src/search_paths.rs
@@ -61,7 +61,7 @@ impl SearchPath {
             (PathKind::All, path)
         };
         if path.is_empty() {
-            early_dcx.early_error("empty search path given via `-L`");
+            early_dcx.early_fatal("empty search path given via `-L`");
         }
 
         let dir = PathBuf::from(path);
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index c9f0f74b0b2..272e231d3ed 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -321,16 +321,6 @@ impl Session {
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_span_warn_with_expectation<S: Into<MultiSpan>>(
-        &self,
-        sp: S,
-        msg: impl Into<DiagnosticMessage>,
-        id: lint::LintExpectationId,
-    ) -> DiagnosticBuilder<'_, ()> {
-        self.dcx().struct_span_warn_with_expectation(sp, msg, id)
-    }
-    #[rustc_lint_diagnostics]
-    #[track_caller]
     pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -346,24 +336,6 @@ impl Session {
     }
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_warn_with_expectation(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-        id: lint::LintExpectationId,
-    ) -> DiagnosticBuilder<'_, ()> {
-        self.dcx().struct_warn_with_expectation(msg, id)
-    }
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_span_allow<S: Into<MultiSpan>>(
-        &self,
-        sp: S,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ()> {
-        self.dcx().struct_span_allow(sp, msg)
-    }
-    #[rustc_lint_diagnostics]
-    #[track_caller]
     pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
         self.dcx().struct_allow(msg)
     }
@@ -382,7 +354,7 @@ impl Session {
         &self,
         sp: S,
         msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         self.dcx().struct_span_err(sp, msg)
     }
     #[rustc_lint_diagnostics]
@@ -392,16 +364,13 @@ impl Session {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         self.dcx().struct_span_err_with_code(sp, msg, code)
     }
     // FIXME: This method should be removed (every error should have an associated error code).
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_err(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    pub fn struct_err(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_> {
         self.parse_sess.struct_err(msg)
     }
     #[track_caller]
@@ -410,7 +379,7 @@ impl Session {
         &self,
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         self.dcx().struct_err_with_code(msg, code)
     }
     #[rustc_lint_diagnostics]
@@ -491,10 +460,7 @@ impl Session {
         self.dcx().err(msg)
     }
     #[track_caller]
-    pub fn create_err<'a>(
-        &'a self,
-        err: impl IntoDiagnostic<'a>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    pub fn create_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> DiagnosticBuilder<'a> {
         self.parse_sess.create_err(err)
     }
     #[track_caller]
@@ -502,10 +468,10 @@ impl Session {
         &'a self,
         err: impl IntoDiagnostic<'a>,
         feature: Symbol,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'a> {
         let mut err = self.parse_sess.create_err(err);
         if err.code.is_none() {
-            err.code = std::option::Option::Some(error_code!(E0658));
+            err.code(error_code!(E0658));
         }
         add_feature_diagnostics(&mut err, &self.parse_sess, feature);
         err
@@ -1393,7 +1359,7 @@ pub fn build_session(
     let target_cfg = config::build_target_config(&early_dcx, &sopts, target_override, &sysroot);
     let host_triple = TargetTriple::from_triple(config::host_triple());
     let (host, target_warnings) = Target::search(&host_triple, &sysroot).unwrap_or_else(|e| {
-        early_dcx.early_error(format!("Error loading host specification: {e}"))
+        early_dcx.early_fatal(format!("Error loading host specification: {e}"))
     });
     for warning in target_warnings.warning_messages() {
         early_dcx.early_warn(warning)
@@ -1768,19 +1734,19 @@ impl EarlyDiagCtxt {
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     #[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"]
-    pub fn early_error_no_abort(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
+    pub fn early_err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
         self.dcx.struct_err(msg).emit()
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
-    pub fn early_error(&self, msg: impl Into<DiagnosticMessage>) -> ! {
+    pub fn early_fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! {
         self.dcx.struct_fatal(msg).emit()
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
-    pub fn early_struct_error(
+    pub fn early_struct_fatal(
         &self,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, FatalAbort> {
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
index 8b7b26f969c..5f505ac181c 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
@@ -41,17 +41,26 @@ impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource {
 impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
     type T = stable_mir::mir::CoroutineKind;
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
-        use rustc_hir::CoroutineKind;
+        use rustc_hir::{CoroutineDesugaring, CoroutineKind};
         match self {
-            CoroutineKind::Async(source) => {
-                stable_mir::mir::CoroutineKind::Async(source.stable(tables))
+            CoroutineKind::Desugared(CoroutineDesugaring::Async, source) => {
+                stable_mir::mir::CoroutineKind::Desugared(
+                    stable_mir::mir::CoroutineDesugaring::Async,
+                    source.stable(tables),
+                )
             }
-            CoroutineKind::Gen(source) => {
-                stable_mir::mir::CoroutineKind::Gen(source.stable(tables))
+            CoroutineKind::Desugared(CoroutineDesugaring::Gen, source) => {
+                stable_mir::mir::CoroutineKind::Desugared(
+                    stable_mir::mir::CoroutineDesugaring::Gen,
+                    source.stable(tables),
+                )
             }
             CoroutineKind::Coroutine => stable_mir::mir::CoroutineKind::Coroutine,
-            CoroutineKind::AsyncGen(source) => {
-                stable_mir::mir::CoroutineKind::AsyncGen(source.stable(tables))
+            CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, source) => {
+                stable_mir::mir::CoroutineKind::Desugared(
+                    stable_mir::mir::CoroutineDesugaring::AsyncGen,
+                    source.stable(tables),
+                )
             }
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs
index 0190d5ab4be..676b40850b1 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs
@@ -1,7 +1,7 @@
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::InferCtxt;
 use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::Node;
 use rustc_middle::ty::{self, Ty};
@@ -29,7 +29,7 @@ pub trait InferCtxtExt<'tcx> {
         found_args: Vec<ArgKind>,
         is_closure: bool,
         closure_pipe_span: Option<Span>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+    ) -> DiagnosticBuilder<'tcx>;
 
     /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
     /// in that order, and returns the generic type corresponding to the
@@ -118,7 +118,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         found_args: Vec<ArgKind>,
         is_closure: bool,
         closure_arg_span: Option<Span>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let kind = if is_closure { "closure" } else { "function" };
 
         let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index a1b896d2251..e84c5b76421 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
     error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
-    ErrorGuaranteed, MultiSpan, Style, SuggestionStyle,
+    MultiSpan, Style, SuggestionStyle,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -22,7 +22,7 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::is_range_literal;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{CoroutineKind, CoroutineSource, Node};
+use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Node};
 use rustc_hir::{Expr, HirId};
 use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -207,7 +207,7 @@ pub trait TypeErrCtxtExt<'tcx> {
 
     fn point_at_returns_when_relevant(
         &self,
-        err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+        err: &mut DiagnosticBuilder<'tcx>,
         obligation: &PredicateObligation<'tcx>,
     );
 
@@ -220,7 +220,7 @@ pub trait TypeErrCtxtExt<'tcx> {
         cause: &ObligationCauseCode<'tcx>,
         found_node: Option<Node<'_>>,
         param_env: ty::ParamEnv<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+    ) -> DiagnosticBuilder<'tcx>;
 
     fn note_conflicting_fn_args(
         &self,
@@ -234,7 +234,7 @@ pub trait TypeErrCtxtExt<'tcx> {
     fn note_conflicting_closure_bounds(
         &self,
         cause: &ObligationCauseCode<'tcx>,
-        err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+        err: &mut DiagnosticBuilder<'tcx>,
     );
 
     fn suggest_fully_qualified_path(
@@ -1384,7 +1384,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     if has_custom_message {
                         err.note(msg);
                     } else {
-                        err.message =
+                        err.messages =
                             vec![(rustc_errors::DiagnosticMessage::from(msg), Style::NoStyle)];
                     }
                     let mut file = None;
@@ -1920,7 +1920,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
     fn point_at_returns_when_relevant(
         &self,
-        err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+        err: &mut DiagnosticBuilder<'tcx>,
         obligation: &PredicateObligation<'tcx>,
     ) {
         match obligation.cause.code().peel_derives() {
@@ -1961,7 +1961,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         cause: &ObligationCauseCode<'tcx>,
         found_node: Option<Node<'_>>,
         param_env: ty::ParamEnv<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         pub(crate) fn build_fn_sig_ty<'tcx>(
             infcx: &InferCtxt<'tcx>,
             trait_ref: ty::PolyTraitRef<'tcx>,
@@ -2187,7 +2187,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     fn note_conflicting_closure_bounds(
         &self,
         cause: &ObligationCauseCode<'tcx>,
-        err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+        err: &mut DiagnosticBuilder<'tcx>,
     ) {
         // First, look for an `ExprBindingObligation`, which means we can get
         // the unsubstituted predicate list of the called function. And check
@@ -2578,7 +2578,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 .and_then(|coroutine_did| {
                     Some(match self.tcx.coroutine_kind(coroutine_did).unwrap() {
                         CoroutineKind::Coroutine => format!("coroutine is not {trait_name}"),
-                        CoroutineKind::Async(CoroutineSource::Fn) => self
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Async,
+                            CoroutineSource::Fn,
+                        ) => self
                             .tcx
                             .parent(coroutine_did)
                             .as_local()
@@ -2587,13 +2590,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             .map(|name| {
                                 format!("future returned by `{name}` is not {trait_name}")
                             })?,
-                        CoroutineKind::Async(CoroutineSource::Block) => {
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Async,
+                            CoroutineSource::Block,
+                        ) => {
                             format!("future created by async block is not {trait_name}")
                         }
-                        CoroutineKind::Async(CoroutineSource::Closure) => {
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Async,
+                            CoroutineSource::Closure,
+                        ) => {
                             format!("future created by async closure is not {trait_name}")
                         }
-                        CoroutineKind::AsyncGen(CoroutineSource::Fn) => self
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::AsyncGen,
+                            CoroutineSource::Fn,
+                        ) => self
                             .tcx
                             .parent(coroutine_did)
                             .as_local()
@@ -2602,27 +2614,40 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             .map(|name| {
                                 format!("async iterator returned by `{name}` is not {trait_name}")
                             })?,
-                        CoroutineKind::AsyncGen(CoroutineSource::Block) => {
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::AsyncGen,
+                            CoroutineSource::Block,
+                        ) => {
                             format!("async iterator created by async gen block is not {trait_name}")
                         }
-                        CoroutineKind::AsyncGen(CoroutineSource::Closure) => {
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::AsyncGen,
+                            CoroutineSource::Closure,
+                        ) => {
                             format!(
                                 "async iterator created by async gen closure is not {trait_name}"
                             )
                         }
-                        CoroutineKind::Gen(CoroutineSource::Fn) => self
-                            .tcx
-                            .parent(coroutine_did)
-                            .as_local()
-                            .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did))
-                            .and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
-                            .map(|name| {
-                                format!("iterator returned by `{name}` is not {trait_name}")
-                            })?,
-                        CoroutineKind::Gen(CoroutineSource::Block) => {
+                        CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Fn) => {
+                            self.tcx
+                                .parent(coroutine_did)
+                                .as_local()
+                                .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did))
+                                .and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
+                                .map(|name| {
+                                    format!("iterator returned by `{name}` is not {trait_name}")
+                                })?
+                        }
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Gen,
+                            CoroutineSource::Block,
+                        ) => {
                             format!("iterator created by gen block is not {trait_name}")
                         }
-                        CoroutineKind::Gen(CoroutineSource::Closure) => {
+                        CoroutineKind::Desugared(
+                            CoroutineDesugaring::Gen,
+                            CoroutineSource::Closure,
+                        ) => {
                             format!("iterator created by gen closure is not {trait_name}")
                         }
                     })
@@ -3145,9 +3170,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 let what = match self.tcx.coroutine_kind(coroutine_def_id) {
                     None
                     | Some(hir::CoroutineKind::Coroutine)
-                    | Some(hir::CoroutineKind::Gen(_)) => "yield",
-                    Some(hir::CoroutineKind::Async(..)) => "await",
-                    Some(hir::CoroutineKind::AsyncGen(_)) => "yield`/`await",
+                    | Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => {
+                        "yield"
+                    }
+                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
+                        "await"
+                    }
+                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => {
+                        "yield`/`await"
+                    }
                 };
                 err.note(format!(
                     "all values live across `{what}` must have a statically known size"
@@ -3535,7 +3566,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     ) {
         if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) {
             let body = self.tcx.hir().body(body_id);
-            if let Some(hir::CoroutineKind::Async(_)) = body.coroutine_kind {
+            if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) =
+                body.coroutine_kind
+            {
                 let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
 
                 let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 9ee091bbd1e..a495f8399b9 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -1,3 +1,5 @@
+// ignore-tidy-filelength :(
+
 use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
 use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
 use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch};
@@ -59,7 +61,7 @@ pub trait TypeErrCtxtExt<'tcx> {
         predicate: &T,
         span: Span,
         suggest_increasing_limit: bool,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>
+    ) -> DiagnosticBuilder<'tcx>
     where
         T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>;
 
@@ -118,7 +120,7 @@ pub trait TypeErrCtxtExt<'tcx> {
         &self,
         ty: Ty<'tcx>,
         obligation: &PredicateObligation<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+    ) -> DiagnosticBuilder<'tcx>;
 }
 
 impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
@@ -263,7 +265,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         predicate: &T,
         span: Span,
         suggest_increasing_limit: bool,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>
+    ) -> DiagnosticBuilder<'tcx>
     where
         T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
     {
@@ -1226,7 +1228,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         &self,
         ty: Ty<'tcx>,
         obligation: &PredicateObligation<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let span = obligation.cause.span;
 
         let mut diag = match ty.kind() {
@@ -1491,7 +1493,7 @@ pub(super) trait InferCtxtPrivExt<'tcx> {
         closure_def_id: DefId,
         found_kind: ty::ClosureKind,
         kind: ty::ClosureKind,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+    ) -> DiagnosticBuilder<'tcx>;
 
     fn report_type_parameter_mismatch_cyclic_type_error(
         &self,
@@ -1499,13 +1501,13 @@ pub(super) trait InferCtxtPrivExt<'tcx> {
         found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
         expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
         terr: TypeError<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+    ) -> DiagnosticBuilder<'tcx>;
 
     fn report_opaque_type_auto_trait_leakage(
         &self,
         obligation: &PredicateObligation<'tcx>,
         def_id: DefId,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+    ) -> DiagnosticBuilder<'tcx>;
 
     fn report_type_parameter_mismatch_error(
         &self,
@@ -1513,13 +1515,13 @@ pub(super) trait InferCtxtPrivExt<'tcx> {
         span: Span,
         found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
         expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>;
+    ) -> Option<DiagnosticBuilder<'tcx>>;
 
     fn report_not_const_evaluatable_error(
         &self,
         obligation: &PredicateObligation<'tcx>,
         span: Span,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>;
+    ) -> Option<DiagnosticBuilder<'tcx>>;
 }
 
 impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
@@ -1926,15 +1928,42 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     fn describe_coroutine(&self, body_id: hir::BodyId) -> Option<&'static str> {
         self.tcx.hir().body(body_id).coroutine_kind.map(|coroutine_source| match coroutine_source {
             hir::CoroutineKind::Coroutine => "a coroutine",
-            hir::CoroutineKind::Async(hir::CoroutineSource::Block) => "an async block",
-            hir::CoroutineKind::Async(hir::CoroutineSource::Fn) => "an async function",
-            hir::CoroutineKind::Async(hir::CoroutineSource::Closure) => "an async closure",
-            hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Block) => "an async gen block",
-            hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Fn) => "an async gen function",
-            hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Closure) => "an async gen closure",
-            hir::CoroutineKind::Gen(hir::CoroutineSource::Block) => "a gen block",
-            hir::CoroutineKind::Gen(hir::CoroutineSource::Fn) => "a gen function",
-            hir::CoroutineKind::Gen(hir::CoroutineSource::Closure) => "a gen closure",
+            hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::Async,
+                hir::CoroutineSource::Block,
+            ) => "an async block",
+            hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::Async,
+                hir::CoroutineSource::Fn,
+            ) => "an async function",
+            hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::Async,
+                hir::CoroutineSource::Closure,
+            ) => "an async closure",
+            hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::AsyncGen,
+                hir::CoroutineSource::Block,
+            ) => "an async gen block",
+            hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::AsyncGen,
+                hir::CoroutineSource::Fn,
+            ) => "an async gen function",
+            hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::AsyncGen,
+                hir::CoroutineSource::Closure,
+            ) => "an async gen closure",
+            hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::Gen,
+                hir::CoroutineSource::Block,
+            ) => "a gen block",
+            hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::Gen,
+                hir::CoroutineSource::Fn,
+            ) => "a gen function",
+            hir::CoroutineKind::Desugared(
+                hir::CoroutineDesugaring::Gen,
+                hir::CoroutineSource::Closure,
+            ) => "a gen closure",
         })
     }
 
@@ -3305,7 +3334,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         closure_def_id: DefId,
         found_kind: ty::ClosureKind,
         kind: ty::ClosureKind,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let closure_span = self.tcx.def_span(closure_def_id);
 
         let mut err = ClosureKindMismatch {
@@ -3347,7 +3376,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
         expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
         terr: TypeError<'tcx>,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let self_ty = found_trait_ref.self_ty().skip_binder();
         let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() {
             (
@@ -3367,7 +3396,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         &self,
         obligation: &PredicateObligation<'tcx>,
         def_id: DefId,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx> {
         let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
             hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
                 "opaque type".to_string()
@@ -3409,7 +3438,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         span: Span,
         found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
         expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref);
         let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref);
 
@@ -3515,7 +3544,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         &self,
         obligation: &PredicateObligation<'tcx>,
         span: Span,
-    ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+    ) -> Option<DiagnosticBuilder<'tcx>> {
         if !self.tcx.features().generic_const_exprs {
             let mut err = self
                 .tcx
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index a5f11ca23e1..86501b5a72d 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -114,13 +114,13 @@ fn fn_sig_for_fn_abi<'tcx>(
             let pin_adt_ref = tcx.adt_def(pin_did);
             let pin_args = tcx.mk_args(&[env_ty.into()]);
             let env_ty = match coroutine_kind {
-                hir::CoroutineKind::Gen(_) => {
+                hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => {
                     // Iterator::next doesn't accept a pinned argument,
                     // unlike for all other coroutine kinds.
                     env_ty
                 }
-                hir::CoroutineKind::Async(_)
-                | hir::CoroutineKind::AsyncGen(_)
+                hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
+                | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)
                 | hir::CoroutineKind::Coroutine => Ty::new_adt(tcx, pin_adt_ref, pin_args),
             };
 
@@ -131,7 +131,7 @@ fn fn_sig_for_fn_abi<'tcx>(
             // or the `Iterator::next(...) -> Option` function in case this is a
             // special coroutine backing a gen construct.
             let (resume_ty, ret_ty) = match coroutine_kind {
-                hir::CoroutineKind::Async(_) => {
+                hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => {
                     // The signature should be `Future::poll(_, &mut Context<'_>) -> Poll<Output>`
                     assert_eq!(sig.yield_ty, tcx.types.unit);
 
@@ -156,7 +156,7 @@ fn fn_sig_for_fn_abi<'tcx>(
 
                     (Some(context_mut_ref), ret_ty)
                 }
-                hir::CoroutineKind::Gen(_) => {
+                hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => {
                     // The signature should be `Iterator::next(_) -> Option<Yield>`
                     let option_did = tcx.require_lang_item(LangItem::Option, None);
                     let option_adt_ref = tcx.adt_def(option_did);
@@ -168,7 +168,7 @@ fn fn_sig_for_fn_abi<'tcx>(
 
                     (None, ret_ty)
                 }
-                hir::CoroutineKind::AsyncGen(_) => {
+                hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
                     // The signature should be
                     // `AsyncIterator::poll_next(_, &mut Context<'_>) -> Poll<Option<Output>>`
                     assert_eq!(sig.return_ty, tcx.types.unit);
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index b8fd9370aa6..89d75569ce3 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -288,27 +288,33 @@ impl AssertMessage {
             AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine) => {
                 Ok("coroutine resumed after completion")
             }
-            AssertMessage::ResumedAfterReturn(CoroutineKind::Async(_)) => {
-                Ok("`async fn` resumed after completion")
-            }
-            AssertMessage::ResumedAfterReturn(CoroutineKind::Gen(_)) => {
-                Ok("`async gen fn` resumed after completion")
-            }
-            AssertMessage::ResumedAfterReturn(CoroutineKind::AsyncGen(_)) => {
-                Ok("`gen fn` should just keep returning `AssertMessage::None` after completion")
-            }
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared(
+                CoroutineDesugaring::Async,
+                _,
+            )) => Ok("`async fn` resumed after completion"),
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared(
+                CoroutineDesugaring::Gen,
+                _,
+            )) => Ok("`async gen fn` resumed after completion"),
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared(
+                CoroutineDesugaring::AsyncGen,
+                _,
+            )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after completion"),
             AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine) => {
                 Ok("coroutine resumed after panicking")
             }
-            AssertMessage::ResumedAfterPanic(CoroutineKind::Async(_)) => {
-                Ok("`async fn` resumed after panicking")
-            }
-            AssertMessage::ResumedAfterPanic(CoroutineKind::Gen(_)) => {
-                Ok("`async gen fn` resumed after panicking")
-            }
-            AssertMessage::ResumedAfterPanic(CoroutineKind::AsyncGen(_)) => {
-                Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking")
-            }
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared(
+                CoroutineDesugaring::Async,
+                _,
+            )) => Ok("`async fn` resumed after panicking"),
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared(
+                CoroutineDesugaring::Gen,
+                _,
+            )) => Ok("`async gen fn` resumed after panicking"),
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared(
+                CoroutineDesugaring::AsyncGen,
+                _,
+            )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"),
 
             AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"),
             AssertMessage::MisalignedPointerDereference { .. } => {
@@ -392,10 +398,8 @@ pub enum UnOp {
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum CoroutineKind {
-    Async(CoroutineSource),
+    Desugared(CoroutineDesugaring, CoroutineSource),
     Coroutine,
-    Gen(CoroutineSource),
-    AsyncGen(CoroutineSource),
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -405,6 +409,15 @@ pub enum CoroutineSource {
     Fn,
 }
 
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum CoroutineDesugaring {
+    Async,
+
+    Gen,
+
+    AsyncGen,
+}
+
 pub(crate) type LocalDefId = Opaque;
 /// The rustc coverage data structures are heavily tied to internal details of the
 /// coverage implementation that are likely to change, and are unlikely to be