about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/visit.rs90
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs7
-rw-r--r--compiler/rustc_attr_data_structures/src/encode_cross_crate.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/test_attrs.rs46
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs3
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs16
-rw-r--r--compiler/rustc_borrowck/src/nll.rs4
-rw-r--r--compiler/rustc_borrowck/src/polonius/dump.rs17
-rw-r--r--compiler/rustc_borrowck/src/region_infer/graphviz.rs34
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs5
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs8
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs92
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs128
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs305
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs5
-rw-r--r--compiler/rustc_expand/src/base.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs10
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs140
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs66
-rw-r--r--compiler/rustc_hir/src/hir.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs205
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs28
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs15
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs23
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/mod.rs2
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs29
-rw-r--r--compiler/rustc_lint/src/map_unit_fn.rs5
-rw-r--r--compiler/rustc_middle/src/middle/region.rs92
-rw-r--r--compiler/rustc_middle/src/ty/context.rs5
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs105
-rw-r--r--compiler/rustc_middle/src/ty/region.rs112
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs26
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs4
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/canonicalizer.rs152
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs5
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs8
-rw-r--r--compiler/rustc_resolve/src/lib.rs6
-rw-r--r--compiler/rustc_resolve/src/macros.rs30
-rw-r--r--compiler/rustc_smir/src/stable_mir/unstable/convert/internal.rs15
-rw-r--r--compiler/rustc_smir/src/stable_mir/unstable/convert/stable/ty.rs18
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs18
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs21
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs5
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs11
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/region.rs48
-rw-r--r--compiler/rustc_trait_selection/src/errors/note_and_explain.rs7
59 files changed, 920 insertions, 1107 deletions
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 5dd6882b025..f8ecff69a76 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -1202,9 +1202,10 @@ macro_rules! common_visitor_and_walkers {
             let TyPat { id, kind, span, tokens: _ } = tp;
             try_visit!(visit_id(vis, id));
             match kind {
-                TyPatKind::Range(start, end, _include_end) => {
+                TyPatKind::Range(start, end, Spanned { span, node: _include_end }) => {
                     visit_opt!(vis, visit_anon_const, start);
                     visit_opt!(vis, visit_anon_const, end);
+                    try_visit!(visit_span(vis, span));
                 }
                 TyPatKind::Or(variants) => walk_list!(vis, visit_ty_pat, variants),
                 TyPatKind::Err(_) => {}
@@ -1523,16 +1524,26 @@ macro_rules! common_visitor_and_walkers {
         }
 
         pub fn walk_inline_asm<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, asm: &$($lt)? $($mut)? InlineAsm) -> V::Result {
-            // FIXME: Visit spans inside all this currently ignored stuff.
             let InlineAsm {
                 asm_macro: _,
-                template: _,
-                template_strs: _,
+                template,
+                template_strs,
                 operands,
-                clobber_abis: _,
+                clobber_abis,
                 options: _,
-                line_spans: _,
+                line_spans,
             } = asm;
+            for piece in template {
+                match piece {
+                    InlineAsmTemplatePiece::String(_str) => {}
+                    InlineAsmTemplatePiece::Placeholder { operand_idx: _, modifier: _, span } => {
+                        try_visit!(visit_span(vis, span));
+                    }
+                }
+            }
+            for (_s1, _s2, span) in template_strs {
+                try_visit!(visit_span(vis, span));
+            }
             for (op, span) in operands {
                 match op {
                     InlineAsmOperand::In { expr, reg: _ }
@@ -1553,6 +1564,12 @@ macro_rules! common_visitor_and_walkers {
                 }
                 try_visit!(visit_span(vis, span));
             }
+            for (_s1, span) in clobber_abis {
+                try_visit!(visit_span(vis, span))
+            }
+            for span in line_spans {
+                try_visit!(visit_span(vis, span))
+            }
             V::Result::output()
         }
 
@@ -1565,9 +1582,9 @@ macro_rules! common_visitor_and_walkers {
             vis.visit_path(path)
         }
 
-        // FIXME: visit the template exhaustively.
         pub fn walk_format_args<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, fmt: &$($lt)? $($mut)? FormatArgs) -> V::Result {
-            let FormatArgs { span, template: _, arguments, uncooked_fmt_str: _, is_source_literal: _ } = fmt;
+            let FormatArgs { span, template, arguments, uncooked_fmt_str: _, is_source_literal: _ } = fmt;
+
             let args = $(${ignore($mut)} arguments.all_args_mut())? $(${ignore($lt)} arguments.all_args())? ;
             for FormatArgument { kind, expr } in args {
                 match kind {
@@ -1578,9 +1595,58 @@ macro_rules! common_visitor_and_walkers {
                 }
                 try_visit!(vis.visit_expr(expr));
             }
+            for piece in template {
+                match piece {
+                    FormatArgsPiece::Literal(_symbol) => {}
+                    FormatArgsPiece::Placeholder(placeholder) => try_visit!(walk_format_placeholder(vis, placeholder)),
+                }
+            }
             visit_span(vis, span)
         }
 
+        fn walk_format_placeholder<$($lt,)? V: $Visitor$(<$lt>)?>(
+            vis: &mut V,
+            placeholder: &$($lt)? $($mut)? FormatPlaceholder,
+        ) -> V::Result {
+            let FormatPlaceholder { argument, span, format_options, format_trait: _ } = placeholder;
+            if let Some(span) = span {
+                try_visit!(visit_span(vis, span));
+            }
+            let FormatArgPosition { span, index: _, kind: _ } = argument;
+            if let Some(span) = span {
+                try_visit!(visit_span(vis, span));
+            }
+            let FormatOptions {
+                width,
+                precision,
+                alignment: _,
+                fill: _,
+                sign: _,
+                alternate: _,
+                zero_pad: _,
+                debug_hex: _,
+            } = format_options;
+            match width {
+                None => {}
+                Some(FormatCount::Literal(_)) => {}
+                Some(FormatCount::Argument(FormatArgPosition { span, index: _, kind: _ })) => {
+                    if let Some(span) = span {
+                        try_visit!(visit_span(vis, span));
+                    }
+                }
+            }
+            match precision {
+                None => {}
+                Some(FormatCount::Literal(_)) => {}
+                Some(FormatCount::Argument(FormatArgPosition { span, index: _, kind: _ })) => {
+                    if let Some(span) = span {
+                        try_visit!(visit_span(vis, span));
+                    }
+                }
+            }
+            V::Result::output()
+        }
+
         pub fn walk_expr<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, expression: &$($lt)? $($mut)? Expr) -> V::Result {
             let Expr { id, kind, span, attrs, tokens: _ } = expression;
             try_visit!(visit_id(vis, id));
@@ -1601,7 +1667,7 @@ macro_rules! common_visitor_and_walkers {
                     try_visit!(visit_expr_fields(vis, fields));
                     match rest {
                         StructRest::Base(expr) => try_visit!(vis.visit_expr(expr)),
-                        StructRest::Rest(_span) => {}
+                        StructRest::Rest(span) => try_visit!(visit_span(vis, span)),
                         StructRest::None => {}
                     }
                 }
@@ -1688,7 +1754,8 @@ macro_rules! common_visitor_and_walkers {
                     visit_opt!(vis, visit_label, opt_label);
                     try_visit!(vis.visit_block(block));
                 }
-                ExprKind::Gen(_capt, body, _kind, decl_span) => {
+                ExprKind::Gen(capture_clause, body, _kind, decl_span) => {
+                    try_visit!(vis.visit_capture_by(capture_clause));
                     try_visit!(vis.visit_block(body));
                     try_visit!(visit_span(vis, decl_span));
                 }
@@ -1705,9 +1772,10 @@ macro_rules! common_visitor_and_walkers {
                     try_visit!(vis.visit_expr(rhs));
                     try_visit!(visit_span(vis, span));
                 }
-                ExprKind::AssignOp(_op, left_expression, right_expression) => {
+                ExprKind::AssignOp(Spanned { span, node: _ }, left_expression, right_expression) => {
                     try_visit!(vis.visit_expr(left_expression));
                     try_visit!(vis.visit_expr(right_expression));
+                    try_visit!(visit_span(vis, span));
                 }
                 ExprKind::Field(subexpression, ident) => {
                     try_visit!(vis.visit_expr(subexpression));
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index b5934f4e36e..6af15da7d08 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -250,6 +250,13 @@ pub enum AttributeKind {
         span: Span,
     },
 
+    /// Represents `#[ignore]`
+    Ignore {
+        span: Span,
+        /// ignore can optionally have a reason: `#[ignore = "reason this is ignored"]`
+        reason: Option<Symbol>,
+    },
+
     /// Represents `#[inline]` and `#[rustc_force_inline]`.
     Inline(InlineAttr, Span),
 
diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
index 02e95ddcb6f..8ebd38a6ba7 100644
--- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
+++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
@@ -26,6 +26,7 @@ impl AttributeKind {
             Deprecation { .. } => Yes,
             DocComment { .. } => Yes,
             ExportName { .. } => Yes,
+            Ignore { .. } => No,
             Inline(..) => No,
             LinkName { .. } => Yes,
             LinkSection { .. } => No,
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index f5ac3890a46..55fbb825466 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -41,6 +41,7 @@ pub(crate) mod repr;
 pub(crate) mod rustc_internal;
 pub(crate) mod semantics;
 pub(crate) mod stability;
+pub(crate) mod test_attrs;
 pub(crate) mod traits;
 pub(crate) mod transparency;
 pub(crate) mod util;
diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
new file mode 100644
index 00000000000..cea3ee52ff4
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
@@ -0,0 +1,46 @@
+use rustc_attr_data_structures::AttributeKind;
+use rustc_attr_data_structures::lints::AttributeLintKind;
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Symbol, sym};
+
+use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
+pub(crate) struct IgnoreParser;
+
+impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
+    const PATH: &[Symbol] = &[sym::ignore];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason");
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        Some(AttributeKind::Ignore {
+            span: cx.attr_span,
+            reason: match args {
+                ArgParser::NoArgs => None,
+                ArgParser::NameValue(name_value) => {
+                    let Some(str_value) = name_value.value_as_str() else {
+                        let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
+                            .suggestions(false, "ignore");
+                        let span = cx.attr_span;
+                        cx.emit_lint(
+                            AttributeLintKind::IllFormedAttributeInput { suggestions },
+                            span,
+                        );
+                        return None;
+                    };
+                    Some(str_value)
+                }
+                ArgParser::List(_) => {
+                    let suggestions =
+                        <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "ignore");
+                    let span = cx.attr_span;
+                    cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
+                    return None;
+                }
+            },
+        })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 265e1bb6a8c..2a01ee58493 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -37,6 +37,7 @@ use crate::attributes::semantics::MayDangleParser;
 use crate::attributes::stability::{
     BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
 };
+use crate::attributes::test_attrs::IgnoreParser;
 use crate::attributes::traits::SkipDuringMethodDispatchParser;
 use crate::attributes::transparency::TransparencyParser;
 use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
@@ -126,6 +127,7 @@ attribute_parsers!(
         // tidy-alphabetical-start
         Single<DeprecationParser>,
         Single<ExportNameParser>,
+        Single<IgnoreParser>,
         Single<InlineParser>,
         Single<LinkNameParser>,
         Single<LinkSectionParser>,
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index d1dac1c7145..040a0607db5 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -4194,7 +4194,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         //    anything.
         let return_ty = sig.output();
         match return_ty.skip_binder().kind() {
-            ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
+            ty::Ref(return_region, _, _)
+                if return_region.is_named(self.infcx.tcx) && !is_closure =>
+            {
                 // This is case 1 from above, return type is a named reference so we need to
                 // search for relevant arguments.
                 let mut arguments = Vec::new();
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index a611557dc92..b130cf8ed27 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -852,7 +852,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 return;
             };
 
-            let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime };
+            let lifetime =
+                if f.is_named(self.infcx.tcx) { fr_name.name } else { kw::UnderscoreLifetime };
 
             let arg = match param.param.pat.simple_ident() {
                 Some(simple_ident) => format!("argument `{simple_ident}`"),
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 1ad629ad167..edd14d155f6 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -289,7 +289,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
 
         debug!("give_region_a_name: error_region = {:?}", error_region);
         match error_region.kind() {
-            ty::ReEarlyParam(ebr) => ebr.has_name().then(|| {
+            ty::ReEarlyParam(ebr) => ebr.is_named().then(|| {
                 let def_id = tcx.generics_of(self.mir_def_id()).region_param(ebr, tcx).def_id;
                 let span = tcx.hir_span_if_local(def_id).unwrap_or(DUMMY_SP);
                 RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyParamRegion(span) }
@@ -300,16 +300,11 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             }
 
             ty::ReLateParam(late_param) => match late_param.kind {
-                ty::LateParamRegionKind::Named(region_def_id, name) => {
+                ty::LateParamRegionKind::Named(region_def_id) => {
                     // Get the span to point to, even if we don't use the name.
                     let span = tcx.hir_span_if_local(region_def_id).unwrap_or(DUMMY_SP);
-                    debug!(
-                        "bound region named: {:?}, is_named: {:?}",
-                        name,
-                        late_param.kind.is_named()
-                    );
 
-                    if late_param.kind.is_named() {
+                    if let Some(name) = late_param.kind.get_name(tcx) {
                         // A named region that is actually named.
                         Some(RegionName {
                             name,
@@ -369,6 +364,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                 }
 
                 ty::LateParamRegionKind::Anon(_) => None,
+                ty::LateParamRegionKind::NamedAnon(_, _) => bug!("only used for pretty printing"),
             },
 
             ty::ReBound(..)
@@ -899,7 +895,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else {
             return None;
         };
-        if region.has_name() {
+        if region.is_named() {
             return None;
         };
 
@@ -934,7 +930,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else {
             return None;
         };
-        if region.has_name() {
+        if region.is_named() {
             return None;
         };
 
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 1b011d73385..af450507296 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -232,13 +232,13 @@ pub(super) fn dump_nll_mir<'tcx>(
     // Also dump the region constraint graph as a graphviz file.
     let _: io::Result<()> = try {
         let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?;
-        regioncx.dump_graphviz_raw_constraints(&mut file)?;
+        regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?;
     };
 
     // Also dump the region constraint SCC graph as a graphviz file.
     let _: io::Result<()> = try {
         let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
-        regioncx.dump_graphviz_scc_constraints(&mut file)?;
+        regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?;
     };
 }
 
diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs
index 6a943e19208..6b13b5ad081 100644
--- a/compiler/rustc_borrowck/src/polonius/dump.rs
+++ b/compiler/rustc_borrowck/src/polonius/dump.rs
@@ -116,7 +116,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "<div>")?;
     writeln!(out, "NLL regions")?;
     writeln!(out, "<pre class='mermaid'>")?;
-    emit_mermaid_nll_regions(regioncx, out)?;
+    emit_mermaid_nll_regions(tcx, regioncx, out)?;
     writeln!(out, "</pre>")?;
     writeln!(out, "</div>")?;
 
@@ -124,7 +124,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "<div>")?;
     writeln!(out, "NLL SCCs")?;
     writeln!(out, "<pre class='mermaid'>")?;
-    emit_mermaid_nll_sccs(regioncx, out)?;
+    emit_mermaid_nll_sccs(tcx, regioncx, out)?;
     writeln!(out, "</pre>")?;
     writeln!(out, "</div>")?;
 
@@ -306,9 +306,10 @@ fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()>
 }
 
 /// Emits a region's label: index, universe, external name.
-fn render_region(
+fn render_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
     region: RegionVid,
-    regioncx: &RegionInferenceContext<'_>,
+    regioncx: &RegionInferenceContext<'tcx>,
     out: &mut dyn io::Write,
 ) -> io::Result<()> {
     let def = regioncx.region_definition(region);
@@ -318,7 +319,7 @@ fn render_region(
     if !universe.is_root() {
         write!(out, "/{universe:?}")?;
     }
-    if let Some(name) = def.external_name.and_then(|e| e.get_name()) {
+    if let Some(name) = def.external_name.and_then(|e| e.get_name(tcx)) {
         write!(out, " ({name})")?;
     }
     Ok(())
@@ -327,6 +328,7 @@ fn render_region(
 /// Emits a mermaid flowchart of the NLL regions and the outlives constraints between them, similar
 /// to the graphviz version.
 fn emit_mermaid_nll_regions<'tcx>(
+    tcx: TyCtxt<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     out: &mut dyn io::Write,
 ) -> io::Result<()> {
@@ -336,7 +338,7 @@ fn emit_mermaid_nll_regions<'tcx>(
     // Emit the region nodes.
     for region in regioncx.definitions.indices() {
         write!(out, "{}[\"", region.as_usize())?;
-        render_region(region, regioncx, out)?;
+        render_region(tcx, region, regioncx, out)?;
         writeln!(out, "\"]")?;
     }
 
@@ -378,6 +380,7 @@ fn emit_mermaid_nll_regions<'tcx>(
 /// Emits a mermaid flowchart of the NLL SCCs and the outlives constraints between them, similar
 /// to the graphviz version.
 fn emit_mermaid_nll_sccs<'tcx>(
+    tcx: TyCtxt<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     out: &mut dyn io::Write,
 ) -> io::Result<()> {
@@ -395,7 +398,7 @@ fn emit_mermaid_nll_sccs<'tcx>(
         // The node label: the regions contained in the SCC.
         write!(out, "{scc}[\"SCC({scc}) = {{", scc = scc.as_usize())?;
         for (idx, &region) in regions.iter().enumerate() {
-            render_region(region, regioncx, out)?;
+            render_region(tcx, region, regioncx, out)?;
             if idx < regions.len() - 1 {
                 write!(out, ",")?;
             }
diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs
index 1936752b63c..a3e29982e90 100644
--- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs
+++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs
@@ -26,11 +26,15 @@ fn render_universe(u: UniverseIndex) -> String {
     format!("/{:?}", u)
 }
 
-fn render_region_vid(rvid: RegionVid, regioncx: &RegionInferenceContext<'_>) -> String {
+fn render_region_vid<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    rvid: RegionVid,
+    regioncx: &RegionInferenceContext<'tcx>,
+) -> String {
     let universe_str = render_universe(regioncx.region_definition(rvid).universe);
 
     let external_name_str = if let Some(external_name) =
-        regioncx.region_definition(rvid).external_name.and_then(|e| e.get_name())
+        regioncx.region_definition(rvid).external_name.and_then(|e| e.get_name(tcx))
     {
         format!(" ({external_name})")
     } else {
@@ -42,12 +46,20 @@ fn render_region_vid(rvid: RegionVid, regioncx: &RegionInferenceContext<'_>) ->
 
 impl<'tcx> RegionInferenceContext<'tcx> {
     /// Write out the region constraint graph.
-    pub(crate) fn dump_graphviz_raw_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> {
-        dot::render(&RawConstraints { regioncx: self }, &mut w)
+    pub(crate) fn dump_graphviz_raw_constraints(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        mut w: &mut dyn Write,
+    ) -> io::Result<()> {
+        dot::render(&RawConstraints { tcx, regioncx: self }, &mut w)
     }
 
     /// Write out the region constraint SCC graph.
-    pub(crate) fn dump_graphviz_scc_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> {
+    pub(crate) fn dump_graphviz_scc_constraints(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        mut w: &mut dyn Write,
+    ) -> io::Result<()> {
         let mut nodes_per_scc: IndexVec<ConstraintSccIndex, _> =
             self.constraint_sccs.all_sccs().map(|_| Vec::new()).collect();
 
@@ -56,11 +68,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             nodes_per_scc[scc].push(region);
         }
 
-        dot::render(&SccConstraints { regioncx: self, nodes_per_scc }, &mut w)
+        dot::render(&SccConstraints { tcx, regioncx: self, nodes_per_scc }, &mut w)
     }
 }
 
 struct RawConstraints<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
     regioncx: &'a RegionInferenceContext<'tcx>,
 }
 
@@ -78,7 +91,7 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> {
         Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
     }
     fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
-        dot::LabelText::LabelStr(render_region_vid(*n, self.regioncx).into())
+        dot::LabelText::LabelStr(render_region_vid(self.tcx, *n, self.regioncx).into())
     }
     fn edge_label(&'this self, e: &OutlivesConstraint<'tcx>) -> dot::LabelText<'this> {
         dot::LabelText::LabelStr(render_outlives_constraint(e).into())
@@ -110,6 +123,7 @@ impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> {
 }
 
 struct SccConstraints<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
     regioncx: &'a RegionInferenceContext<'tcx>,
     nodes_per_scc: IndexVec<ConstraintSccIndex, Vec<RegionVid>>,
 }
@@ -128,8 +142,10 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> {
         Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
     }
     fn node_label(&'this self, n: &ConstraintSccIndex) -> dot::LabelText<'this> {
-        let nodes_str =
-            self.nodes_per_scc[*n].iter().map(|n| render_region_vid(*n, self.regioncx)).join(", ");
+        let nodes_str = self.nodes_per_scc[*n]
+            .iter()
+            .map(|n| render_region_vid(self.tcx, *n, self.regioncx))
+            .join(", ");
         dot::LabelText::LabelStr(format!("SCC({n}) = {{{nodes_str}}}", n = n.as_usize()).into())
     }
 }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 05bcd9f862e..f877e5eaadb 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -786,8 +786,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     let region_ctxt_fn = || {
                         let reg_info = match br.kind {
                             ty::BoundRegionKind::Anon => sym::anon,
-                            ty::BoundRegionKind::Named(_, name) => name,
+                            ty::BoundRegionKind::Named(def_id) => tcx.item_name(def_id),
                             ty::BoundRegionKind::ClosureEnv => sym::env,
+                            ty::BoundRegionKind::NamedAnon(_) => {
+                                bug!("only used for pretty printing")
+                            }
                         };
 
                         RegionCtxt::LateBound(reg_info)
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index 02a41469c97..e023300f1c2 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -7,11 +7,11 @@ use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
 use rustc_infer::traits::Obligation;
 use rustc_infer::traits::solve::Goal;
 use rustc_middle::mir::ConstraintCategory;
-use rustc_middle::span_bug;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
 use rustc_middle::ty::{self, FnMutDelegate, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::{bug, span_bug};
 use rustc_span::{Span, Symbol, sym};
 use tracing::{debug, instrument};
 
@@ -215,7 +215,8 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
                 if let Some(ex_reg_var) = reg_map.get(&br) {
                     *ex_reg_var
                 } else {
-                    let ex_reg_var = self.next_existential_region_var(true, br.kind.get_name());
+                    let ex_reg_var =
+                        self.next_existential_region_var(true, br.kind.get_name(infcx.infcx.tcx));
                     debug!(?ex_reg_var);
                     reg_map.insert(br, ex_reg_var);
 
@@ -263,8 +264,9 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
 
         let reg_info = match placeholder.bound.kind {
             ty::BoundRegionKind::Anon => sym::anon,
-            ty::BoundRegionKind::Named(_, name) => name,
+            ty::BoundRegionKind::Named(def_id) => self.type_checker.tcx().item_name(def_id),
             ty::BoundRegionKind::ClosureEnv => sym::env,
+            ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
         };
 
         if cfg!(debug_assertions) {
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index 846299711be..f138f265320 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -497,7 +497,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
                 |r| {
                     debug!(?r);
                     let region_vid = {
-                        let name = r.get_name_or_anon();
+                        let name = r.get_name_or_anon(self.infcx.tcx);
                         self.infcx.next_nll_region_var(FR, || RegionCtxt::LateBound(name))
                     };
 
@@ -523,7 +523,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
                 let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind);
                 let r = ty::Region::new_late_param(self.infcx.tcx, self.mir_def.to_def_id(), kind);
                 let region_vid = {
-                    let name = r.get_name_or_anon();
+                    let name = r.get_name_or_anon(self.infcx.tcx);
                     self.infcx.next_nll_region_var(FR, || RegionCtxt::LateBound(name))
                 };
 
@@ -861,7 +861,7 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
         T: TypeFoldable<TyCtxt<'tcx>>,
     {
         fold_regions(self.infcx.tcx, value, |region, _depth| {
-            let name = region.get_name_or_anon();
+            let name = region.get_name_or_anon(self.infcx.tcx);
             debug!(?region, ?name);
 
             self.next_nll_region_var(origin, || RegionCtxt::Free(name))
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index da615cc9a00..2896dfd5463 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -1,5 +1,6 @@
 use std::fmt;
 
+use itertools::Either;
 use rustc_abi as abi;
 use rustc_abi::{
     Align, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, TagEncoding, VariantIdx, Variants,
@@ -13,7 +14,7 @@ use rustc_session::config::OptLevel;
 use tracing::{debug, instrument};
 
 use super::place::{PlaceRef, PlaceValue};
-use super::rvalue::transmute_immediate;
+use super::rvalue::transmute_scalar;
 use super::{FunctionCx, LocalRef};
 use crate::common::IntPredicate;
 use crate::traits::*;
@@ -346,14 +347,16 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
 
         let val = if field.is_zst() {
             OperandValue::ZeroSized
+        } else if let BackendRepr::SimdVector { .. } = self.layout.backend_repr {
+            // codegen_transmute_operand doesn't support SIMD, but since the previous
+            // check handled ZSTs, the only possible field access into something SIMD
+            // is to the `non_1zst_field` that's the same SIMD. (Other things, even
+            // just padding, would change the wrapper's representation type.)
+            assert_eq!(field.size, self.layout.size);
+            self.val
         } else if field.size == self.layout.size {
             assert_eq!(offset.bytes(), 0);
-            fx.codegen_transmute_operand(bx, *self, field).unwrap_or_else(|| {
-                bug!(
-                    "Expected `codegen_transmute_operand` to handle equal-size \
-                      field {i:?} projection from {self:?} to {field:?}"
-                )
-            })
+            fx.codegen_transmute_operand(bx, *self, field)
         } else {
             let (in_scalar, imm) = match (self.val, self.layout.backend_repr) {
                 // Extract a scalar component from a pair.
@@ -565,23 +568,30 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
 
     /// Creates an incomplete operand containing the [`abi::Scalar`]s expected based
     /// on the `layout` passed. This is for use with [`OperandRef::insert_field`]
-    /// later to set the necessary immediate(s).
+    /// later to set the necessary immediate(s), one-by-one converting all the `Right` to `Left`.
     ///
     /// Returns `None` for `layout`s which cannot be built this way.
     pub(crate) fn builder(
         layout: TyAndLayout<'tcx>,
-    ) -> Option<OperandRef<'tcx, Result<V, abi::Scalar>>> {
+    ) -> Option<OperandRef<'tcx, Either<V, abi::Scalar>>> {
+        // Uninhabited types are weird, because for example `Result<!, !>`
+        // shows up as `FieldsShape::Primitive` and we need to be able to write
+        // a field into `(u32, !)`. We'll do that in an `alloca` instead.
+        if layout.uninhabited {
+            return None;
+        }
+
         let val = match layout.backend_repr {
             BackendRepr::Memory { .. } if layout.is_zst() => OperandValue::ZeroSized,
-            BackendRepr::Scalar(s) => OperandValue::Immediate(Err(s)),
-            BackendRepr::ScalarPair(a, b) => OperandValue::Pair(Err(a), Err(b)),
+            BackendRepr::Scalar(s) => OperandValue::Immediate(Either::Right(s)),
+            BackendRepr::ScalarPair(a, b) => OperandValue::Pair(Either::Right(a), Either::Right(b)),
             BackendRepr::Memory { .. } | BackendRepr::SimdVector { .. } => return None,
         };
         Some(OperandRef { val, layout })
     }
 }
 
-impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> {
+impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Either<V, abi::Scalar>> {
     pub(crate) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
         &mut self,
         bx: &mut Bx,
@@ -605,31 +615,29 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> {
             (field_layout.is_zst(), field_offset == Size::ZERO)
         };
 
-        let mut update = |tgt: &mut Result<V, abi::Scalar>, src, from_scalar| {
-            let from_bty = bx.cx().type_from_scalar(from_scalar);
-            let to_scalar = tgt.unwrap_err();
-            let to_bty = bx.cx().type_from_scalar(to_scalar);
-            let imm = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty);
-            *tgt = Ok(imm);
+        let mut update = |tgt: &mut Either<V, abi::Scalar>, src, from_scalar| {
+            let to_scalar = tgt.unwrap_right();
+            let imm = transmute_scalar(bx, src, from_scalar, to_scalar);
+            *tgt = Either::Left(imm);
         };
 
         match (operand.val, operand.layout.backend_repr) {
             (OperandValue::ZeroSized, _) if expect_zst => {}
             (OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val {
-                OperandValue::Immediate(val @ Err(_)) if is_zero_offset => {
+                OperandValue::Immediate(val @ Either::Right(_)) if is_zero_offset => {
                     update(val, v, from_scalar);
                 }
-                OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => {
+                OperandValue::Pair(fst @ Either::Right(_), _) if is_zero_offset => {
                     update(fst, v, from_scalar);
                 }
-                OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => {
+                OperandValue::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => {
                     update(snd, v, from_scalar);
                 }
                 _ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
             },
             (OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => {
                 match &mut self.val {
-                    OperandValue::Pair(fst @ Err(_), snd @ Err(_)) => {
+                    OperandValue::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => {
                         update(fst, a, from_sa);
                         update(snd, b, from_sb);
                     }
@@ -640,17 +648,47 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> {
         }
     }
 
+    /// Insert the immediate value `imm` for field `f` in the *type itself*,
+    /// rather than into one of the variants.
+    ///
+    /// Most things want [`OperandRef::insert_field`] instead, but this one is
+    /// necessary for writing things like enum tags that aren't in any variant.
+    pub(super) fn insert_imm(&mut self, f: FieldIdx, imm: V) {
+        let field_offset = self.layout.fields.offset(f.as_usize());
+        let is_zero_offset = field_offset == Size::ZERO;
+        match &mut self.val {
+            OperandValue::Immediate(val @ Either::Right(_)) if is_zero_offset => {
+                *val = Either::Left(imm);
+            }
+            OperandValue::Pair(fst @ Either::Right(_), _) if is_zero_offset => {
+                *fst = Either::Left(imm);
+            }
+            OperandValue::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => {
+                *snd = Either::Left(imm);
+            }
+            _ => bug!("Tried to insert {imm:?} into field {f:?} of {self:?}"),
+        }
+    }
+
     /// After having set all necessary fields, this converts the
-    /// `OperandValue<Result<V, _>>` (as obtained from [`OperandRef::builder`])
+    /// `OperandValue<Either<V, _>>` (as obtained from [`OperandRef::builder`])
     /// to the normal `OperandValue<V>`.
     ///
     /// ICEs if any required fields were not set.
-    pub fn build(&self) -> OperandRef<'tcx, V> {
+    pub fn build(&self, cx: &impl CodegenMethods<'tcx, Value = V>) -> OperandRef<'tcx, V> {
         let OperandRef { val, layout } = *self;
 
-        let unwrap = |r: Result<V, abi::Scalar>| match r {
-            Ok(v) => v,
-            Err(_) => bug!("OperandRef::build called while fields are missing {self:?}"),
+        // For something like `Option::<u32>::None`, it's expected that the
+        // payload scalar will not actually have been set, so this converts
+        // unset scalars to corresponding `undef` values so long as the scalar
+        // from the layout allows uninit.
+        let unwrap = |r: Either<V, abi::Scalar>| match r {
+            Either::Left(v) => v,
+            Either::Right(s) if s.is_uninit_valid() => {
+                let bty = cx.type_from_scalar(s);
+                cx.const_undef(bty)
+            }
+            Either::Right(_) => bug!("OperandRef::build called while fields are missing {self:?}"),
         };
 
         let val = match val {
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 937063c24a6..0090be9fdef 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -1,4 +1,6 @@
-use rustc_abi::{Align, BackendRepr, FieldsShape, Size, TagEncoding, VariantIdx, Variants};
+use rustc_abi::{
+    Align, BackendRepr, FieldIdx, FieldsShape, Size, TagEncoding, VariantIdx, Variants,
+};
 use rustc_middle::mir::PlaceTy;
 use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
@@ -239,53 +241,17 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         bx: &mut Bx,
         variant_index: VariantIdx,
     ) {
-        if self.layout.for_variant(bx.cx(), variant_index).is_uninhabited() {
-            // We play it safe by using a well-defined `abort`, but we could go for immediate UB
-            // if that turns out to be helpful.
-            bx.abort();
-            return;
-        }
-        match self.layout.variants {
-            Variants::Empty => unreachable!("we already handled uninhabited types"),
-            Variants::Single { index } => assert_eq!(index, variant_index),
-
-            Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => {
-                let ptr = self.project_field(bx, tag_field.as_usize());
-                let to =
-                    self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val;
-                bx.store_to_place(
-                    bx.cx().const_uint_big(bx.cx().backend_type(ptr.layout), to),
-                    ptr.val,
-                );
+        match codegen_tag_value(bx.cx(), variant_index, self.layout) {
+            Err(UninhabitedVariantError) => {
+                // We play it safe by using a well-defined `abort`, but we could go for immediate UB
+                // if that turns out to be helpful.
+                bx.abort();
             }
-            Variants::Multiple {
-                tag_encoding:
-                    TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
-                tag_field,
-                ..
-            } => {
-                if variant_index != untagged_variant {
-                    let niche = self.project_field(bx, tag_field.as_usize());
-                    let niche_llty = bx.cx().immediate_backend_type(niche.layout);
-                    let BackendRepr::Scalar(scalar) = niche.layout.backend_repr else {
-                        bug!("expected a scalar placeref for the niche");
-                    };
-                    // We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping
-                    // around the `niche`'s type.
-                    // The easiest way to do that is to do wrapping arithmetic on `u128` and then
-                    // masking off any extra bits that occur because we did the arithmetic with too many bits.
-                    let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
-                    let niche_value = (niche_value as u128).wrapping_add(niche_start);
-                    let niche_value = niche_value & niche.layout.size.unsigned_int_max();
-
-                    let niche_llval = bx.cx().scalar_to_backend(
-                        Scalar::from_uint(niche_value, niche.layout.size),
-                        scalar,
-                        niche_llty,
-                    );
-                    OperandValue::Immediate(niche_llval).store(bx, niche);
-                }
+            Ok(Some((tag_field, imm))) => {
+                let tag_place = self.project_field(bx, tag_field.as_usize());
+                OperandValue::Immediate(imm).store(bx, tag_place);
             }
+            Ok(None) => {}
         }
     }
 
@@ -471,3 +437,73 @@ fn round_up_const_value_to_alignment<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     let offset = bx.and(neg_value, align_minus_1);
     bx.add(value, offset)
 }
+
+/// Calculates the value that needs to be stored to mark the discriminant.
+///
+/// This might be `None` for a `struct` or a niched variant (like `Some(&3)`).
+///
+/// If it's `Some`, it returns the value to store and the field in which to
+/// store it. Note that this value is *not* the same as the discriminant, in
+/// general, as it might be a niche value or have a different size.
+///
+/// It might also be an `Err` because the variant is uninhabited.
+pub(super) fn codegen_tag_value<'tcx, V>(
+    cx: &impl CodegenMethods<'tcx, Value = V>,
+    variant_index: VariantIdx,
+    layout: TyAndLayout<'tcx>,
+) -> Result<Option<(FieldIdx, V)>, UninhabitedVariantError> {
+    // By checking uninhabited-ness first we don't need to worry about types
+    // like `(u32, !)` which are single-variant but weird.
+    if layout.for_variant(cx, variant_index).is_uninhabited() {
+        return Err(UninhabitedVariantError);
+    }
+
+    Ok(match layout.variants {
+        Variants::Empty => unreachable!("we already handled uninhabited types"),
+        Variants::Single { index } => {
+            assert_eq!(index, variant_index);
+            None
+        }
+
+        Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => {
+            let discr = layout.ty.discriminant_for_variant(cx.tcx(), variant_index);
+            let to = discr.unwrap().val;
+            let tag_layout = layout.field(cx, tag_field.as_usize());
+            let tag_llty = cx.immediate_backend_type(tag_layout);
+            let imm = cx.const_uint_big(tag_llty, to);
+            Some((tag_field, imm))
+        }
+        Variants::Multiple {
+            tag_encoding: TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
+            tag_field,
+            ..
+        } => {
+            if variant_index != untagged_variant {
+                let niche_layout = layout.field(cx, tag_field.as_usize());
+                let niche_llty = cx.immediate_backend_type(niche_layout);
+                let BackendRepr::Scalar(scalar) = niche_layout.backend_repr else {
+                    bug!("expected a scalar placeref for the niche");
+                };
+                // We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping
+                // around the `niche`'s type.
+                // The easiest way to do that is to do wrapping arithmetic on `u128` and then
+                // masking off any extra bits that occur because we did the arithmetic with too many bits.
+                let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
+                let niche_value = (niche_value as u128).wrapping_add(niche_start);
+                let niche_value = niche_value & niche_layout.size.unsigned_int_max();
+
+                let niche_llval = cx.scalar_to_backend(
+                    Scalar::from_uint(niche_value, niche_layout.size),
+                    scalar,
+                    niche_llty,
+                );
+                Some((tag_field, niche_llval))
+            } else {
+                None
+            }
+        }
+    })
+}
+
+#[derive(Debug)]
+pub(super) struct UninhabitedVariantError;
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 60cf4e28b5a..43726e93252 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -1,18 +1,16 @@
-use std::assert_matches::assert_matches;
-
 use rustc_abi::{self as abi, FIRST_VARIANT};
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
-use rustc_middle::{bug, mir, span_bug};
+use rustc_middle::{bug, mir};
 use rustc_session::config::OptLevel;
 use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument};
 
 use super::operand::{OperandRef, OperandValue};
-use super::place::PlaceRef;
+use super::place::{PlaceRef, codegen_tag_value};
 use super::{FunctionCx, LocalRef};
-use crate::common::IntPredicate;
+use crate::common::{IntPredicate, TypeKind};
 use crate::traits::*;
 use crate::{MemFlags, base};
 
@@ -190,6 +188,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         }
     }
 
+    /// Transmutes the `src` value to the destination type by writing it to `dst`.
+    ///
+    /// See also [`Self::codegen_transmute_operand`] for cases that can be done
+    /// without needing a pre-allocated place for the destination.
     fn codegen_transmute(
         &mut self,
         bx: &mut Bx,
@@ -200,37 +202,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         assert!(src.layout.is_sized());
         assert!(dst.layout.is_sized());
 
-        if let Some(val) = self.codegen_transmute_operand(bx, src, dst.layout) {
-            val.store(bx, dst);
-            return;
-        }
-
-        match src.val {
-            OperandValue::Ref(..) | OperandValue::ZeroSized => {
-                span_bug!(
-                    self.mir.span,
-                    "Operand path should have handled transmute \
-                    from {src:?} to place {dst:?}"
-                );
-            }
-            OperandValue::Immediate(..) | OperandValue::Pair(..) => {
-                // When we have immediate(s), the alignment of the source is irrelevant,
-                // so we can store them using the destination's alignment.
-                src.val.store(bx, dst.val.with_type(src.layout));
-            }
+        if src.layout.size != dst.layout.size
+            || src.layout.is_uninhabited()
+            || dst.layout.is_uninhabited()
+        {
+            // These cases are all UB to actually hit, so don't emit code for them.
+            // (The size mismatches are reachable via `transmute_unchecked`.)
+            // We can't use unreachable because that's a terminator, and we
+            // need something that can be in the middle of a basic block.
+            bx.assume(bx.cx().const_bool(false))
+        } else {
+            // Since in this path we have a place anyway, we can store or copy to it,
+            // making sure we use the destination place's alignment even if the
+            // source would normally have a higher one.
+            src.val.store(bx, dst.val.with_type(src.layout));
         }
     }
 
-    /// Attempts to transmute an `OperandValue` to another `OperandValue`.
+    /// Transmutes an `OperandValue` to another `OperandValue`.
     ///
-    /// Returns `None` for cases that can't work in that framework, such as for
-    /// `Immediate`->`Ref` that needs an `alloc` to get the location.
+    /// This is supported only for cases where [`Self::rvalue_creates_operand`]
+    /// returns `true`, and will ICE otherwise. (In particular, anything that
+    /// would need to `alloca` in order to return a `PlaceValue` will ICE,
+    /// expecting those to go via [`Self::codegen_transmute`] instead where
+    /// the destination place is already allocated.)
     pub(crate) fn codegen_transmute_operand(
         &mut self,
         bx: &mut Bx,
         operand: OperandRef<'tcx, Bx::Value>,
         cast: TyAndLayout<'tcx>,
-    ) -> Option<OperandValue<Bx::Value>> {
+    ) -> OperandValue<Bx::Value> {
         // Check for transmutes that are always UB.
         if operand.layout.size != cast.size
             || operand.layout.is_uninhabited()
@@ -244,71 +245,34 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
             // Because this transmute is UB, return something easy to generate,
             // since it's fine that later uses of the value are probably UB.
-            return Some(OperandValue::poison(bx, cast));
+            return OperandValue::poison(bx, cast);
         }
 
-        let operand_kind = self.value_kind(operand.layout);
-        let cast_kind = self.value_kind(cast);
-
-        match operand.val {
-            OperandValue::Ref(source_place_val) => {
+        match (operand.val, operand.layout.backend_repr, cast.backend_repr) {
+            _ if cast.is_zst() => OperandValue::ZeroSized,
+            (_, _, abi::BackendRepr::Memory { .. }) => {
+                bug!("Cannot `codegen_transmute_operand` to non-ZST memory-ABI output {cast:?}");
+            }
+            (OperandValue::Ref(source_place_val), abi::BackendRepr::Memory { .. }, _) => {
                 assert_eq!(source_place_val.llextra, None);
-                assert_matches!(operand_kind, OperandValueKind::Ref);
                 // The existing alignment is part of `source_place_val`,
                 // so that alignment will be used, not `cast`'s.
-                Some(bx.load_operand(source_place_val.with_type(cast)).val)
-            }
-            OperandValue::ZeroSized => {
-                let OperandValueKind::ZeroSized = operand_kind else {
-                    bug!("Found {operand_kind:?} for operand {operand:?}");
-                };
-                if let OperandValueKind::ZeroSized = cast_kind {
-                    Some(OperandValue::ZeroSized)
-                } else {
-                    None
-                }
-            }
-            OperandValue::Immediate(imm) => {
-                let OperandValueKind::Immediate(from_scalar) = operand_kind else {
-                    bug!("Found {operand_kind:?} for operand {operand:?}");
-                };
-                if let OperandValueKind::Immediate(to_scalar) = cast_kind
-                    && from_scalar.size(self.cx) == to_scalar.size(self.cx)
-                {
-                    let from_backend_ty = bx.backend_type(operand.layout);
-                    let to_backend_ty = bx.backend_type(cast);
-                    Some(OperandValue::Immediate(transmute_immediate(
-                        bx,
-                        imm,
-                        from_scalar,
-                        from_backend_ty,
-                        to_scalar,
-                        to_backend_ty,
-                    )))
-                } else {
-                    None
-                }
-            }
-            OperandValue::Pair(imm_a, imm_b) => {
-                let OperandValueKind::Pair(in_a, in_b) = operand_kind else {
-                    bug!("Found {operand_kind:?} for operand {operand:?}");
-                };
-                if let OperandValueKind::Pair(out_a, out_b) = cast_kind
-                    && in_a.size(self.cx) == out_a.size(self.cx)
-                    && in_b.size(self.cx) == out_b.size(self.cx)
-                {
-                    let in_a_ibty = bx.scalar_pair_element_backend_type(operand.layout, 0, false);
-                    let in_b_ibty = bx.scalar_pair_element_backend_type(operand.layout, 1, false);
-                    let out_a_ibty = bx.scalar_pair_element_backend_type(cast, 0, false);
-                    let out_b_ibty = bx.scalar_pair_element_backend_type(cast, 1, false);
-                    Some(OperandValue::Pair(
-                        transmute_immediate(bx, imm_a, in_a, in_a_ibty, out_a, out_a_ibty),
-                        transmute_immediate(bx, imm_b, in_b, in_b_ibty, out_b, out_b_ibty),
-                    ))
-                } else {
-                    None
-                }
+                bx.load_operand(source_place_val.with_type(cast)).val
             }
+            (
+                OperandValue::Immediate(imm),
+                abi::BackendRepr::Scalar(from_scalar),
+                abi::BackendRepr::Scalar(to_scalar),
+            ) => OperandValue::Immediate(transmute_scalar(bx, imm, from_scalar, to_scalar)),
+            (
+                OperandValue::Pair(imm_a, imm_b),
+                abi::BackendRepr::ScalarPair(in_a, in_b),
+                abi::BackendRepr::ScalarPair(out_a, out_b),
+            ) => OperandValue::Pair(
+                transmute_scalar(bx, imm_a, in_a, out_a),
+                transmute_scalar(bx, imm_b, in_b, out_b),
+            ),
+            _ => bug!("Cannot `codegen_transmute_operand` {operand:?} to {cast:?}"),
         }
     }
 
@@ -479,9 +443,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     // path as the other integer-to-X casts.
                     | mir::CastKind::PointerWithExposedProvenance => {
                         let imm = operand.immediate();
-                        let operand_kind = self.value_kind(operand.layout);
-                        let OperandValueKind::Immediate(from_scalar) = operand_kind else {
-                            bug!("Found {operand_kind:?} for operand {operand:?}");
+                        let abi::BackendRepr::Scalar(from_scalar) = operand.layout.backend_repr else {
+                            bug!("Found non-scalar for operand {operand:?}");
                         };
                         let from_backend_ty = bx.cx().immediate_backend_type(operand.layout);
 
@@ -491,9 +454,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             let val = OperandValue::Immediate(bx.cx().const_poison(to_backend_ty));
                             return OperandRef { val, layout: cast };
                         }
-                        let cast_kind = self.value_kind(cast);
-                        let OperandValueKind::Immediate(to_scalar) = cast_kind else {
-                            bug!("Found {cast_kind:?} for operand {cast:?}");
+                        let abi::BackendRepr::Scalar(to_scalar) = cast.layout.backend_repr else {
+                            bug!("Found non-scalar for cast {cast:?}");
                         };
 
                         self.cast_immediate(bx, imm, from_scalar, from_backend_ty, to_scalar, to_backend_ty)
@@ -503,9 +465,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             })
                     }
                     mir::CastKind::Transmute => {
-                        self.codegen_transmute_operand(bx, operand, cast).unwrap_or_else(|| {
-                            bug!("Unsupported transmute-as-operand of {operand:?} to {cast:?}");
-                        })
+                        self.codegen_transmute_operand(bx, operand, cast)
                     }
                 };
                 OperandRef { val, layout: cast }
@@ -694,7 +654,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
             mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
             mir::Rvalue::Repeat(..) => bug!("{rvalue:?} in codegen_rvalue_operand"),
-            mir::Rvalue::Aggregate(_, ref fields) => {
+            mir::Rvalue::Aggregate(ref kind, ref fields) => {
+                let (variant_index, active_field_index) = match **kind {
+                    mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
+                        (variant_index, active_field_index)
+                    }
+                    _ => (FIRST_VARIANT, None),
+                };
+
                 let ty = rvalue.ty(self.mir, self.cx.tcx());
                 let ty = self.monomorphize(ty);
                 let layout = self.cx.layout_of(ty);
@@ -706,10 +673,27 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 };
                 for (field_idx, field) in fields.iter_enumerated() {
                     let op = self.codegen_operand(bx, field);
-                    builder.insert_field(bx, FIRST_VARIANT, field_idx, op);
+                    let fi = active_field_index.unwrap_or(field_idx);
+                    builder.insert_field(bx, variant_index, fi, op);
                 }
 
-                builder.build()
+                let tag_result = codegen_tag_value(self.cx, variant_index, layout);
+                match tag_result {
+                    Err(super::place::UninhabitedVariantError) => {
+                        // Like codegen_set_discr we use a sound abort, but could
+                        // potentially `unreachable` or just return the poison for
+                        // more optimizability, if that turns out to be helpful.
+                        bx.abort();
+                        let val = OperandValue::poison(bx, layout);
+                        OperandRef { val, layout }
+                    }
+                    Ok(maybe_tag_value) => {
+                        if let Some((tag_field, tag_imm)) = maybe_tag_value {
+                            builder.insert_imm(tag_field, tag_imm);
+                        }
+                        builder.build(bx.cx())
+                    }
+                }
             }
             mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
                 let operand = self.codegen_operand(bx, operand);
@@ -987,37 +971,46 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         OperandValue::Pair(val, of)
     }
 
+    /// Returns `true` if the `rvalue` can be computed into an [`OperandRef`],
+    /// rather than needing a full `PlaceRef` for the assignment destination.
+    ///
+    /// This is used by the [`super::analyze`] code to decide which MIR locals
+    /// can stay as SSA values (as opposed to generating `alloca` slots for them).
+    /// As such, some paths here return `true` even where the specific rvalue
+    /// will not actually take the operand path because the result type is such
+    /// that it always gets an `alloca`, but where it's not worth re-checking the
+    /// layout in this code when the right thing will happen anyway.
     pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool {
         match *rvalue {
             mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => {
                 let operand_ty = operand.ty(self.mir, self.cx.tcx());
                 let cast_layout = self.cx.layout_of(self.monomorphize(cast_ty));
                 let operand_layout = self.cx.layout_of(self.monomorphize(operand_ty));
+                match (operand_layout.backend_repr, cast_layout.backend_repr) {
+                    // When the output will be in memory anyway, just use its place
+                    // (instead of the operand path) unless it's the trivial ZST case.
+                    (_, abi::BackendRepr::Memory { .. }) => cast_layout.is_zst(),
 
-                match (self.value_kind(operand_layout), self.value_kind(cast_layout)) {
-                    // Can always load from a pointer as needed
-                    (OperandValueKind::Ref, _) => true,
-
-                    // ZST-to-ZST is the easiest thing ever
-                    (OperandValueKind::ZeroSized, OperandValueKind::ZeroSized) => true,
-
-                    // But if only one of them is a ZST the sizes can't match
-                    (OperandValueKind::ZeroSized, _) | (_, OperandValueKind::ZeroSized) => false,
-
-                    // Need to generate an `alloc` to get a pointer from an immediate
-                    (OperandValueKind::Immediate(..) | OperandValueKind::Pair(..), OperandValueKind::Ref) => false,
+                    // Otherwise (for a non-memory output) if the input is memory
+                    // then we can just read the value from the place.
+                    (abi::BackendRepr::Memory { .. }, _) => true,
 
                     // When we have scalar immediates, we can only convert things
                     // where the sizes match, to avoid endianness questions.
-                    (OperandValueKind::Immediate(a), OperandValueKind::Immediate(b)) =>
+                    (abi::BackendRepr::Scalar(a), abi::BackendRepr::Scalar(b)) =>
                         a.size(self.cx) == b.size(self.cx),
-                    (OperandValueKind::Pair(a0, a1), OperandValueKind::Pair(b0, b1)) =>
+                    (abi::BackendRepr::ScalarPair(a0, a1), abi::BackendRepr::ScalarPair(b0, b1)) =>
                         a0.size(self.cx) == b0.size(self.cx) && a1.size(self.cx) == b1.size(self.cx),
 
-                    // Send mixings between scalars and pairs through the memory route
-                    // FIXME: Maybe this could use insertvalue/extractvalue instead?
-                    (OperandValueKind::Immediate(..), OperandValueKind::Pair(..)) |
-                    (OperandValueKind::Pair(..), OperandValueKind::Immediate(..)) => false,
+                    // Mixing Scalars and ScalarPairs can get quite complicated when
+                    // padding and undef get involved, so leave that to the memory path.
+                    (abi::BackendRepr::Scalar(_), abi::BackendRepr::ScalarPair(_, _)) |
+                    (abi::BackendRepr::ScalarPair(_, _), abi::BackendRepr::Scalar(_)) => false,
+
+                    // SIMD vectors aren't worth the trouble of dealing with complex
+                    // cases like from vectors of f32 to vectors of pointers or
+                    // from fat pointers to vectors of u16. (See #143194 #110021 ...)
+                    (abi::BackendRepr::SimdVector { .. }, _) | (_, abi::BackendRepr::SimdVector { .. }) => false,
                 }
             }
             mir::Rvalue::Ref(..) |
@@ -1037,93 +1030,53 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             // Arrays are always aggregates, so it's not worth checking anything here.
             // (If it's really `[(); N]` or `[T; 0]` and we use the place path, fine.)
             mir::Rvalue::Repeat(..) => false,
-            mir::Rvalue::Aggregate(ref kind, _) => {
-                let allowed_kind = match **kind {
-                    // This always produces a `ty::RawPtr`, so will be Immediate or Pair
-                    mir::AggregateKind::RawPtr(..) => true,
-                    mir::AggregateKind::Array(..) => false,
-                    mir::AggregateKind::Tuple => true,
-                    mir::AggregateKind::Adt(def_id, ..) => {
-                        let adt_def = self.cx.tcx().adt_def(def_id);
-                        adt_def.is_struct() && !adt_def.repr().simd()
-                    }
-                    mir::AggregateKind::Closure(..) => true,
-                    // FIXME: Can we do this for simple coroutines too?
-                    mir::AggregateKind::Coroutine(..) | mir::AggregateKind::CoroutineClosure(..) => false,
-                };
-                allowed_kind && {
+            mir::Rvalue::Aggregate(..) => {
                     let ty = rvalue.ty(self.mir, self.cx.tcx());
                     let ty = self.monomorphize(ty);
                     let layout = self.cx.spanned_layout_of(ty, span);
                     OperandRef::<Bx::Value>::builder(layout).is_some()
                 }
             }
-        }
 
         // (*) this is only true if the type is suitable
     }
-
-    /// Gets which variant of [`OperandValue`] is expected for a particular type.
-    fn value_kind(&self, layout: TyAndLayout<'tcx>) -> OperandValueKind {
-        if layout.is_zst() {
-            OperandValueKind::ZeroSized
-        } else if self.cx.is_backend_immediate(layout) {
-            assert!(!self.cx.is_backend_scalar_pair(layout));
-            OperandValueKind::Immediate(match layout.backend_repr {
-                abi::BackendRepr::Scalar(s) => s,
-                abi::BackendRepr::SimdVector { element, .. } => element,
-                x => span_bug!(self.mir.span, "Couldn't translate {x:?} as backend immediate"),
-            })
-        } else if self.cx.is_backend_scalar_pair(layout) {
-            let abi::BackendRepr::ScalarPair(s1, s2) = layout.backend_repr else {
-                span_bug!(
-                    self.mir.span,
-                    "Couldn't translate {:?} as backend scalar pair",
-                    layout.backend_repr,
-                );
-            };
-            OperandValueKind::Pair(s1, s2)
-        } else {
-            OperandValueKind::Ref
-        }
-    }
-}
-
-/// The variants of this match [`OperandValue`], giving details about the
-/// backend values that will be held in that other type.
-#[derive(Debug, Copy, Clone)]
-enum OperandValueKind {
-    Ref,
-    Immediate(abi::Scalar),
-    Pair(abi::Scalar, abi::Scalar),
-    ZeroSized,
 }
 
-/// Transmutes one of the immediates from an [`OperandValue::Immediate`]
-/// or an [`OperandValue::Pair`] to an immediate of the target type.
+/// Transmutes a single scalar value `imm` from `from_scalar` to `to_scalar`.
 ///
-/// `to_backend_ty` must be the *non*-immediate backend type (so it will be
-/// `i8`, not `i1`, for `bool`-like types.)
-pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+/// This is expected to be in *immediate* form, as seen in [`OperandValue::Immediate`]
+/// or [`OperandValue::Pair`] (so `i1` for bools, not `i8`, for example).
+///
+/// ICEs if the passed-in `imm` is not a value of the expected type for
+/// `from_scalar`, such as if it's a vector or a pair.
+pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
     mut imm: Bx::Value,
     from_scalar: abi::Scalar,
-    from_backend_ty: Bx::Type,
     to_scalar: abi::Scalar,
-    to_backend_ty: Bx::Type,
 ) -> Bx::Value {
     assert_eq!(from_scalar.size(bx.cx()), to_scalar.size(bx.cx()));
+    let imm_ty = bx.cx().val_ty(imm);
+    assert_ne!(
+        bx.cx().type_kind(imm_ty),
+        TypeKind::Vector,
+        "Vector type {imm_ty:?} not allowed in transmute_scalar {from_scalar:?} -> {to_scalar:?}"
+    );
 
     // While optimizations will remove no-op transmutes, they might still be
     // there in debug or things that aren't no-op in MIR because they change
     // the Rust type but not the underlying layout/niche.
-    if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
+    if from_scalar == to_scalar {
         return imm;
     }
 
     use abi::Primitive::*;
     imm = bx.from_immediate(imm);
 
+    let from_backend_ty = bx.cx().type_from_scalar(from_scalar);
+    debug_assert_eq!(bx.cx().val_ty(imm), from_backend_ty);
+    let to_backend_ty = bx.cx().type_from_scalar(to_scalar);
+
     // If we have a scalar, we must already know its range. Either
     //
     // 1) It's a parameter with `range` parameter metadata,
@@ -1154,6 +1107,8 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         }
     };
 
+    debug_assert_eq!(bx.cx().val_ty(imm), to_backend_ty);
+
     // This `assume` remains important for cases like (a conceptual)
     //    transmute::<u32, NonZeroU32>(x) == 0
     // since it's never passed to something with parameter metadata (especially
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index d95d552d7d5..0082f90f3b8 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -1,9 +1,9 @@
 // Not in interpret to make sure we do not use private implementation details
 
 use rustc_abi::{FieldIdx, VariantIdx};
-use rustc_middle::query::Key;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, mir};
+use rustc_span::DUMMY_SP;
 use tracing::instrument;
 
 use crate::interpret::InterpCx;
@@ -71,8 +71,7 @@ pub fn tag_for_variant_provider<'tcx>(
     let (ty, variant_index) = key.value;
     assert!(ty.is_enum());
 
-    let ecx =
-        InterpCx::new(tcx, ty.default_span(tcx), key.typing_env, crate::const_eval::DummyMachine);
+    let ecx = InterpCx::new(tcx, DUMMY_SP, key.typing_env, crate::const_eval::DummyMachine);
 
     let layout = ecx.layout_of(ty).unwrap();
     ecx.tag_for_variant(layout, variant_index).unwrap().map(|(tag, _tag_field)| tag)
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 80f6e9d9fc4..d6d89808839 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -348,6 +348,10 @@ pub trait TTMacroExpander {
         span: Span,
         input: TokenStream,
     ) -> MacroExpanderResult<'cx>;
+
+    fn get_unused_rule(&self, _rule_i: usize) -> Option<(&Ident, Span)> {
+        None
+    }
 }
 
 pub type MacroExpanderResult<'cx> = ExpandResult<Box<dyn MacResult + 'cx>, ()>;
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index c607a3a3652..7a280d671f4 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -10,7 +10,7 @@ use rustc_span::source_map::SourceMap;
 use rustc_span::{ErrorGuaranteed, Ident, Span};
 use tracing::debug;
 
-use super::macro_rules::{NoopTracker, parser_from_cx};
+use super::macro_rules::{MacroRule, NoopTracker, parser_from_cx};
 use crate::expand::{AstFragmentKind, parse_ast_fragment};
 use crate::mbe::macro_parser::ParseResult::*;
 use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser};
@@ -22,14 +22,14 @@ pub(super) fn failed_to_match_macro(
     def_span: Span,
     name: Ident,
     arg: TokenStream,
-    lhses: &[Vec<MatcherLoc>],
+    rules: &[MacroRule],
 ) -> (Span, ErrorGuaranteed) {
     debug!("failed to match macro");
     // An error occurred, try the expansion again, tracking the expansion closely for better
     // diagnostics.
     let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);
 
-    let try_success_result = try_match_macro(psess, name, &arg, lhses, &mut tracker);
+    let try_success_result = try_match_macro(psess, name, &arg, rules, &mut tracker);
 
     if try_success_result.is_ok() {
         // Nonterminal parser recovery might turn failed matches into successful ones,
@@ -80,12 +80,12 @@ pub(super) fn failed_to_match_macro(
 
     // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
     if let Some((arg, comma_span)) = arg.add_comma() {
-        for lhs in lhses {
+        for rule in rules {
             let parser = parser_from_cx(psess, arg.clone(), Recovery::Allowed);
             let mut tt_parser = TtParser::new(name);
 
             if let Success(_) =
-                tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker)
+                tt_parser.parse_tt(&mut Cow::Borrowed(&parser), &rule.lhs, &mut NoopTracker)
             {
                 if comma_span.is_dummy() {
                     err.note("you might be missing a comma");
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 2ffd4e3cf28..52cdcc5c747 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -36,6 +36,7 @@ use crate::base::{
 };
 use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_ast_fragment};
 use crate::mbe::macro_parser::{Error, ErrorReported, Failure, MatcherLoc, Success, TtParser};
+use crate::mbe::quoted::{RulePart, parse_one_tt};
 use crate::mbe::transcribe::transcribe;
 use crate::mbe::{self, KleeneOp, macro_check};
 
@@ -97,13 +98,18 @@ impl<'a> ParserAnyMacro<'a> {
     }
 }
 
+pub(super) struct MacroRule {
+    pub(super) lhs: Vec<MatcherLoc>,
+    lhs_span: Span,
+    rhs: mbe::TokenTree,
+}
+
 struct MacroRulesMacroExpander {
     node_id: NodeId,
     name: Ident,
     span: Span,
     transparency: Transparency,
-    lhses: Vec<Vec<MatcherLoc>>,
-    rhses: Vec<mbe::TokenTree>,
+    rules: Vec<MacroRule>,
 }
 
 impl TTMacroExpander for MacroRulesMacroExpander {
@@ -121,10 +127,15 @@ impl TTMacroExpander for MacroRulesMacroExpander {
             self.name,
             self.transparency,
             input,
-            &self.lhses,
-            &self.rhses,
+            &self.rules,
         ))
     }
+
+    fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, Span)> {
+        // If the rhs contains an invocation like `compile_error!`, don't report it as unused.
+        let rule = &self.rules[rule_i];
+        if has_compile_error_macro(&rule.rhs) { None } else { Some((&self.name, rule.lhs_span)) }
+    }
 }
 
 struct DummyExpander(ErrorGuaranteed);
@@ -183,9 +194,8 @@ impl<'matcher> Tracker<'matcher> for NoopTracker {
     }
 }
 
-/// Expands the rules based macro defined by `lhses` and `rhses` for a given
-/// input `arg`.
-#[instrument(skip(cx, transparency, arg, lhses, rhses))]
+/// Expands the rules based macro defined by `rules` for a given input `arg`.
+#[instrument(skip(cx, transparency, arg, rules))]
 fn expand_macro<'cx>(
     cx: &'cx mut ExtCtxt<'_>,
     sp: Span,
@@ -194,8 +204,7 @@ fn expand_macro<'cx>(
     name: Ident,
     transparency: Transparency,
     arg: TokenStream,
-    lhses: &[Vec<MatcherLoc>],
-    rhses: &[mbe::TokenTree],
+    rules: &[MacroRule],
 ) -> Box<dyn MacResult + 'cx> {
     let psess = &cx.sess.psess;
     // Macros defined in the current crate have a real node id,
@@ -208,15 +217,14 @@ fn expand_macro<'cx>(
     }
 
     // Track nothing for the best performance.
-    let try_success_result = try_match_macro(psess, name, &arg, lhses, &mut NoopTracker);
+    let try_success_result = try_match_macro(psess, name, &arg, rules, &mut NoopTracker);
 
     match try_success_result {
-        Ok((i, named_matches)) => {
-            let (rhs, rhs_span): (&mbe::Delimited, DelimSpan) = match &rhses[i] {
-                mbe::TokenTree::Delimited(span, _, delimited) => (&delimited, *span),
-                _ => cx.dcx().span_bug(sp, "malformed macro rhs"),
+        Ok((i, rule, named_matches)) => {
+            let mbe::TokenTree::Delimited(rhs_span, _, ref rhs) = rule.rhs else {
+                cx.dcx().span_bug(sp, "malformed macro rhs");
             };
-            let arm_span = rhses[i].span();
+            let arm_span = rule.rhs.span();
 
             // rhs has holes ( `$id` and `$(...)` that need filled)
             let id = cx.current_expansion.id;
@@ -262,7 +270,7 @@ fn expand_macro<'cx>(
         Err(CanRetry::Yes) => {
             // Retry and emit a better error.
             let (span, guar) =
-                diagnostics::failed_to_match_macro(cx.psess(), sp, def_span, name, arg, lhses);
+                diagnostics::failed_to_match_macro(cx.psess(), sp, def_span, name, arg, rules);
             cx.trace_macros_diag();
             DummyResult::any(span, guar)
         }
@@ -278,14 +286,14 @@ pub(super) enum CanRetry {
 /// Try expanding the macro. Returns the index of the successful arm and its named_matches if it was successful,
 /// and nothing if it failed. On failure, it's the callers job to use `track` accordingly to record all errors
 /// correctly.
-#[instrument(level = "debug", skip(psess, arg, lhses, track), fields(tracking = %T::description()))]
+#[instrument(level = "debug", skip(psess, arg, rules, track), fields(tracking = %T::description()))]
 pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
     psess: &ParseSess,
     name: Ident,
     arg: &TokenStream,
-    lhses: &'matcher [Vec<MatcherLoc>],
+    rules: &'matcher [MacroRule],
     track: &mut T,
-) -> Result<(usize, NamedMatches), CanRetry> {
+) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> {
     // We create a base parser that can be used for the "black box" parts.
     // Every iteration needs a fresh copy of that parser. However, the parser
     // is not mutated on many of the iterations, particularly when dealing with
@@ -308,7 +316,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
     let parser = parser_from_cx(psess, arg.clone(), T::recovery());
     // Try each arm's matchers.
     let mut tt_parser = TtParser::new(name);
-    for (i, lhs) in lhses.iter().enumerate() {
+    for (i, rule) in rules.iter().enumerate() {
         let _tracing_span = trace_span!("Matching arm", %i);
 
         // Take a snapshot of the state of pre-expansion gating at this point.
@@ -317,7 +325,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
         // are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
         let mut gated_spans_snapshot = mem::take(&mut *psess.gated_spans.spans.borrow_mut());
 
-        let result = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, track);
+        let result = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), &rule.lhs, track);
 
         track.after_arm(&result);
 
@@ -328,7 +336,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
                 // Merge the gated spans from parsing the matcher with the preexisting ones.
                 psess.gated_spans.merge(gated_spans_snapshot);
 
-                return Ok((i, named_matches));
+                return Ok((i, rule, named_matches));
             }
             Failure(_) => {
                 trace!("Failed to match arm, trying the next one");
@@ -364,7 +372,7 @@ pub fn compile_declarative_macro(
     span: Span,
     node_id: NodeId,
     edition: Edition,
-) -> (SyntaxExtension, Vec<(usize, Span)>) {
+) -> (SyntaxExtension, usize) {
     let mk_syn_ext = |expander| {
         SyntaxExtension::new(
             sess,
@@ -377,7 +385,7 @@ pub fn compile_declarative_macro(
             node_id != DUMMY_NODE_ID,
         )
     };
-    let dummy_syn_ext = |guar| (mk_syn_ext(Arc::new(DummyExpander(guar))), Vec::new());
+    let dummy_syn_ext = |guar| (mk_syn_ext(Arc::new(DummyExpander(guar))), 0);
 
     let macro_rules = macro_def.macro_rules;
     let exp_sep = if macro_rules { exp!(Semi) } else { exp!(Comma) };
@@ -389,21 +397,11 @@ pub fn compile_declarative_macro(
     let mut guar = None;
     let mut check_emission = |ret: Result<(), ErrorGuaranteed>| guar = guar.or(ret.err());
 
-    let mut lhses = Vec::new();
-    let mut rhses = Vec::new();
+    let mut rules = Vec::new();
 
     while p.token != token::Eof {
         let lhs_tt = p.parse_token_tree();
-        let lhs_tt = mbe::quoted::parse(
-            &TokenStream::new(vec![lhs_tt]),
-            true, // LHS
-            sess,
-            node_id,
-            features,
-            edition,
-        )
-        .pop()
-        .unwrap();
+        let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition);
         // We don't handle errors here, the driver will abort after parsing/expansion. We can
         // report every error in every macro this way.
         check_emission(check_lhs_nt_follows(sess, node_id, &lhs_tt));
@@ -421,20 +419,18 @@ pub fn compile_declarative_macro(
             return dummy_syn_ext(guar);
         }
         let rhs_tt = p.parse_token_tree();
-        let rhs_tt = mbe::quoted::parse(
-            &TokenStream::new(vec![rhs_tt]),
-            false, // RHS
-            sess,
-            node_id,
-            features,
-            edition,
-        )
-        .pop()
-        .unwrap();
+        let rhs_tt = parse_one_tt(rhs_tt, RulePart::Body, sess, node_id, features, edition);
         check_emission(check_rhs(sess, &rhs_tt));
         check_emission(macro_check::check_meta_variables(&sess.psess, node_id, &lhs_tt, &rhs_tt));
-        lhses.push(lhs_tt);
-        rhses.push(rhs_tt);
+        let lhs_span = lhs_tt.span();
+        // Convert the lhs into `MatcherLoc` form, which is better for doing the
+        // actual matching.
+        let lhs = if let mbe::TokenTree::Delimited(.., delimited) = lhs_tt {
+            mbe::macro_parser::compute_locs(&delimited.tts)
+        } else {
+            return dummy_syn_ext(guar.unwrap());
+        };
+        rules.push(MacroRule { lhs, lhs_span, rhs: rhs_tt });
         if p.token == token::Eof {
             break;
         }
@@ -443,7 +439,7 @@ pub fn compile_declarative_macro(
         }
     }
 
-    if lhses.is_empty() {
+    if rules.is_empty() {
         let guar = sess.dcx().span_err(span, "macros must contain at least one rule");
         return dummy_syn_ext(guar);
     }
@@ -457,48 +453,12 @@ pub fn compile_declarative_macro(
         return dummy_syn_ext(guar);
     }
 
-    // Compute the spans of the macro rules for unused rule linting.
-    // Also, we are only interested in non-foreign macros.
-    let rule_spans = if node_id != DUMMY_NODE_ID {
-        lhses
-            .iter()
-            .zip(rhses.iter())
-            .enumerate()
-            // If the rhs contains an invocation like compile_error!,
-            // don't consider the rule for the unused rule lint.
-            .filter(|(_idx, (_lhs, rhs))| !has_compile_error_macro(rhs))
-            // We only take the span of the lhs here,
-            // so that the spans of created warnings are smaller.
-            .map(|(idx, (lhs, _rhs))| (idx, lhs.span()))
-            .collect::<Vec<_>>()
-    } else {
-        Vec::new()
-    };
+    // Return the number of rules for unused rule linting, if this is a local macro.
+    let nrules = if node_id != DUMMY_NODE_ID { rules.len() } else { 0 };
 
-    // Convert the lhses into `MatcherLoc` form, which is better for doing the
-    // actual matching.
-    let lhses = lhses
-        .iter()
-        .map(|lhs| {
-            // Ignore the delimiters around the matcher.
-            match lhs {
-                mbe::TokenTree::Delimited(.., delimited) => {
-                    mbe::macro_parser::compute_locs(&delimited.tts)
-                }
-                _ => sess.dcx().span_bug(span, "malformed macro lhs"),
-            }
-        })
-        .collect();
-
-    let expander = Arc::new(MacroRulesMacroExpander {
-        name: ident,
-        span,
-        node_id,
-        transparency,
-        lhses,
-        rhses,
-    });
-    (mk_syn_ext(expander), rule_spans)
+    let expander =
+        Arc::new(MacroRulesMacroExpander { name: ident, span, node_id, transparency, rules });
+    (mk_syn_ext(expander), nrules)
 }
 
 fn check_lhs_nt_follows(
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index 2daa4e71558..eb874a27cec 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -16,6 +16,27 @@ pub(crate) const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are
     `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, \
     `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility";
 
+/// Which part of a macro rule we're parsing
+#[derive(Copy, Clone)]
+pub(crate) enum RulePart {
+    /// The left-hand side, with patterns and metavar definitions with types
+    Pattern,
+    /// The right-hand side body, with metavar references and metavar expressions
+    Body,
+}
+
+impl RulePart {
+    #[inline(always)]
+    fn is_pattern(&self) -> bool {
+        matches!(self, Self::Pattern)
+    }
+
+    #[inline(always)]
+    fn is_body(&self) -> bool {
+        matches!(self, Self::Body)
+    }
+}
+
 /// Takes a `tokenstream::TokenStream` and returns a `Vec<self::TokenTree>`. Specifically, this
 /// takes a generic `TokenStream`, such as is used in the rest of the compiler, and returns a
 /// collection of `TokenTree` for use in parsing a macro.
@@ -23,8 +44,8 @@ pub(crate) const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are
 /// # Parameters
 ///
 /// - `input`: a token stream to read from, the contents of which we are parsing.
-/// - `parsing_patterns`: `parse` can be used to parse either the "patterns" or the "body" of a
-///   macro. Both take roughly the same form _except_ that:
+/// - `part`: whether we're parsing the patterns or the body of a macro. Both take roughly the same
+///   form _except_ that:
 ///   - In a pattern, metavars are declared with their "matcher" type. For example `$var:expr` or
 ///     `$id:ident`. In this example, `expr` and `ident` are "matchers". They are not present in the
 ///     body of a macro rule -- just in the pattern.
@@ -36,9 +57,9 @@ pub(crate) const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are
 /// # Returns
 ///
 /// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`.
-pub(super) fn parse(
+fn parse(
     input: &tokenstream::TokenStream,
-    parsing_patterns: bool,
+    part: RulePart,
     sess: &Session,
     node_id: NodeId,
     features: &Features,
@@ -53,9 +74,9 @@ pub(super) fn parse(
     while let Some(tree) = iter.next() {
         // Given the parsed tree, if there is a metavar and we are expecting matchers, actually
         // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
-        let tree = parse_tree(tree, &mut iter, parsing_patterns, sess, node_id, features, edition);
+        let tree = parse_tree(tree, &mut iter, part, sess, node_id, features, edition);
 
-        if !parsing_patterns {
+        if part.is_body() {
             // No matchers allowed, nothing to process here
             result.push(tree);
             continue;
@@ -131,6 +152,22 @@ pub(super) fn parse(
     result
 }
 
+/// Takes a `tokenstream::TokenTree` and returns a `self::TokenTree`. Like `parse`, but for a
+/// single token tree. Emits errors to `sess` if needed.
+#[inline]
+pub(super) fn parse_one_tt(
+    input: tokenstream::TokenTree,
+    part: RulePart,
+    sess: &Session,
+    node_id: NodeId,
+    features: &Features,
+    edition: Edition,
+) -> TokenTree {
+    parse(&tokenstream::TokenStream::new(vec![input]), part, sess, node_id, features, edition)
+        .pop()
+        .unwrap()
+}
+
 /// Asks for the `macro_metavar_expr` feature if it is not enabled
 fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, span: Span) {
     if !features.macro_metavar_expr() {
@@ -157,13 +194,13 @@ fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Sess
 /// - `tree`: the tree we wish to convert.
 /// - `outer_iter`: an iterator over trees. We may need to read more tokens from it in order to finish
 ///   converting `tree`
-/// - `parsing_patterns`: same as [parse].
+/// - `part`: same as [parse].
 /// - `sess`: the parsing session. Any errors will be emitted to this session.
 /// - `features`: language features so we can do feature gating.
 fn parse_tree<'a>(
     tree: &'a tokenstream::TokenTree,
     outer_iter: &mut TokenStreamIter<'a>,
-    parsing_patterns: bool,
+    part: RulePart,
     sess: &Session,
     node_id: NodeId,
     features: &Features,
@@ -189,7 +226,7 @@ fn parse_tree<'a>(
             match next {
                 // `tree` is followed by a delimited set of token trees.
                 Some(&tokenstream::TokenTree::Delimited(delim_span, _, delim, ref tts)) => {
-                    if parsing_patterns {
+                    if part.is_pattern() {
                         if delim != Delimiter::Parenthesis {
                             span_dollar_dollar_or_metavar_in_the_lhs_err(
                                 sess,
@@ -244,13 +281,13 @@ fn parse_tree<'a>(
                     // If we didn't find a metavar expression above, then we must have a
                     // repetition sequence in the macro (e.g. `$(pat)*`). Parse the
                     // contents of the sequence itself
-                    let sequence = parse(tts, parsing_patterns, sess, node_id, features, edition);
+                    let sequence = parse(tts, part, sess, node_id, features, edition);
                     // Get the Kleene operator and optional separator
                     let (separator, kleene) =
                         parse_sep_and_kleene_op(&mut iter, delim_span.entire(), sess);
                     // Count the number of captured "names" (i.e., named metavars)
                     let num_captures =
-                        if parsing_patterns { count_metavar_decls(&sequence) } else { 0 };
+                        if part.is_pattern() { count_metavar_decls(&sequence) } else { 0 };
                     TokenTree::Sequence(
                         delim_span,
                         SequenceRepetition { tts: sequence, separator, kleene, num_captures },
@@ -274,7 +311,7 @@ fn parse_tree<'a>(
                     Token { kind: token::Dollar, span: dollar_span2 },
                     _,
                 )) => {
-                    if parsing_patterns {
+                    if part.is_pattern() {
                         span_dollar_dollar_or_metavar_in_the_lhs_err(
                             sess,
                             &Token { kind: token::Dollar, span: dollar_span2 },
@@ -306,10 +343,7 @@ fn parse_tree<'a>(
         &tokenstream::TokenTree::Delimited(span, spacing, delim, ref tts) => TokenTree::Delimited(
             span,
             spacing,
-            Delimited {
-                delim,
-                tts: parse(tts, parsing_patterns, sess, node_id, features, edition),
-            },
+            Delimited { delim, tts: parse(tts, part, sess, node_id, features, edition) },
         ),
     }
 }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index ca6405ea209..559a771931e 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1303,6 +1303,7 @@ impl AttributeExt for Attribute {
             Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
+            Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
             a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index abbe497858b..87db80f2423 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -1239,7 +1239,7 @@ fn check_region_late_boundedness<'tcx>(
                 .unwrap_region_constraints()
                 .opportunistic_resolve_var(tcx, vid)
             && let ty::ReLateParam(ty::LateParamRegion {
-                kind: ty::LateParamRegionKind::Named(trait_param_def_id, _),
+                kind: ty::LateParamRegionKind::Named(trait_param_def_id),
                 ..
             }) = r.kind()
             && let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
@@ -1264,7 +1264,7 @@ fn check_region_late_boundedness<'tcx>(
                 .unwrap_region_constraints()
                 .opportunistic_resolve_var(tcx, vid)
             && let ty::ReLateParam(ty::LateParamRegion {
-                kind: ty::LateParamRegionKind::Named(impl_param_def_id, _),
+                kind: ty::LateParamRegionKind::Named(impl_param_def_id),
                 ..
             }) = r.kind()
             && let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
@@ -2468,7 +2468,7 @@ fn param_env_with_gat_bounds<'tcx>(
         let normalize_impl_ty_args = ty::GenericArgs::identity_for_item(tcx, container_id)
             .extend_to(tcx, impl_ty.def_id, |param, _| match param.kind {
                 GenericParamDefKind::Type { .. } => {
-                    let kind = ty::BoundTyKind::Param(param.def_id, param.name);
+                    let kind = ty::BoundTyKind::Param(param.def_id);
                     let bound_var = ty::BoundVariableKind::Ty(kind);
                     bound_vars.push(bound_var);
                     Ty::new_bound(
@@ -2479,7 +2479,7 @@ fn param_env_with_gat_bounds<'tcx>(
                     .into()
                 }
                 GenericParamDefKind::Lifetime => {
-                    let kind = ty::BoundRegionKind::Named(param.def_id, param.name);
+                    let kind = ty::BoundRegionKind::Named(param.def_id);
                     let bound_var = ty::BoundVariableKind::Region(kind);
                     bound_vars.push(bound_var);
                     ty::Region::new_bound(
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index c458878da15..288105dfba7 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -15,7 +15,6 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{Arm, Block, Expr, LetStmt, Pat, PatKind, Stmt};
 use rustc_index::Idx;
-use rustc_middle::bug;
 use rustc_middle::middle::region::*;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::lint;
@@ -34,14 +33,6 @@ struct Context {
 struct ScopeResolutionVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
 
-    // The number of expressions and patterns visited in the current body.
-    expr_and_pat_count: usize,
-    // When this is `true`, we record the `Scopes` we encounter
-    // when processing a Yield expression. This allows us to fix
-    // up their indices.
-    pessimistic_yield: bool,
-    // Stores scopes when `pessimistic_yield` is `true`.
-    fixup_scopes: Vec<Scope>,
     // The generated scope tree.
     scope_tree: ScopeTree,
 
@@ -199,19 +190,14 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir:
     visitor.cx = prev_cx;
 }
 
+#[tracing::instrument(level = "debug", skip(visitor))]
 fn resolve_pat<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
     // If this is a binding then record the lifetime of that binding.
     if let PatKind::Binding(..) = pat.kind {
         record_var_lifetime(visitor, pat.hir_id.local_id);
     }
 
-    debug!("resolve_pat - pre-increment {} pat = {:?}", visitor.expr_and_pat_count, pat);
-
     intravisit::walk_pat(visitor, pat);
-
-    visitor.expr_and_pat_count += 1;
-
-    debug!("resolve_pat - post-increment {} pat = {:?}", visitor.expr_and_pat_count, pat);
 }
 
 fn resolve_stmt<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, stmt: &'tcx hir::Stmt<'tcx>) {
@@ -243,68 +229,15 @@ fn resolve_stmt<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, stmt: &'tcx hi
     }
 }
 
+#[tracing::instrument(level = "debug", skip(visitor))]
 fn resolve_expr<'tcx>(
     visitor: &mut ScopeResolutionVisitor<'tcx>,
     expr: &'tcx hir::Expr<'tcx>,
     terminating: bool,
 ) {
-    debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr);
-
     let prev_cx = visitor.cx;
     visitor.enter_node_scope_with_dtor(expr.hir_id.local_id, terminating);
 
-    let prev_pessimistic = visitor.pessimistic_yield;
-
-    // Ordinarily, we can rely on the visit order of HIR intravisit
-    // to correspond to the actual execution order of statements.
-    // However, there's a weird corner case with compound assignment
-    // operators (e.g. `a += b`). The evaluation order depends on whether
-    // or not the operator is overloaded (e.g. whether or not a trait
-    // like AddAssign is implemented).
-
-    // For primitive types (which, despite having a trait impl, don't actually
-    // end up calling it), the evaluation order is right-to-left. For example,
-    // the following code snippet:
-    //
-    //    let y = &mut 0;
-    //    *{println!("LHS!"); y} += {println!("RHS!"); 1};
-    //
-    // will print:
-    //
-    // RHS!
-    // LHS!
-    //
-    // However, if the operator is used on a non-primitive type,
-    // the evaluation order will be left-to-right, since the operator
-    // actually get desugared to a method call. For example, this
-    // nearly identical code snippet:
-    //
-    //     let y = &mut String::new();
-    //    *{println!("LHS String"); y} += {println!("RHS String"); "hi"};
-    //
-    // will print:
-    // LHS String
-    // RHS String
-    //
-    // To determine the actual execution order, we need to perform
-    // trait resolution. Unfortunately, we need to be able to compute
-    // yield_in_scope before type checking is even done, as it gets
-    // used by AST borrowcheck.
-    //
-    // Fortunately, we don't need to know the actual execution order.
-    // It suffices to know the 'worst case' order with respect to yields.
-    // Specifically, we need to know the highest 'expr_and_pat_count'
-    // that we could assign to the yield expression. To do this,
-    // we pick the greater of the two values from the left-hand
-    // and right-hand expressions. This makes us overly conservative
-    // about what types could possibly live across yield points,
-    // but we will never fail to detect that a type does actually
-    // live across a yield point. The latter part is critical -
-    // we're already overly conservative about what types will live
-    // across yield points, as the generated MIR will determine
-    // when things are actually live. However, for typecheck to work
-    // properly, we can't miss any types.
-
     match expr.kind {
         // Conditional or repeating scopes are always terminating
         // scopes, meaning that temporaries cannot outlive them.
@@ -360,55 +293,42 @@ fn resolve_expr<'tcx>(
             let body = visitor.tcx.hir_body(body);
             visitor.visit_body(body);
         }
+        // Ordinarily, we can rely on the visit order of HIR intravisit
+        // to correspond to the actual execution order of statements.
+        // However, there's a weird corner case with compound assignment
+        // operators (e.g. `a += b`). The evaluation order depends on whether
+        // or not the operator is overloaded (e.g. whether or not a trait
+        // like AddAssign is implemented).
+        //
+        // For primitive types (which, despite having a trait impl, don't actually
+        // end up calling it), the evaluation order is right-to-left. For example,
+        // the following code snippet:
+        //
+        //    let y = &mut 0;
+        //    *{println!("LHS!"); y} += {println!("RHS!"); 1};
+        //
+        // will print:
+        //
+        // RHS!
+        // LHS!
+        //
+        // However, if the operator is used on a non-primitive type,
+        // the evaluation order will be left-to-right, since the operator
+        // actually get desugared to a method call. For example, this
+        // nearly identical code snippet:
+        //
+        //     let y = &mut String::new();
+        //    *{println!("LHS String"); y} += {println!("RHS String"); "hi"};
+        //
+        // will print:
+        // LHS String
+        // RHS String
+        //
+        // To determine the actual execution order, we need to perform
+        // trait resolution. Fortunately, we don't need to know the actual execution order.
         hir::ExprKind::AssignOp(_, left_expr, right_expr) => {
-            debug!(
-                "resolve_expr - enabling pessimistic_yield, was previously {}",
-                prev_pessimistic
-            );
-
-            let start_point = visitor.fixup_scopes.len();
-            visitor.pessimistic_yield = true;
-
-            // If the actual execution order turns out to be right-to-left,
-            // then we're fine. However, if the actual execution order is left-to-right,
-            // then we'll assign too low a count to any `yield` expressions
-            // we encounter in 'right_expression' - they should really occur after all of the
-            // expressions in 'left_expression'.
             visitor.visit_expr(right_expr);
-            visitor.pessimistic_yield = prev_pessimistic;
-
-            debug!("resolve_expr - restoring pessimistic_yield to {}", prev_pessimistic);
             visitor.visit_expr(left_expr);
-            debug!("resolve_expr - fixing up counts to {}", visitor.expr_and_pat_count);
-
-            // Remove and process any scopes pushed by the visitor
-            let target_scopes = visitor.fixup_scopes.drain(start_point..);
-
-            for scope in target_scopes {
-                let yield_data =
-                    visitor.scope_tree.yield_in_scope.get_mut(&scope).unwrap().last_mut().unwrap();
-                let count = yield_data.expr_and_pat_count;
-                let span = yield_data.span;
-
-                // expr_and_pat_count never decreases. Since we recorded counts in yield_in_scope
-                // before walking the left-hand side, it should be impossible for the recorded
-                // count to be greater than the left-hand side count.
-                if count > visitor.expr_and_pat_count {
-                    bug!(
-                        "Encountered greater count {} at span {:?} - expected no greater than {}",
-                        count,
-                        span,
-                        visitor.expr_and_pat_count
-                    );
-                }
-                let new_count = visitor.expr_and_pat_count;
-                debug!(
-                    "resolve_expr - increasing count for scope {:?} from {} to {} at span {:?}",
-                    scope, count, new_count, span
-                );
-
-                yield_data.expr_and_pat_count = new_count;
-            }
         }
 
         hir::ExprKind::If(cond, then, Some(otherwise)) => {
@@ -453,43 +373,6 @@ fn resolve_expr<'tcx>(
         _ => intravisit::walk_expr(visitor, expr),
     }
 
-    visitor.expr_and_pat_count += 1;
-
-    debug!("resolve_expr post-increment {}, expr = {:?}", visitor.expr_and_pat_count, expr);
-
-    if let hir::ExprKind::Yield(_, source) = &expr.kind {
-        // Mark this expr's scope and all parent scopes as containing `yield`.
-        let mut scope = Scope { local_id: expr.hir_id.local_id, data: ScopeData::Node };
-        loop {
-            let data = YieldData {
-                span: expr.span,
-                expr_and_pat_count: visitor.expr_and_pat_count,
-                source: *source,
-            };
-            match visitor.scope_tree.yield_in_scope.get_mut(&scope) {
-                Some(yields) => yields.push(data),
-                None => {
-                    visitor.scope_tree.yield_in_scope.insert(scope, vec![data]);
-                }
-            }
-
-            if visitor.pessimistic_yield {
-                debug!("resolve_expr in pessimistic_yield - marking scope {:?} for fixup", scope);
-                visitor.fixup_scopes.push(scope);
-            }
-
-            // Keep traversing up while we can.
-            match visitor.scope_tree.parent_map.get(&scope) {
-                // Don't cross from closure bodies to their parent.
-                Some(&superscope) => match superscope.data {
-                    ScopeData::CallSite => break,
-                    _ => scope = superscope,
-                },
-                None => break,
-            }
-        }
-    }
-
     visitor.cx = prev_cx;
 }
 
@@ -612,8 +495,8 @@ fn resolve_local<'tcx>(
         }
     }
 
-    // Make sure we visit the initializer first, so expr_and_pat_count remains correct.
-    // The correct order, as shared between coroutine_interior, drop_ranges and intravisitor,
+    // Make sure we visit the initializer first.
+    // The correct order, as shared between drop_ranges and intravisitor,
     // is to walk initializer, followed by pattern bindings, finally followed by the `else` block.
     if let Some(expr) = init {
         visitor.visit_expr(expr);
@@ -798,16 +681,7 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> {
     }
 
     fn enter_body(&mut self, hir_id: hir::HirId, f: impl FnOnce(&mut Self)) {
-        // Save all state that is specific to the outer function
-        // body. These will be restored once down below, once we've
-        // visited the body.
-        let outer_ec = mem::replace(&mut self.expr_and_pat_count, 0);
         let outer_cx = self.cx;
-        // The 'pessimistic yield' flag is set to true when we are
-        // processing a `+=` statement and have to make pessimistic
-        // control flow assumptions. This doesn't apply to nested
-        // bodies within the `+=` statements. See #69307.
-        let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
 
         self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::CallSite });
         self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::Arguments });
@@ -815,9 +689,7 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> {
         f(self);
 
         // Restore context we had at the start.
-        self.expr_and_pat_count = outer_ec;
         self.cx = outer_cx;
-        self.pessimistic_yield = outer_pessimistic_yield;
     }
 }
 
@@ -919,10 +791,7 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
         let mut visitor = ScopeResolutionVisitor {
             tcx,
             scope_tree: ScopeTree::default(),
-            expr_and_pat_count: 0,
             cx: Context { parent: None, var_parent: None },
-            pessimistic_yield: false,
-            fixup_scopes: vec![],
             extended_super_lets: Default::default(),
         };
 
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 4934136bc7a..0a3e018b79a 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -2338,7 +2338,7 @@ fn lint_redundant_lifetimes<'tcx>(
             lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind));
         }
     }
-    lifetimes.retain(|candidate| candidate.has_name());
+    lifetimes.retain(|candidate| candidate.is_named(tcx));
 
     // Keep track of lifetimes which have already been replaced with other lifetimes.
     // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 65bc441a473..8356a0af63c 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -654,7 +654,7 @@ fn infringing_fields_error<'tcx>(
                                 .or_default()
                                 .push(origin.span());
                             if let ty::RegionKind::ReEarlyParam(ebr) = b.kind()
-                                && ebr.has_name()
+                                && ebr.is_named()
                             {
                                 bounds.push((b.to_string(), a.to_string(), None));
                             }
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 271104c20c6..25064c327d0 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -578,13 +578,7 @@ fn get_new_lifetime_name<'tcx>(
     let existing_lifetimes = tcx
         .collect_referenced_late_bound_regions(poly_trait_ref)
         .into_iter()
-        .filter_map(|lt| {
-            if let ty::BoundRegionKind::Named(_, name) = lt {
-                Some(name.as_str().to_string())
-            } else {
-                None
-            }
-        })
+        .filter_map(|lt| lt.get_name(tcx).map(|name| name.as_str().to_string()))
         .chain(generics.params.iter().filter_map(|param| {
             if let hir::GenericParamKind::Lifetime { .. } = &param.kind {
                 Some(param.name.ident().as_str().to_string())
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 0e775d374ab..a0d1273eb85 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -279,19 +279,13 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou
     rbv
 }
 
-fn late_arg_as_bound_arg<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    param: &GenericParam<'tcx>,
-) -> ty::BoundVariableKind {
+fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableKind {
     let def_id = param.def_id.to_def_id();
-    let name = tcx.item_name(def_id);
     match param.kind {
         GenericParamKind::Lifetime { .. } => {
-            ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id, name))
-        }
-        GenericParamKind::Type { .. } => {
-            ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, name))
+            ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id))
         }
+        GenericParamKind::Type { .. } => ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)),
         GenericParamKind::Const { .. } => ty::BoundVariableKind::Const,
     }
 }
@@ -302,10 +296,10 @@ fn late_arg_as_bound_arg<'tcx>(
 fn generic_param_def_as_bound_arg(param: &ty::GenericParamDef) -> ty::BoundVariableKind {
     match param.kind {
         ty::GenericParamDefKind::Lifetime => {
-            ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(param.def_id, param.name))
+            ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(param.def_id))
         }
         ty::GenericParamDefKind::Type { .. } => {
-            ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id, param.name))
+            ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id))
         }
         ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
     }
@@ -386,7 +380,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
             trait_ref.bound_generic_params.iter().enumerate().map(|(late_bound_idx, param)| {
                 let arg = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param);
                 bound_vars.insert(param.def_id, arg);
-                late_arg_as_bound_arg(self.tcx, param)
+                late_arg_as_bound_arg(param)
             });
         binders.extend(binders_iter);
 
@@ -485,7 +479,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     .map(|(late_bound_idx, param)| {
                         (
                             (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
-                            late_arg_as_bound_arg(self.tcx, param),
+                            late_arg_as_bound_arg(param),
                         )
                     })
                     .unzip();
@@ -718,7 +712,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     .map(|(late_bound_idx, param)| {
                         (
                             (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
-                            late_arg_as_bound_arg(self.tcx, param),
+                            late_arg_as_bound_arg(param),
                         )
                     })
                     .unzip();
@@ -748,7 +742,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                         .map(|(late_bound_idx, param)| {
                             (
                                 (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
-                                late_arg_as_bound_arg(self.tcx, param),
+                                late_arg_as_bound_arg(param),
                             )
                         })
                         .unzip();
@@ -957,7 +951,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                         .map(|(late_bound_idx, param)| {
                             (
                                 (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
-                                late_arg_as_bound_arg(self.tcx, param),
+                                late_arg_as_bound_arg(param),
                             )
                         })
                         .unzip();
@@ -1171,7 +1165,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                 matches!(param.kind, GenericParamKind::Lifetime { .. })
                     && self.tcx.is_late_bound(param.hir_id)
             })
-            .map(|param| late_arg_as_bound_arg(self.tcx, param))
+            .map(|param| late_arg_as_bound_arg(param))
             .collect();
         self.record_late_bound_vars(hir_id, binders);
         let scope = Scope::Binder {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index d17986d45d2..4784cfb5235 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -12,7 +12,7 @@ use rustc_middle::ty::{
     self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
     TypeVisitor, Upcast,
 };
-use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
+use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
 use rustc_trait_selection::traits;
 use smallvec::SmallVec;
 use tracing::{debug, instrument};
@@ -888,7 +888,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     ty::INNERMOST,
                     ty::BoundRegion {
                         var: ty::BoundVar::from_usize(num_bound_vars),
-                        kind: ty::BoundRegionKind::Named(param.def_id, param.name),
+                        kind: ty::BoundRegionKind::Named(param.def_id),
                     },
                 )
                 .into(),
@@ -1006,12 +1006,12 @@ fn check_assoc_const_binding_type<'tcx>(
             ty_note,
         }));
     }
-    for (var_def_id, var_name) in collector.vars {
+    for var_def_id in collector.vars {
         guar.get_or_insert(cx.dcx().emit_err(
             crate::errors::EscapingBoundVarInTyOfAssocConstBinding {
                 span: assoc_const.span,
                 assoc_const,
-                var_name,
+                var_name: cx.tcx().item_name(var_def_id),
                 var_def_kind: tcx.def_descr(var_def_id),
                 var_defined_here_label: tcx.def_ident_span(var_def_id).unwrap(),
                 ty_note,
@@ -1026,7 +1026,7 @@ fn check_assoc_const_binding_type<'tcx>(
 struct GenericParamAndBoundVarCollector<'a, 'tcx> {
     cx: &'a dyn HirTyLowerer<'tcx>,
     params: FxIndexSet<u32>,
-    vars: FxIndexSet<(DefId, Symbol)>,
+    vars: FxIndexSet<DefId>,
     depth: ty::DebruijnIndex,
 }
 
@@ -1050,7 +1050,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 't
             }
             ty::Bound(db, bt) if *db >= self.depth => {
                 self.vars.insert(match bt.kind {
-                    ty::BoundTyKind::Param(def_id, name) => (def_id, name),
+                    ty::BoundTyKind::Param(def_id) => def_id,
                     ty::BoundTyKind::Anon => {
                         let reported = self
                             .cx
@@ -1073,7 +1073,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 't
             }
             ty::ReBound(db, br) if db >= self.depth => {
                 self.vars.insert(match br.kind {
-                    ty::BoundRegionKind::Named(def_id, name) => (def_id, name),
+                    ty::BoundRegionKind::Named(def_id) => def_id,
                     ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => {
                         let guar = self
                             .cx
@@ -1081,6 +1081,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 't
                             .delayed_bug(format!("unexpected bound region kind: {:?}", br.kind));
                         return ControlFlow::Break(guar);
                     }
+                    ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
                 });
             }
             _ => {}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index e13daabeb50..434375060df 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -392,16 +392,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     #[instrument(level = "debug", skip(self), ret)]
     pub fn lower_resolved_lifetime(&self, resolved: rbv::ResolvedArg) -> ty::Region<'tcx> {
         let tcx = self.tcx();
-        let lifetime_name = |def_id| tcx.hir_name(tcx.local_def_id_to_hir_id(def_id));
 
         match resolved {
             rbv::ResolvedArg::StaticLifetime => tcx.lifetimes.re_static,
 
             rbv::ResolvedArg::LateBound(debruijn, index, def_id) => {
-                let name = lifetime_name(def_id);
                 let br = ty::BoundRegion {
                     var: ty::BoundVar::from_u32(index),
-                    kind: ty::BoundRegionKind::Named(def_id.to_def_id(), name),
+                    kind: ty::BoundRegionKind::Named(def_id.to_def_id()),
                 };
                 ty::Region::new_bound(tcx, debruijn, br)
             }
@@ -415,11 +413,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
 
             rbv::ResolvedArg::Free(scope, id) => {
-                let name = lifetime_name(id);
                 ty::Region::new_late_param(
                     tcx,
                     scope.to_def_id(),
-                    ty::LateParamRegionKind::Named(id.to_def_id(), name),
+                    ty::LateParamRegionKind::Named(id.to_def_id()),
                 )
 
                 // (*) -- not late-bound, won't change
@@ -2070,10 +2067,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let tcx = self.tcx();
         match tcx.named_bound_var(hir_id) {
             Some(rbv::ResolvedArg::LateBound(debruijn, index, def_id)) => {
-                let name = tcx.item_name(def_id.to_def_id());
                 let br = ty::BoundTy {
                     var: ty::BoundVar::from_u32(index),
-                    kind: ty::BoundTyKind::Param(def_id.to_def_id(), name),
+                    kind: ty::BoundTyKind::Param(def_id.to_def_id()),
                 };
                 Ty::new_bound(tcx, debruijn, br)
             }
@@ -2749,18 +2745,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         generate_err: impl Fn(&str) -> Diag<'cx>,
     ) {
         for br in referenced_regions.difference(&constrained_regions) {
-            let br_name = match *br {
-                ty::BoundRegionKind::Named(_, kw::UnderscoreLifetime)
-                | ty::BoundRegionKind::Anon
-                | ty::BoundRegionKind::ClosureEnv => "an anonymous lifetime".to_string(),
-                ty::BoundRegionKind::Named(_, name) => format!("lifetime `{name}`"),
+            let br_name = if let Some(name) = br.get_name(self.tcx()) {
+                format!("lifetime `{name}`")
+            } else {
+                "an anonymous lifetime".to_string()
             };
 
             let mut err = generate_err(&br_name);
 
-            if let ty::BoundRegionKind::Named(_, kw::UnderscoreLifetime)
-            | ty::BoundRegionKind::Anon = *br
-            {
+            if !br.is_named(self.tcx()) {
                 // The only way for an anonymous lifetime to wind up
                 // in the return type but **also** be unconstrained is
                 // if it only appears in "associated types" in the
diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
index f4cb73685d5..a1744b4df80 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
@@ -655,7 +655,7 @@ impl<'tcx> fmt::Display for GenericKind<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
             GenericKind::Param(ref p) => write!(f, "{p}"),
-            GenericKind::Placeholder(ref p) => write!(f, "{p:?}"),
+            GenericKind::Placeholder(ref p) => write!(f, "{p}"),
             GenericKind::Alias(ref p) => write!(f, "{p}"),
         }
     }
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index aa6f36a67f0..c17281deff4 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -136,7 +136,7 @@ enum ParamKind {
     // Early-bound var.
     Early(Symbol, u32),
     // Late-bound var on function, not within a binder. We can capture these.
-    Free(DefId, Symbol),
+    Free(DefId),
     // Late-bound var in a binder. We can't capture these yet.
     Late,
 }
@@ -156,12 +156,11 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
     }
 
     for bound_var in sig.bound_vars() {
-        let ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id, name)) = bound_var
-        else {
+        let ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id)) = bound_var else {
             span_bug!(tcx.def_span(parent_def_id), "unexpected non-lifetime binder on fn sig");
         };
 
-        in_scope_parameters.insert(def_id, ParamKind::Free(def_id, name));
+        in_scope_parameters.insert(def_id, ParamKind::Free(def_id));
     }
 
     let sig = tcx.liberate_late_bound_regions(parent_def_id.to_def_id(), sig);
@@ -215,8 +214,8 @@ where
         for arg in t.bound_vars() {
             let arg: ty::BoundVariableKind = arg;
             match arg {
-                ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id, ..))
-                | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, _)) => {
+                ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id))
+                | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)) => {
                     added.push(def_id);
                     let unique = self.in_scope_parameters.insert(def_id, ParamKind::Late);
                     assert_eq!(unique, None);
@@ -316,10 +315,10 @@ where
                             self.tcx,
                             ty::EarlyParamRegion { name, index },
                         ),
-                        ParamKind::Free(def_id, name) => ty::Region::new_late_param(
+                        ParamKind::Free(def_id) => ty::Region::new_late_param(
                             self.tcx,
                             self.parent_def_id.to_def_id(),
-                            ty::LateParamRegionKind::Named(def_id, name),
+                            ty::LateParamRegionKind::Named(def_id),
                         ),
                         // Totally ignore late bound args from binders.
                         ParamKind::Late => return true,
@@ -463,13 +462,10 @@ fn extract_def_id_from_arg<'tcx>(
     match arg.kind() {
         ty::GenericArgKind::Lifetime(re) => match re.kind() {
             ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id,
-            ty::ReBound(
-                _,
-                ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id, ..), .. },
-            )
+            ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id), .. })
             | ty::ReLateParam(ty::LateParamRegion {
                 scope: _,
-                kind: ty::LateParamRegionKind::Named(def_id, ..),
+                kind: ty::LateParamRegionKind::Named(def_id),
             }) => def_id,
             _ => unreachable!(),
         },
@@ -532,13 +528,10 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for FunctionalVariances<'tcx> {
     ) -> RelateResult<'tcx, ty::Region<'tcx>> {
         let def_id = match a.kind() {
             ty::ReEarlyParam(ebr) => self.generics.region_param(ebr, self.tcx).def_id,
-            ty::ReBound(
-                _,
-                ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id, ..), .. },
-            )
+            ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id), .. })
             | ty::ReLateParam(ty::LateParamRegion {
                 scope: _,
-                kind: ty::LateParamRegionKind::Named(def_id, ..),
+                kind: ty::LateParamRegionKind::Named(def_id),
             }) => def_id,
             _ => {
                 return Ok(a);
diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs
index 3b27e456136..af509cb786d 100644
--- a/compiler/rustc_lint/src/map_unit_fn.rs
+++ b/compiler/rustc_lint/src/map_unit_fn.rs
@@ -1,5 +1,4 @@
 use rustc_hir::{Expr, ExprKind, HirId, Stmt, StmtKind};
-use rustc_middle::query::Key;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint, declare_lint_pass};
 
@@ -69,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn {
                                         .span_of_impl(*id)
                                         .unwrap_or(default_span),
                                     argument_label: args[0].span,
-                                    map_label: arg_ty.default_span(cx.tcx),
+                                    map_label: span,
                                     suggestion: path.ident.span,
                                     replace: "for_each".to_string(),
                                 },
@@ -88,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn {
                                         .span_of_impl(*id)
                                         .unwrap_or(default_span),
                                     argument_label: args[0].span,
-                                    map_label: arg_ty.default_span(cx.tcx),
+                                    map_label: span,
                                     suggestion: path.ident.span,
                                     replace: "for_each".to_string(),
                                 },
diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs
index 92eab59dd02..0f5b63f5c1d 100644
--- a/compiler/rustc_middle/src/middle/region.rs
+++ b/compiler/rustc_middle/src/middle/region.rs
@@ -7,7 +7,6 @@
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html
 
 use std::fmt;
-use std::ops::Deref;
 
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::unord::UnordMap;
@@ -228,82 +227,6 @@ pub struct ScopeTree {
     /// This information is used later for linting to identify locals and
     /// temporary values that will receive backwards-incompatible drop orders.
     pub backwards_incompatible_scope: UnordMap<hir::ItemLocalId, Scope>,
-
-    /// If there are any `yield` nested within a scope, this map
-    /// stores the `Span` of the last one and its index in the
-    /// postorder of the Visitor traversal on the HIR.
-    ///
-    /// HIR Visitor postorder indexes might seem like a peculiar
-    /// thing to care about. but it turns out that HIR bindings
-    /// and the temporary results of HIR expressions are never
-    /// storage-live at the end of HIR nodes with postorder indexes
-    /// lower than theirs, and therefore don't need to be suspended
-    /// at yield-points at these indexes.
-    ///
-    /// For an example, suppose we have some code such as:
-    /// ```rust,ignore (example)
-    ///     foo(f(), yield y, bar(g()))
-    /// ```
-    ///
-    /// With the HIR tree (calls numbered for expository purposes)
-    ///
-    /// ```text
-    ///     Call#0(foo, [Call#1(f), Yield(y), Call#2(bar, Call#3(g))])
-    /// ```
-    ///
-    /// Obviously, the result of `f()` was created before the yield
-    /// (and therefore needs to be kept valid over the yield) while
-    /// the result of `g()` occurs after the yield (and therefore
-    /// doesn't). If we want to infer that, we can look at the
-    /// postorder traversal:
-    /// ```plain,ignore
-    ///     `foo` `f` Call#1 `y` Yield `bar` `g` Call#3 Call#2 Call#0
-    /// ```
-    ///
-    /// In which we can easily see that `Call#1` occurs before the yield,
-    /// and `Call#3` after it.
-    ///
-    /// To see that this method works, consider:
-    ///
-    /// Let `D` be our binding/temporary and `U` be our other HIR node, with
-    /// `HIR-postorder(U) < HIR-postorder(D)`. Suppose, as in our example,
-    /// U is the yield and D is one of the calls.
-    /// Let's show that `D` is storage-dead at `U`.
-    ///
-    /// Remember that storage-live/storage-dead refers to the state of
-    /// the *storage*, and does not consider moves/drop flags.
-    ///
-    /// Then:
-    ///
-    ///   1. From the ordering guarantee of HIR visitors (see
-    ///   `rustc_hir::intravisit`), `D` does not dominate `U`.
-    ///
-    ///   2. Therefore, `D` is *potentially* storage-dead at `U` (because
-    ///   we might visit `U` without ever getting to `D`).
-    ///
-    ///   3. However, we guarantee that at each HIR point, each
-    ///   binding/temporary is always either always storage-live
-    ///   or always storage-dead. This is what is being guaranteed
-    ///   by `terminating_scopes` including all blocks where the
-    ///   count of executions is not guaranteed.
-    ///
-    ///   4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`,
-    ///   QED.
-    ///
-    /// This property ought to not on (3) in an essential way -- it
-    /// is probably still correct even if we have "unrestricted" terminating
-    /// scopes. However, why use the complicated proof when a simple one
-    /// works?
-    ///
-    /// A subtle thing: `box` expressions, such as `box (&x, yield 2, &y)`. It
-    /// might seem that a `box` expression creates a `Box<T>` temporary
-    /// when it *starts* executing, at `HIR-preorder(BOX-EXPR)`. That might
-    /// be true in the MIR desugaring, but it is not important in the semantics.
-    ///
-    /// The reason is that semantically, until the `box` expression returns,
-    /// the values are still owned by their containing expressions. So
-    /// we'll see that `&x`.
-    pub yield_in_scope: UnordMap<Scope, Vec<YieldData>>,
 }
 
 /// See the `rvalue_candidates` field for more information on rvalue
@@ -316,15 +239,6 @@ pub struct RvalueCandidate {
     pub lifetime: Option<Scope>,
 }
 
-#[derive(Debug, Copy, Clone, HashStable)]
-pub struct YieldData {
-    /// The `Span` of the yield.
-    pub span: Span,
-    /// The number of expressions and patterns appearing before the `yield` in the body, plus one.
-    pub expr_and_pat_count: usize,
-    pub source: hir::YieldSource,
-}
-
 impl ScopeTree {
     pub fn record_scope_parent(&mut self, child: Scope, parent: Option<Scope>) {
         debug!("{:?}.parent = {:?}", child, parent);
@@ -380,10 +294,4 @@ impl ScopeTree {
 
         true
     }
-
-    /// Checks whether the given scope contains a `yield`. If so,
-    /// returns `Some(YieldData)`. If not, returns `None`.
-    pub fn yield_in_scope(&self, scope: Scope) -> Option<&[YieldData]> {
-        self.yield_in_scope.get(&scope).map(Deref::deref)
-    }
 }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index e8b15b76fd8..98b2ce01d89 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -3279,10 +3279,7 @@ impl<'tcx> TyCtxt<'tcx> {
                     return ty::Region::new_late_param(
                         self,
                         new_parent.to_def_id(),
-                        ty::LateParamRegionKind::Named(
-                            lbv.to_def_id(),
-                            self.item_name(lbv.to_def_id()),
-                        ),
+                        ty::LateParamRegionKind::Named(lbv.to_def_id()),
                     );
                 }
                 resolve_bound_vars::ResolvedArg::Error(guar) => {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index a92d6fe3916..f1b16ea54e6 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -474,7 +474,7 @@ impl<'tcx> rustc_type_ir::Flags for Ty<'tcx> {
 impl EarlyParamRegion {
     /// Does this early bound region have a name? Early bound regions normally
     /// always have names except when using anonymous lifetimes (`'_`).
-    pub fn has_name(&self) -> bool {
+    pub fn is_named(&self) -> bool {
         self.name != kw::UnderscoreLifetime
     }
 }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index b4c4f48a0a6..1dba4a7b040 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -11,7 +11,7 @@ use rustc_data_structures::unord::UnordMap;
 use rustc_hir as hir;
 use rustc_hir::LangItem;
 use rustc_hir::def::{self, CtorKind, DefKind, Namespace};
-use rustc_hir::def_id::{CRATE_DEF_ID, DefIdMap, DefIdSet, LOCAL_CRATE, ModDefId};
+use rustc_hir::def_id::{DefIdMap, DefIdSet, LOCAL_CRATE, ModDefId};
 use rustc_hir::definitions::{DefKey, DefPathDataName};
 use rustc_macros::{Lift, extension};
 use rustc_session::Limit;
@@ -795,9 +795,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                 ty::BoundTyKind::Anon => {
                     rustc_type_ir::debug_bound_var(self, debruijn, bound_ty.var)?
                 }
-                ty::BoundTyKind::Param(_, s) => match self.should_print_verbose() {
+                ty::BoundTyKind::Param(def_id) => match self.should_print_verbose() {
                     true => p!(write("{:?}", ty.kind())),
-                    false => p!(write("{s}")),
+                    false => p!(write("{}", self.tcx().item_name(def_id))),
                 },
             },
             ty::Adt(def, args) => {
@@ -822,13 +822,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             ty::Alias(ty::Projection | ty::Inherent | ty::Free, ref data) => {
                 p!(print(data))
             }
-            ty::Placeholder(placeholder) => match placeholder.bound.kind {
-                ty::BoundTyKind::Anon => p!(write("{placeholder:?}")),
-                ty::BoundTyKind::Param(_, name) => match self.should_print_verbose() {
-                    true => p!(write("{:?}", ty.kind())),
-                    false => p!(write("{name}")),
-                },
-            },
+            ty::Placeholder(placeholder) => p!(print(placeholder)),
             ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
                 // We use verbose printing in 'NO_QUERIES' mode, to
                 // avoid needing to call `predicates_of`. This should
@@ -2551,14 +2545,14 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
         let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions;
 
         match region.kind() {
-            ty::ReEarlyParam(ref data) => data.has_name(),
+            ty::ReEarlyParam(ref data) => data.is_named(),
 
-            ty::ReLateParam(ty::LateParamRegion { kind, .. }) => kind.is_named(),
+            ty::ReLateParam(ty::LateParamRegion { kind, .. }) => kind.is_named(self.tcx),
             ty::ReBound(_, ty::BoundRegion { kind: br, .. })
             | ty::RePlaceholder(ty::Placeholder {
                 bound: ty::BoundRegion { kind: br, .. }, ..
             }) => {
-                if br.is_named() {
+                if br.is_named(self.tcx) {
                     return true;
                 }
 
@@ -2626,7 +2620,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
                 return Ok(());
             }
             ty::ReLateParam(ty::LateParamRegion { kind, .. }) => {
-                if let Some(name) = kind.get_name() {
+                if let Some(name) = kind.get_name(self.tcx) {
                     p!(write("{}", name));
                     return Ok(());
                 }
@@ -2635,9 +2629,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
             | ty::RePlaceholder(ty::Placeholder {
                 bound: ty::BoundRegion { kind: br, .. }, ..
             }) => {
-                if let ty::BoundRegionKind::Named(_, name) = br
-                    && br.is_named()
-                {
+                if let Some(name) = br.get_name(self.tcx) {
                     p!(write("{}", name));
                     return Ok(());
                 }
@@ -2844,55 +2836,22 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
             let mut name = |lifetime_idx: Option<ty::DebruijnIndex>,
                             binder_level_idx: ty::DebruijnIndex,
                             br: ty::BoundRegion| {
-                let (name, kind) = match br.kind {
-                    ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => {
-                        let name = next_name(self);
-
-                        if let Some(lt_idx) = lifetime_idx {
-                            if lt_idx > binder_level_idx {
-                                let kind =
-                                    ty::BoundRegionKind::Named(CRATE_DEF_ID.to_def_id(), name);
-                                return ty::Region::new_bound(
-                                    tcx,
-                                    ty::INNERMOST,
-                                    ty::BoundRegion { var: br.var, kind },
-                                );
-                            }
-                        }
-
-                        (name, ty::BoundRegionKind::Named(CRATE_DEF_ID.to_def_id(), name))
-                    }
-                    ty::BoundRegionKind::Named(def_id, kw::UnderscoreLifetime) => {
-                        let name = next_name(self);
-
-                        if let Some(lt_idx) = lifetime_idx {
-                            if lt_idx > binder_level_idx {
-                                let kind = ty::BoundRegionKind::Named(def_id, name);
-                                return ty::Region::new_bound(
-                                    tcx,
-                                    ty::INNERMOST,
-                                    ty::BoundRegion { var: br.var, kind },
-                                );
-                            }
-                        }
-
-                        (name, ty::BoundRegionKind::Named(def_id, name))
-                    }
-                    ty::BoundRegionKind::Named(_, name) => {
-                        if let Some(lt_idx) = lifetime_idx {
-                            if lt_idx > binder_level_idx {
-                                let kind = br.kind;
-                                return ty::Region::new_bound(
-                                    tcx,
-                                    ty::INNERMOST,
-                                    ty::BoundRegion { var: br.var, kind },
-                                );
-                            }
-                        }
+                let (name, kind) = if let Some(name) = br.kind.get_name(tcx) {
+                    (name, br.kind)
+                } else {
+                    let name = next_name(self);
+                    (name, ty::BoundRegionKind::NamedAnon(name))
+                };
 
-                        (name, br.kind)
+                if let Some(lt_idx) = lifetime_idx {
+                    if lt_idx > binder_level_idx {
+                        return ty::Region::new_bound(
+                            tcx,
+                            ty::INNERMOST,
+                            ty::BoundRegion { var: br.var, kind },
+                        );
                     }
-                };
+                }
 
                 // Unconditionally render `unsafe<>`.
                 if !trim_path || mode == WrapBinderMode::Unsafe {
@@ -2960,13 +2919,15 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
         T: TypeFoldable<TyCtxt<'tcx>>,
     {
         struct RegionNameCollector<'tcx> {
+            tcx: TyCtxt<'tcx>,
             used_region_names: FxHashSet<Symbol>,
             type_collector: SsoHashSet<Ty<'tcx>>,
         }
 
         impl<'tcx> RegionNameCollector<'tcx> {
-            fn new() -> Self {
+            fn new(tcx: TyCtxt<'tcx>) -> Self {
                 RegionNameCollector {
+                    tcx,
                     used_region_names: Default::default(),
                     type_collector: SsoHashSet::new(),
                 }
@@ -2980,7 +2941,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
                 // Collect all named lifetimes. These allow us to prevent duplication
                 // of already existing lifetime names when introducing names for
                 // anonymous late-bound regions.
-                if let Some(name) = r.get_name() {
+                if let Some(name) = r.get_name(self.tcx) {
                     self.used_region_names.insert(name);
                 }
             }
@@ -2995,7 +2956,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
             }
         }
 
-        let mut collector = RegionNameCollector::new();
+        let mut collector = RegionNameCollector::new(self.tcx());
         value.visit_with(&mut collector);
         self.used_region_names = collector.used_region_names;
         self.region_index = 0;
@@ -3406,6 +3367,16 @@ define_print_and_forward_display! {
         p!(write("{}", self.name))
     }
 
+    ty::PlaceholderType {
+        match self.bound.kind {
+            ty::BoundTyKind::Anon => p!(write("{self:?}")),
+            ty::BoundTyKind::Param(def_id) => match cx.should_print_verbose() {
+                true => p!(write("{self:?}")),
+                false => p!(write("{}", cx.tcx().item_name(def_id))),
+            },
+        }
+    }
+
     ty::ParamConst {
         p!(write("{}", self.name))
     }
diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs
index cc25cd16567..51be93d9a72 100644
--- a/compiler/rustc_middle/src/ty/region.rs
+++ b/compiler/rustc_middle/src/ty/region.rs
@@ -163,37 +163,33 @@ impl<'tcx> Region<'tcx> {
         *self.0.0
     }
 
-    pub fn get_name(self) -> Option<Symbol> {
-        if self.has_name() {
-            match self.kind() {
-                ty::ReEarlyParam(ebr) => Some(ebr.name),
-                ty::ReBound(_, br) => br.kind.get_name(),
-                ty::ReLateParam(fr) => fr.kind.get_name(),
-                ty::ReStatic => Some(kw::StaticLifetime),
-                ty::RePlaceholder(placeholder) => placeholder.bound.kind.get_name(),
-                _ => None,
-            }
-        } else {
-            None
+    pub fn get_name(self, tcx: TyCtxt<'tcx>) -> Option<Symbol> {
+        match self.kind() {
+            ty::ReEarlyParam(ebr) => ebr.is_named().then_some(ebr.name),
+            ty::ReBound(_, br) => br.kind.get_name(tcx),
+            ty::ReLateParam(fr) => fr.kind.get_name(tcx),
+            ty::ReStatic => Some(kw::StaticLifetime),
+            ty::RePlaceholder(placeholder) => placeholder.bound.kind.get_name(tcx),
+            _ => None,
         }
     }
 
-    pub fn get_name_or_anon(self) -> Symbol {
-        match self.get_name() {
+    pub fn get_name_or_anon(self, tcx: TyCtxt<'tcx>) -> Symbol {
+        match self.get_name(tcx) {
             Some(name) => name,
             None => sym::anon,
         }
     }
 
     /// Is this region named by the user?
-    pub fn has_name(self) -> bool {
+    pub fn is_named(self, tcx: TyCtxt<'tcx>) -> bool {
         match self.kind() {
-            ty::ReEarlyParam(ebr) => ebr.has_name(),
-            ty::ReBound(_, br) => br.kind.is_named(),
-            ty::ReLateParam(fr) => fr.kind.is_named(),
+            ty::ReEarlyParam(ebr) => ebr.is_named(),
+            ty::ReBound(_, br) => br.kind.is_named(tcx),
+            ty::ReLateParam(fr) => fr.kind.is_named(tcx),
             ty::ReStatic => true,
             ty::ReVar(..) => false,
-            ty::RePlaceholder(placeholder) => placeholder.bound.kind.is_named(),
+            ty::RePlaceholder(placeholder) => placeholder.bound.kind.is_named(tcx),
             ty::ReErased => false,
             ty::ReError(_) => false,
         }
@@ -313,7 +309,7 @@ impl<'tcx> Region<'tcx> {
                 Some(tcx.generics_of(binding_item).region_param(ebr, tcx).def_id)
             }
             ty::ReLateParam(ty::LateParamRegion {
-                kind: ty::LateParamRegionKind::Named(def_id, _),
+                kind: ty::LateParamRegionKind::Named(def_id),
                 ..
             }) => Some(def_id),
             _ => None,
@@ -371,11 +367,13 @@ pub enum LateParamRegionKind {
     /// sake of diagnostics in `FnCtxt::sig_of_closure_with_expectation`.
     Anon(u32),
 
-    /// Named region parameters for functions (a in &'a T)
+    /// An anonymous region parameter with a `Symbol` name.
     ///
-    /// The `DefId` is needed to distinguish free regions in
-    /// the event of shadowing.
-    Named(DefId, Symbol),
+    /// Used to give late-bound regions names for things like pretty printing.
+    NamedAnon(u32, Symbol),
+
+    /// Late-bound regions that appear in the AST.
+    Named(DefId),
 
     /// Anonymous region for the implicit env pointer parameter
     /// to a closure
@@ -386,32 +384,30 @@ impl LateParamRegionKind {
     pub fn from_bound(var: BoundVar, br: BoundRegionKind) -> LateParamRegionKind {
         match br {
             BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()),
-            BoundRegionKind::Named(def_id, name) => LateParamRegionKind::Named(def_id, name),
+            BoundRegionKind::Named(def_id) => LateParamRegionKind::Named(def_id),
             BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv,
+            BoundRegionKind::NamedAnon(name) => LateParamRegionKind::NamedAnon(var.as_u32(), name),
         }
     }
 
-    pub fn is_named(&self) -> bool {
-        match *self {
-            LateParamRegionKind::Named(_, name) => name != kw::UnderscoreLifetime,
-            _ => false,
-        }
+    pub fn is_named(&self, tcx: TyCtxt<'_>) -> bool {
+        self.get_name(tcx).is_some()
     }
 
-    pub fn get_name(&self) -> Option<Symbol> {
-        if self.is_named() {
-            match *self {
-                LateParamRegionKind::Named(_, name) => return Some(name),
-                _ => unreachable!(),
+    pub fn get_name(&self, tcx: TyCtxt<'_>) -> Option<Symbol> {
+        match *self {
+            LateParamRegionKind::Named(def_id) => {
+                let name = tcx.item_name(def_id);
+                if name != kw::UnderscoreLifetime { Some(name) } else { None }
             }
+            LateParamRegionKind::NamedAnon(_, name) => Some(name),
+            _ => None,
         }
-
-        None
     }
 
     pub fn get_id(&self) -> Option<DefId> {
         match *self {
-            LateParamRegionKind::Named(id, _) => Some(id),
+            LateParamRegionKind::Named(id) => Some(id),
             _ => None,
         }
     }
@@ -423,11 +419,13 @@ pub enum BoundRegionKind {
     /// An anonymous region parameter for a given fn (&T)
     Anon,
 
-    /// Named region parameters for functions (a in &'a T)
+    /// An anonymous region parameter with a `Symbol` name.
     ///
-    /// The `DefId` is needed to distinguish free regions in
-    /// the event of shadowing.
-    Named(DefId, Symbol),
+    /// Used to give late-bound regions names for things like pretty printing.
+    NamedAnon(Symbol),
+
+    /// Late-bound regions that appear in the AST.
+    Named(DefId),
 
     /// Anonymous region for the implicit env pointer parameter
     /// to a closure
@@ -456,35 +454,35 @@ impl core::fmt::Debug for BoundRegion {
         match self.kind {
             BoundRegionKind::Anon => write!(f, "{:?}", self.var),
             BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var),
-            BoundRegionKind::Named(def, symbol) => {
-                write!(f, "{:?}.Named({:?}, {:?})", self.var, def, symbol)
+            BoundRegionKind::Named(def) => {
+                write!(f, "{:?}.Named({:?})", self.var, def)
+            }
+            BoundRegionKind::NamedAnon(symbol) => {
+                write!(f, "{:?}.NamedAnon({:?})", self.var, symbol)
             }
         }
     }
 }
 
 impl BoundRegionKind {
-    pub fn is_named(&self) -> bool {
-        match *self {
-            BoundRegionKind::Named(_, name) => name != kw::UnderscoreLifetime,
-            _ => false,
-        }
+    pub fn is_named(&self, tcx: TyCtxt<'_>) -> bool {
+        self.get_name(tcx).is_some()
     }
 
-    pub fn get_name(&self) -> Option<Symbol> {
-        if self.is_named() {
-            match *self {
-                BoundRegionKind::Named(_, name) => return Some(name),
-                _ => unreachable!(),
+    pub fn get_name(&self, tcx: TyCtxt<'_>) -> Option<Symbol> {
+        match *self {
+            BoundRegionKind::Named(def_id) => {
+                let name = tcx.item_name(def_id);
+                if name != kw::UnderscoreLifetime { Some(name) } else { None }
             }
+            BoundRegionKind::NamedAnon(name) => Some(name),
+            _ => None,
         }
-
-        None
     }
 
     pub fn get_id(&self) -> Option<DefId> {
         match *self {
-            BoundRegionKind::Named(id, _) => Some(id),
+            BoundRegionKind::Named(id) => Some(id),
             _ => None,
         }
     }
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 1214731a3b2..af9c98bd87d 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -69,12 +69,11 @@ impl fmt::Debug for ty::BoundRegionKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
             ty::BoundRegionKind::Anon => write!(f, "BrAnon"),
-            ty::BoundRegionKind::Named(did, name) => {
-                if did.is_crate_root() {
-                    write!(f, "BrNamed({name})")
-                } else {
-                    write!(f, "BrNamed({did:?}, {name})")
-                }
+            ty::BoundRegionKind::NamedAnon(name) => {
+                write!(f, "BrNamedAnon({name})")
+            }
+            ty::BoundRegionKind::Named(did) => {
+                write!(f, "BrNamed({did:?})")
             }
             ty::BoundRegionKind::ClosureEnv => write!(f, "BrEnv"),
         }
@@ -91,12 +90,11 @@ impl fmt::Debug for ty::LateParamRegionKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
             ty::LateParamRegionKind::Anon(idx) => write!(f, "LateAnon({idx})"),
-            ty::LateParamRegionKind::Named(did, name) => {
-                if did.is_crate_root() {
-                    write!(f, "LateNamed({name})")
-                } else {
-                    write!(f, "LateNamed({did:?}, {name})")
-                }
+            ty::LateParamRegionKind::NamedAnon(idx, name) => {
+                write!(f, "LateNamedAnon({idx:?}, {name})")
+            }
+            ty::LateParamRegionKind::Named(did) => {
+                write!(f, "LateNamed({did:?})")
             }
             ty::LateParamRegionKind::ClosureEnv => write!(f, "LateEnv"),
         }
@@ -185,7 +183,7 @@ impl fmt::Debug for ty::BoundTy {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self.kind {
             ty::BoundTyKind::Anon => write!(f, "{:?}", self.var),
-            ty::BoundTyKind::Param(_, sym) => write!(f, "{sym:?}"),
+            ty::BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"),
         }
     }
 }
@@ -274,7 +272,6 @@ TrivialTypeTraversalImpls! {
     crate::ty::BoundVar,
     crate::ty::InferConst,
     crate::ty::Placeholder<crate::ty::BoundRegion>,
-    crate::ty::Placeholder<crate::ty::BoundTy>,
     crate::ty::Placeholder<ty::BoundVar>,
     crate::ty::UserTypeAnnotationIndex,
     crate::ty::ValTree<'tcx>,
@@ -305,6 +302,7 @@ TrivialTypeTraversalAndLiftImpls! {
     // tidy-alphabetical-start
     crate::ty::ParamConst,
     crate::ty::ParamTy,
+    crate::ty::Placeholder<crate::ty::BoundTy>,
     crate::ty::instance::ReifyReason,
     rustc_hir::def_id::DefId,
     // tidy-alphabetical-end
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 7a1890226c9..8bb3b3f1263 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -400,7 +400,7 @@ impl<'tcx> rustc_type_ir::inherent::BoundVarLike<TyCtxt<'tcx>> for BoundTy {
 #[derive(HashStable)]
 pub enum BoundTyKind {
     Anon,
-    Param(DefId, Symbol),
+    Param(DefId),
 }
 
 impl From<BoundVar> for BoundTy {
@@ -2032,7 +2032,7 @@ mod size_asserts {
 
     use super::*;
     // tidy-alphabetical-start
-    static_assert_size!(ty::RegionKind<'_>, 24);
+    static_assert_size!(ty::RegionKind<'_>, 20);
     static_assert_size!(ty::TyKind<'_>, 24);
     // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
index 975226bb642..daf8fa5f19e 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
@@ -171,9 +171,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 this.diverge_from(block);
                 block = success;
 
-                // The `Box<T>` temporary created here is not a part of the HIR,
-                // and therefore is not considered during coroutine auto-trait
-                // determination. See the comment about `box` at `yield_in_scope`.
                 let result = this.local_decls.push(LocalDecl::new(expr.ty, expr_span));
                 this.cfg
                     .push(block, Statement::new(source_info, StatementKind::StorageLive(result)));
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index 47ed9e87244..a418aa82100 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -1,5 +1,3 @@
-use std::cmp::Ordering;
-
 use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::solve::{Goal, QueryInput};
@@ -266,11 +264,15 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
         // See the rustc-dev-guide section about how we deal with universes
         // during canonicalization in the new solver.
         match self.canonicalize_mode {
-            // We try to deduplicate as many query calls as possible and hide
-            // all information which should not matter for the solver.
-            //
-            // For this we compress universes as much as possible.
-            CanonicalizeMode::Input { .. } => {}
+            // All placeholders and vars are canonicalized in the root universe.
+            CanonicalizeMode::Input { .. } => {
+                debug_assert!(
+                    var_kinds.iter().all(|var| var.universe() == ty::UniverseIndex::ROOT),
+                    "expected all vars to be canonicalized in root universe: {var_kinds:#?}"
+                );
+                let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
+                (ty::UniverseIndex::ROOT, var_kinds)
+            }
             // When canonicalizing a response we map a universes already entered
             // by the caller to the root universe and only return useful universe
             // information for placeholders and inference variables created inside
@@ -288,113 +290,10 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
                     .map(|kind| kind.universe())
                     .max()
                     .unwrap_or(ty::UniverseIndex::ROOT);
-
                 let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
-                return (max_universe, var_kinds);
+                (max_universe, var_kinds)
             }
         }
-
-        // Given a `var_kinds` with existentials `En` and universals `Un` in
-        // universes `n`, this algorithm compresses them in place so that:
-        //
-        // - the new universe indices are as small as possible
-        // - we create a new universe if we would otherwise
-        //   1. put existentials from a different universe into the same one
-        //   2. put a placeholder in the same universe as an existential which cannot name it
-        //
-        // Let's walk through an example:
-        // - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0
-        // - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1
-        // - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2
-        // - var_kinds: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5
-        // - var_kinds: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
-        // - var_kinds: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
-        //
-        // This algorithm runs in `O(mn)` where `n` is the number of different universes and
-        // `m` the number of variables. This should be fine as both are expected to be small.
-        let mut curr_compressed_uv = ty::UniverseIndex::ROOT;
-        let mut existential_in_new_uv = None;
-        let mut next_orig_uv = Some(ty::UniverseIndex::ROOT);
-        while let Some(orig_uv) = next_orig_uv.take() {
-            let mut update_uv = |var: &mut CanonicalVarKind<I>, orig_uv, is_existential| {
-                let uv = var.universe();
-                match uv.cmp(&orig_uv) {
-                    Ordering::Less => (), // Already updated
-                    Ordering::Equal => {
-                        if is_existential {
-                            if existential_in_new_uv.is_some_and(|uv| uv < orig_uv) {
-                                // Condition 1.
-                                //
-                                // We already put an existential from a outer universe
-                                // into the current compressed universe, so we need to
-                                // create a new one.
-                                curr_compressed_uv = curr_compressed_uv.next_universe();
-                            }
-
-                            // `curr_compressed_uv` will now contain an existential from
-                            // `orig_uv`. Trying to canonicalizing an existential from
-                            // a higher universe has to therefore use a new compressed
-                            // universe.
-                            existential_in_new_uv = Some(orig_uv);
-                        } else if existential_in_new_uv.is_some() {
-                            // Condition 2.
-                            //
-                            //  `var` is a placeholder from a universe which is not nameable
-                            // by an existential which we already put into the compressed
-                            // universe `curr_compressed_uv`. We therefore have to create a
-                            // new universe for `var`.
-                            curr_compressed_uv = curr_compressed_uv.next_universe();
-                            existential_in_new_uv = None;
-                        }
-
-                        *var = var.with_updated_universe(curr_compressed_uv);
-                    }
-                    Ordering::Greater => {
-                        // We can ignore this variable in this iteration. We only look at
-                        // universes which actually occur in the input for performance.
-                        //
-                        // For this we set `next_orig_uv` to the next smallest, not yet compressed,
-                        // universe of the input.
-                        if next_orig_uv.is_none_or(|curr_next_uv| uv.cannot_name(curr_next_uv)) {
-                            next_orig_uv = Some(uv);
-                        }
-                    }
-                }
-            };
-
-            // For each universe which occurs in the input, we first iterate over all
-            // placeholders and then over all inference variables.
-            //
-            // Whenever we compress the universe of a placeholder, no existential with
-            // an already compressed universe can name that placeholder.
-            for is_existential in [false, true] {
-                for var in var_kinds.iter_mut() {
-                    // We simply put all regions from the input into the highest
-                    // compressed universe, so we only deal with them at the end.
-                    if !var.is_region() {
-                        if is_existential == var.is_existential() {
-                            update_uv(var, orig_uv, is_existential)
-                        }
-                    }
-                }
-            }
-        }
-
-        // We put all regions into a separate universe.
-        let mut first_region = true;
-        for var in var_kinds.iter_mut() {
-            if var.is_region() {
-                if first_region {
-                    first_region = false;
-                    curr_compressed_uv = curr_compressed_uv.next_universe();
-                }
-                debug_assert!(var.is_existential());
-                *var = var.with_updated_universe(curr_compressed_uv);
-            }
-        }
-
-        let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
-        (curr_compressed_uv, var_kinds)
     }
 
     fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty {
@@ -407,11 +306,18 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
                         "ty vid should have been resolved fully before canonicalization"
                     );
 
-                    CanonicalVarKind::Ty(CanonicalTyVarKind::General(
-                        self.delegate
-                            .universe_of_ty(vid)
-                            .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")),
-                    ))
+                    match self.canonicalize_mode {
+                        CanonicalizeMode::Input { .. } => CanonicalVarKind::Ty(
+                            CanonicalTyVarKind::General(ty::UniverseIndex::ROOT),
+                        ),
+                        CanonicalizeMode::Response { .. } => {
+                            CanonicalVarKind::Ty(CanonicalTyVarKind::General(
+                                self.delegate.universe_of_ty(vid).unwrap_or_else(|| {
+                                    panic!("ty var should have been resolved: {t:?}")
+                                }),
+                            ))
+                        }
+                    }
                 }
                 ty::IntVar(vid) => {
                     debug_assert_eq!(
@@ -435,7 +341,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
             },
             ty::Placeholder(placeholder) => match self.canonicalize_mode {
                 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
-                    PlaceholderLike::new_anon(placeholder.universe(), self.variables.len().into()),
+                    PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
                 ),
                 CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
             },
@@ -588,13 +494,21 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
                         c,
                         "const vid should have been resolved fully before canonicalization"
                     );
-                    CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
+
+                    match self.canonicalize_mode {
+                        CanonicalizeMode::Input { .. } => {
+                            CanonicalVarKind::Const(ty::UniverseIndex::ROOT)
+                        }
+                        CanonicalizeMode::Response { .. } => {
+                            CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
+                        }
+                    }
                 }
                 ty::InferConst::Fresh(_) => todo!(),
             },
             ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
                 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
-                    PlaceholderLike::new_anon(placeholder.universe(), self.variables.len().into()),
+                    PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
                 ),
                 CanonicalizeMode::Response { .. } => {
                     CanonicalVarKind::PlaceholderConst(placeholder)
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 27355a422d1..8fdc06ee463 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -304,6 +304,7 @@ fn emit_malformed_attribute(
             | sym::naked
             | sym::no_mangle
             | sym::non_exhaustive
+            | sym::ignore
             | sym::must_use
             | sym::track_caller
             | sym::link_name
@@ -319,8 +320,7 @@ fn emit_malformed_attribute(
 
     // Some of previously accepted forms were used in practice,
     // report them as warnings for now.
-    let should_warn =
-        |name| matches!(name, sym::doc | sym::ignore | sym::link | sym::test | sym::bench);
+    let should_warn = |name| matches!(name, sym::doc | sym::link | sym::test | sym::bench);
 
     let error_msg = format!("malformed `{name}` attribute input");
     let mut suggestions = vec![];
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index c5ced406414..18b3ab12e2d 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -215,6 +215,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
                     self.check_may_dangle(hir_id, *attr_span)
                 }
+                Attribute::Parsed(AttributeKind::Ignore { span, .. }) => {
+                    self.check_generic_attr(hir_id, sym::ignore, *span, target, Target::Fn)
+                }
                 Attribute::Parsed(AttributeKind::MustUse { span, .. }) => {
                     self.check_must_use(hir_id, *span, target)
                 }
@@ -303,7 +306,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         }
                         [sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod),
                         [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
-                        [sym::ignore, ..] | [sym::should_panic, ..] => {
+                        [sym::should_panic, ..] => {
                             self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn)
                         }
                         [sym::automatically_derived, ..] => {
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 650a827ba56..eeb8cb893d7 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1202,12 +1202,8 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
     fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) {
         if !ident.as_str().starts_with('_') {
             self.r.unused_macros.insert(def_id, (node_id, ident));
-            for (rule_i, rule_span) in &self.r.macro_map[&def_id.to_def_id()].rule_spans {
-                self.r
-                    .unused_macro_rules
-                    .entry(node_id)
-                    .or_default()
-                    .insert(*rule_i, (ident, *rule_span));
+            for rule_i in 0..self.r.macro_map[&def_id.to_def_id()].nrules {
+                self.r.unused_macro_rules.entry(node_id).or_default().insert(rule_i);
             }
         }
     }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 7162f3a77d3..3f865d7c2da 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1014,13 +1014,13 @@ struct DeriveData {
 
 struct MacroData {
     ext: Arc<SyntaxExtension>,
-    rule_spans: Vec<(usize, Span)>,
+    nrules: usize,
     macro_rules: bool,
 }
 
 impl MacroData {
     fn new(ext: Arc<SyntaxExtension>) -> MacroData {
-        MacroData { ext, rule_spans: Vec::new(), macro_rules: false }
+        MacroData { ext, nrules: 0, macro_rules: false }
     }
 }
 
@@ -1135,7 +1135,7 @@ pub struct Resolver<'ra, 'tcx> {
     ast_transform_scopes: FxHashMap<LocalExpnId, Module<'ra>>,
     unused_macros: FxIndexMap<LocalDefId, (NodeId, Ident)>,
     /// A map from the macro to all its potentially unused arms.
-    unused_macro_rules: FxIndexMap<NodeId, UnordMap<usize, (Ident, Span)>>,
+    unused_macro_rules: FxIndexMap<NodeId, UnordSet<usize>>,
     proc_macro_stubs: FxHashSet<LocalDefId>,
     /// Traces collected during macro resolution and validated when it's complete.
     single_segment_macro_resolutions:
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 3d33a02a9c6..9bc96403559 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -351,13 +351,23 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
         }
 
         for (&node_id, unused_arms) in self.unused_macro_rules.iter() {
-            for (&arm_i, &(ident, rule_span)) in unused_arms.to_sorted_stable_ord() {
-                self.lint_buffer.buffer_lint(
-                    UNUSED_MACRO_RULES,
-                    node_id,
-                    rule_span,
-                    BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name),
-                );
+            if unused_arms.is_empty() {
+                continue;
+            }
+            let def_id = self.local_def_id(node_id).to_def_id();
+            let m = &self.macro_map[&def_id];
+            let SyntaxExtensionKind::LegacyBang(ref ext) = m.ext.kind else {
+                continue;
+            };
+            for &arm_i in unused_arms.to_sorted_stable_ord() {
+                if let Some((ident, rule_span)) = ext.get_unused_rule(arm_i) {
+                    self.lint_buffer.buffer_lint(
+                        UNUSED_MACRO_RULES,
+                        node_id,
+                        rule_span,
+                        BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name),
+                    );
+                }
             }
         }
     }
@@ -1146,7 +1156,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         node_id: NodeId,
         edition: Edition,
     ) -> MacroData {
-        let (mut ext, mut rule_spans) = compile_declarative_macro(
+        let (mut ext, mut nrules) = compile_declarative_macro(
             self.tcx.sess,
             self.tcx.features(),
             macro_def,
@@ -1163,13 +1173,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 // The macro is a built-in, replace its expander function
                 // while still taking everything else from the source code.
                 ext.kind = builtin_ext_kind.clone();
-                rule_spans = Vec::new();
+                nrules = 0;
             } else {
                 self.dcx().emit_err(errors::CannotFindBuiltinMacroWithName { span, ident });
             }
         }
 
-        MacroData { ext: Arc::new(ext), rule_spans, macro_rules: macro_def.macro_rules }
+        MacroData { ext: Arc::new(ext), nrules, macro_rules: macro_def.macro_rules }
     }
 
     fn path_accessible(
diff --git a/compiler/rustc_smir/src/stable_mir/unstable/convert/internal.rs b/compiler/rustc_smir/src/stable_mir/unstable/convert/internal.rs
index 4cbe02bfa0d..37c93af392e 100644
--- a/compiler/rustc_smir/src/stable_mir/unstable/convert/internal.rs
+++ b/compiler/rustc_smir/src/stable_mir/unstable/convert/internal.rs
@@ -7,7 +7,6 @@
 
 use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy};
 use rustc_smir::Tables;
-use rustc_span::Symbol;
 use stable_mir::abi::Layout;
 use stable_mir::compiler_interface::BridgeTys;
 use stable_mir::mir::alloc::AllocId;
@@ -446,17 +445,15 @@ impl RustcInternal for BoundVariableKind {
         match self {
             BoundVariableKind::Ty(kind) => rustc_ty::BoundVariableKind::Ty(match kind {
                 BoundTyKind::Anon => rustc_ty::BoundTyKind::Anon,
-                BoundTyKind::Param(def, symbol) => rustc_ty::BoundTyKind::Param(
-                    def.0.internal(tables, tcx),
-                    Symbol::intern(symbol),
-                ),
+                BoundTyKind::Param(def, _symbol) => {
+                    rustc_ty::BoundTyKind::Param(def.0.internal(tables, tcx))
+                }
             }),
             BoundVariableKind::Region(kind) => rustc_ty::BoundVariableKind::Region(match kind {
                 BoundRegionKind::BrAnon => rustc_ty::BoundRegionKind::Anon,
-                BoundRegionKind::BrNamed(def, symbol) => rustc_ty::BoundRegionKind::Named(
-                    def.0.internal(tables, tcx),
-                    Symbol::intern(symbol),
-                ),
+                BoundRegionKind::BrNamed(def, _symbol) => {
+                    rustc_ty::BoundRegionKind::Named(def.0.internal(tables, tcx))
+                }
                 BoundRegionKind::BrEnv => rustc_ty::BoundRegionKind::ClosureEnv,
             }),
             BoundVariableKind::Const => rustc_ty::BoundVariableKind::Const,
diff --git a/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/ty.rs b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/ty.rs
index c0a430079d8..596c8b96bfc 100644
--- a/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/unstable/convert/stable/ty.rs
@@ -1,7 +1,7 @@
 //! Conversion of internal Rust compiler `ty` items to stable ones.
 
 use rustc_middle::ty::Ty;
-use rustc_middle::{mir, ty};
+use rustc_middle::{bug, mir, ty};
 use rustc_smir::Tables;
 use rustc_smir::context::SmirCtxt;
 use stable_mir::alloc;
@@ -291,14 +291,14 @@ impl<'tcx> Stable<'tcx> for ty::BoundTyKind {
     fn stable<'cx>(
         &self,
         tables: &mut Tables<'cx, BridgeTys>,
-        _: &SmirCtxt<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
     ) -> Self::T {
         use stable_mir::ty::BoundTyKind;
 
         match self {
             ty::BoundTyKind::Anon => BoundTyKind::Anon,
-            ty::BoundTyKind::Param(def_id, symbol) => {
-                BoundTyKind::Param(tables.param_def(*def_id), symbol.to_string())
+            ty::BoundTyKind::Param(def_id) => {
+                BoundTyKind::Param(tables.param_def(*def_id), cx.tcx.item_name(*def_id).to_string())
             }
         }
     }
@@ -310,16 +310,18 @@ impl<'tcx> Stable<'tcx> for ty::BoundRegionKind {
     fn stable<'cx>(
         &self,
         tables: &mut Tables<'cx, BridgeTys>,
-        _: &SmirCtxt<'cx, BridgeTys>,
+        cx: &SmirCtxt<'cx, BridgeTys>,
     ) -> Self::T {
         use stable_mir::ty::BoundRegionKind;
 
         match self {
             ty::BoundRegionKind::Anon => BoundRegionKind::BrAnon,
-            ty::BoundRegionKind::Named(def_id, symbol) => {
-                BoundRegionKind::BrNamed(tables.br_named_def(*def_id), symbol.to_string())
-            }
+            ty::BoundRegionKind::Named(def_id) => BoundRegionKind::BrNamed(
+                tables.br_named_def(*def_id),
+                cx.tcx.item_name(*def_id).to_string(),
+            ),
             ty::BoundRegionKind::ClosureEnv => BoundRegionKind::BrEnv,
+            ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
         }
     }
 }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs
index aa7935a29f0..73a04d78519 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs
@@ -2,8 +2,6 @@
 //! where one region is named and the other is anonymous.
 
 use rustc_errors::Diag;
-use rustc_middle::ty;
-use rustc_span::kw;
 use tracing::debug;
 
 use crate::error_reporting::infer::nice_region_error::NiceRegionError;
@@ -27,12 +25,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         // only introduced anonymous regions in parameters) as well as a
         // version new_ty of its type where the anonymous region is replaced
         // with the named one.
-        let (named, anon, anon_param_info, region_info) = if sub.has_name()
+        let (named, anon, anon_param_info, region_info) = if sub.is_named(self.tcx())
             && let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sup)
             && let Some(anon_param_info) = self.find_param_with_region(sup, sub)
         {
             (sub, sup, anon_param_info, region_info)
-        } else if sup.has_name()
+        } else if sup.is_named(self.tcx())
             && let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sub)
             && let Some(anon_param_info) = self.find_param_with_region(sub, sup)
         {
@@ -58,14 +56,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         let scope_def_id = region_info.scope;
         let is_impl_item = region_info.is_impl_item;
 
-        match anon_param_info.kind {
-            ty::LateParamRegionKind::Named(_, kw::UnderscoreLifetime)
-            | ty::LateParamRegionKind::Anon(_) => {}
-            _ => {
-                /* not an anonymous region */
-                debug!("try_report_named_anon_conflict: not an anonymous region");
-                return None;
-            }
+        if anon_param_info.kind.is_named(self.tcx()) {
+            /* not an anonymous region */
+            debug!("try_report_named_anon_conflict: not an anonymous region");
+            return None;
         }
 
         if is_impl_item {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs
index 5056161e117..64fc365c44a 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs
@@ -164,7 +164,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
                 sub_region @ Region(Interned(RePlaceholder(_), _)),
                 sup_region,
             )) => self.try_report_trait_placeholder_mismatch(
-                (!sup_region.has_name()).then_some(*sup_region),
+                (!sup_region.is_named(self.tcx())).then_some(*sup_region),
                 cause,
                 Some(*sub_region),
                 None,
@@ -176,7 +176,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
                 sub_region,
                 sup_region @ Region(Interned(RePlaceholder(_), _)),
             )) => self.try_report_trait_placeholder_mismatch(
-                (!sub_region.has_name()).then_some(*sub_region),
+                (!sub_region.is_named(self.tcx())).then_some(*sub_region),
                 cause,
                 None,
                 Some(*sup_region),
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs
index 7fcd3c847e3..3bab09bc587 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs
@@ -1,5 +1,6 @@
 use rustc_data_structures::intern::Interned;
 use rustc_errors::Diag;
+use rustc_middle::bug;
 use rustc_middle::ty::{self, RePlaceholder, Region};
 
 use crate::error_reporting::infer::nice_region_error::NiceRegionError;
@@ -28,20 +29,22 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
                 )),
             )) => {
                 let span = *span;
-                let (sub_span, sub_symbol) = match sub_name {
-                    ty::BoundRegionKind::Named(def_id, symbol) => {
-                        (Some(self.tcx().def_span(def_id)), Some(symbol))
+                let (sub_span, sub_symbol) = match *sub_name {
+                    ty::BoundRegionKind::Named(def_id) => {
+                        (Some(self.tcx().def_span(def_id)), Some(self.tcx().item_name(def_id)))
                     }
                     ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => (None, None),
+                    ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
                 };
-                let (sup_span, sup_symbol) = match sup_name {
-                    ty::BoundRegionKind::Named(def_id, symbol) => {
-                        (Some(self.tcx().def_span(def_id)), Some(symbol))
+                let (sup_span, sup_symbol) = match *sup_name {
+                    ty::BoundRegionKind::Named(def_id) => {
+                        (Some(self.tcx().def_span(def_id)), Some(self.tcx().item_name(def_id)))
                     }
                     ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => (None, None),
+                    ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
                 };
                 let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) {
-                    (Some(sub_span), Some(sup_span), Some(&sub_symbol), Some(&sup_symbol)) => {
+                    (Some(sub_span), Some(sup_span), Some(sub_symbol), Some(sup_symbol)) => {
                         PlaceholderRelationLfNotSatisfied::HasBoth {
                             span,
                             sub_span,
@@ -51,7 +54,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
                             note: (),
                         }
                     }
-                    (Some(sub_span), Some(sup_span), _, Some(&sup_symbol)) => {
+                    (Some(sub_span), Some(sup_span), _, Some(sup_symbol)) => {
                         PlaceholderRelationLfNotSatisfied::HasSup {
                             span,
                             sub_span,
@@ -60,7 +63,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
                             note: (),
                         }
                     }
-                    (Some(sub_span), Some(sup_span), Some(&sub_symbol), _) => {
+                    (Some(sub_span), Some(sup_span), Some(sub_symbol), _) => {
                         PlaceholderRelationLfNotSatisfied::HasSub {
                             span,
                             sub_span,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
index eaa06d8e8b0..3edc365c886 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
@@ -45,7 +45,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         let return_sp = sub_origin.span();
         let param = self.find_param_with_region(*sup_r, *sub_r)?;
         let simple_ident = param.param.pat.simple_ident();
-        let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
+        let lifetime_name =
+            if sup_r.is_named(self.tcx()) { sup_r.to_string() } else { "'_".to_owned() };
 
         let (mention_influencer, influencer_point) =
             if sup_origin.span().overlaps(param.param_ty_span) {
@@ -99,7 +100,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             // We don't need a note, it's already at the end, it can be shown as a `span_label`.
             require_span_as_label: (!require_as_note).then_some(require_span),
 
-            has_lifetime: sup_r.has_name(),
+            has_lifetime: sup_r.is_named(self.tcx()),
             lifetime: lifetime_name.clone(),
             has_param_name: simple_ident.is_some(),
             param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
index f1237130c15..772a7f01332 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
@@ -60,14 +60,15 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         // Mark all unnamed regions in the type with a number.
         // This diagnostic is called in response to lifetime errors, so be informative.
         struct HighlightBuilder<'tcx> {
+            tcx: TyCtxt<'tcx>,
             highlight: RegionHighlightMode<'tcx>,
             counter: usize,
         }
 
         impl<'tcx> HighlightBuilder<'tcx> {
-            fn build(sig: ty::PolyFnSig<'tcx>) -> RegionHighlightMode<'tcx> {
+            fn build(tcx: TyCtxt<'tcx>, sig: ty::PolyFnSig<'tcx>) -> RegionHighlightMode<'tcx> {
                 let mut builder =
-                    HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 };
+                    HighlightBuilder { tcx, highlight: RegionHighlightMode::default(), counter: 1 };
                 sig.visit_with(&mut builder);
                 builder.highlight
             }
@@ -75,15 +76,15 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 
         impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> {
             fn visit_region(&mut self, r: ty::Region<'tcx>) {
-                if !r.has_name() && self.counter <= 3 {
+                if !r.is_named(self.tcx) && self.counter <= 3 {
                     self.highlight.highlighting_region(r, self.counter);
                     self.counter += 1;
                 }
             }
         }
 
-        let expected_highlight = HighlightBuilder::build(expected);
         let tcx = self.cx.tcx;
+        let expected_highlight = HighlightBuilder::build(tcx, expected);
         let expected = Highlighted {
             highlight: expected_highlight,
             ns: Namespace::TypeNS,
@@ -91,7 +92,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             value: expected,
         }
         .to_string();
-        let found_highlight = HighlightBuilder::build(found);
+        let found_highlight = HighlightBuilder::build(tcx, found);
         let found =
             Highlighted { highlight: found_highlight, ns: Namespace::TypeNS, tcx, value: found }
                 .to_string();
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
index 4a71ab4e06a..5f2aab38c31 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
@@ -46,7 +46,7 @@ pub fn find_param_with_region<'tcx>(
         ty::ReLateParam(late_param) => (late_param.scope, late_param.kind),
         ty::ReEarlyParam(ebr) => {
             let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id;
-            (tcx.parent(region_def), ty::LateParamRegionKind::Named(region_def, ebr.name))
+            (tcx.parent(region_def), ty::LateParamRegionKind::Named(region_def))
         }
         _ => return None, // not a free region
     };
@@ -144,7 +144,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         // We are only checking is any region meets the condition so order doesn't matter
         #[allow(rustc::potential_query_instability)]
         late_bound_regions.iter().any(|r| match *r {
-            ty::BoundRegionKind::Named(def_id, _) => def_id == region_def_id,
+            ty::BoundRegionKind::Named(def_id) => def_id == region_def_id,
             _ => false,
         })
     }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
index 4fab67b01cb..f3441a8d72a 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
@@ -713,14 +713,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
 
         let labeled_user_string = match bound_kind {
-            GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
-            GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"),
-            GenericKind::Alias(ref p) => match p.kind(self.tcx) {
+            GenericKind::Param(_) => format!("the parameter type `{bound_kind}`"),
+            GenericKind::Placeholder(_) => format!("the placeholder type `{bound_kind}`"),
+            GenericKind::Alias(p) => match p.kind(self.tcx) {
                 ty::Projection | ty::Inherent => {
-                    format!("the associated type `{p}`")
+                    format!("the associated type `{bound_kind}`")
                 }
-                ty::Free => format!("the type alias `{p}`"),
-                ty::Opaque => format!("the opaque type `{p}`"),
+                ty::Free => format!("the type alias `{bound_kind}`"),
+                ty::Opaque => format!("the opaque type `{bound_kind}`"),
             },
         };
 
@@ -729,7 +729,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             .dcx()
             .struct_span_err(span, format!("{labeled_user_string} may not live long enough"));
         err.code(match sub.kind() {
-            ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309,
+            ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.is_named(self.tcx) => E0309,
             ty::ReStatic => E0310,
             _ => E0311,
         });
@@ -755,7 +755,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 || (bound_kind, sub).has_placeholders()
                 || !bound_kind.is_suggestable(self.tcx, false)
             {
-                let lt_name = sub.get_name_or_anon().to_string();
+                let lt_name = sub.get_name_or_anon(self.tcx).to_string();
                 err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
                 break 'suggestion;
             }
@@ -875,13 +875,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
         }
 
-        let (lifetime_def_id, lifetime_scope) = match self
-            .tcx
-            .is_suitable_region(generic_param_scope, lifetime)
-        {
-            Some(info) if !lifetime.has_name() => (info.region_def_id.expect_local(), info.scope),
-            _ => return lifetime.get_name_or_anon().to_string(),
-        };
+        let (lifetime_def_id, lifetime_scope) =
+            match self.tcx.is_suitable_region(generic_param_scope, lifetime) {
+                Some(info) if !lifetime.is_named(self.tcx) => {
+                    (info.region_def_id.expect_local(), info.scope)
+                }
+                _ => return lifetime.get_name_or_anon(self.tcx).to_string(),
+            };
 
         let new_lt = {
             let generics = self.tcx.generics_of(lifetime_scope);
@@ -895,7 +895,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             // consider late-bound lifetimes ...
             used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(
                 |p| match p {
-                    ty::BoundVariableKind::Region(lt) => lt.get_name(),
+                    ty::BoundVariableKind::Region(lt) => lt.get_name(self.tcx),
                     _ => None,
                 },
             ));
@@ -1006,7 +1006,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> {
         let br_string = |br: ty::BoundRegionKind| {
             let mut s = match br {
-                ty::BoundRegionKind::Named(_, name) => name.to_string(),
+                ty::BoundRegionKind::Named(def_id) => self.tcx.item_name(def_id).to_string(),
                 _ => String::new(),
             };
             if !s.is_empty() {
@@ -1109,7 +1109,7 @@ fn msg_span_from_named_region<'tcx>(
         ty::ReEarlyParam(br) => {
             let param_def_id = tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id;
             let span = tcx.def_span(param_def_id);
-            let text = if br.has_name() {
+            let text = if br.is_named() {
                 format!("the lifetime `{}` as defined here", br.name)
             } else {
                 "the anonymous lifetime as defined here".to_string()
@@ -1117,13 +1117,14 @@ fn msg_span_from_named_region<'tcx>(
             (text, Some(span))
         }
         ty::ReLateParam(ref fr) => {
-            if !fr.kind.is_named()
+            if !fr.kind.is_named(tcx)
                 && let Some((ty, _)) = find_anon_type(tcx, generic_param_scope, region)
             {
                 ("the anonymous lifetime defined here".to_string(), Some(ty.span))
             } else {
                 match fr.kind {
-                    ty::LateParamRegionKind::Named(param_def_id, name) => {
+                    ty::LateParamRegionKind::Named(param_def_id) => {
+                        let name = tcx.item_name(param_def_id);
                         let span = tcx.def_span(param_def_id);
                         let text = if name == kw::UnderscoreLifetime {
                             "the anonymous lifetime as defined here".to_string()
@@ -1145,9 +1146,12 @@ fn msg_span_from_named_region<'tcx>(
         }
         ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
         ty::RePlaceholder(ty::PlaceholderRegion {
-            bound: ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id, name), .. },
+            bound: ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id), .. },
             ..
-        }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))),
+        }) => (
+            format!("the lifetime `{}` as defined here", tcx.item_name(def_id)),
+            Some(tcx.def_span(def_id)),
+        ),
         ty::RePlaceholder(ty::PlaceholderRegion {
             bound: ty::BoundRegion { kind: ty::BoundRegionKind::Anon, .. },
             ..
diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
index ec3c1ba4a45..3471036256d 100644
--- a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
+++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
@@ -32,21 +32,22 @@ impl<'a> DescriptionCtx<'a> {
                 } else {
                     tcx.def_span(scope)
                 };
-                if br.has_name() {
+                if br.is_named() {
                     (Some(span), "as_defined", br.name.to_string())
                 } else {
                     (Some(span), "as_defined_anon", String::new())
                 }
             }
             ty::ReLateParam(ref fr) => {
-                if !fr.kind.is_named()
+                if !fr.kind.is_named(tcx)
                     && let Some((ty, _)) = find_anon_type(tcx, generic_param_scope, region)
                 {
                     (Some(ty.span), "defined_here", String::new())
                 } else {
                     let scope = fr.scope.expect_local();
                     match fr.kind {
-                        ty::LateParamRegionKind::Named(_, name) => {
+                        ty::LateParamRegionKind::Named(def_id) => {
+                            let name = tcx.item_name(def_id);
                             let span = if let Some(param) = tcx
                                 .hir_get_generics(scope)
                                 .and_then(|generics| generics.get_named(name))