Rollup merge of #142755 - aDotInTheVoid:rdj-shattrs, r=GuillaumeGomez

rustdoc: Remove `FormatRenderer::cache`

We only called it it one place, which isn't generic and can be replaced with a field access.
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 718edad..f297bf9 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -2289,12 +2289,12 @@ pub(super) fn expr_array_ref(
         span: Span,
         elements: &'hir [hir::Expr<'hir>],
     ) -> hir::Expr<'hir> {
-        let addrof = hir::ExprKind::AddrOf(
-            hir::BorrowKind::Ref,
-            hir::Mutability::Not,
-            self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements))),
-        );
-        self.expr(span, addrof)
+        let array = self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements)));
+        self.expr_ref(span, array)
+    }
+
+    pub(super) fn expr_ref(&mut self, span: Span, expr: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
+        self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr))
     }
 
     pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index 17b443b..12f0af7 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -1,7 +1,5 @@
-use core::ops::ControlFlow;
 use std::borrow::Cow;
 
-use rustc_ast::visit::Visitor;
 use rustc_ast::*;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_hir as hir;
@@ -476,77 +474,52 @@ fn expand_format_args<'hir>(
         return hir::ExprKind::Call(new, new_args);
     }
 
-    // If the args array contains exactly all the original arguments once,
-    // in order, we can use a simple array instead of a `match` construction.
-    // However, if there's a yield point in any argument except the first one,
-    // we don't do this, because an Argument cannot be kept across yield points.
-    //
-    // This is an optimization, speeding up compilation about 1-2% in some cases.
-    // See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
-    let use_simple_array = argmap.len() == arguments.len()
-        && argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
-        && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
-
-    let args = if arguments.is_empty() {
+    let (let_statements, args) = if arguments.is_empty() {
         // Generate:
-        //    &<core::fmt::Argument>::none()
+        //     []
+        (vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
+    } else if argmap.len() == 1 && arguments.len() == 1 {
+        // Only one argument, so we don't need to make the `args` tuple.
         //
-        // Note:
-        //     `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime.
-        //
-        //     This makes sure that this still fails to compile, even when the argument is inlined:
-        //
-        //     ```
-        //     let f = format_args!("{}", "a");
-        //     println!("{f}"); // error E0716
-        //     ```
-        //
-        //     Cases where keeping the object around is allowed, such as `format_args!("a")`,
-        //     are handled above by the `allow_const` case.
-        let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
-            macsp,
-            hir::LangItem::FormatArgument,
-            sym::none,
-        ));
-        let none = ctx.expr_call(macsp, none_fn, &[]);
-        ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none))
-    } else if use_simple_array {
         // Generate:
-        //     &[
-        //         <core::fmt::Argument>::new_display(&arg0),
-        //         <core::fmt::Argument>::new_lower_hex(&arg1),
-        //         <core::fmt::Argument>::new_debug(&arg2),
-        //         …
-        //     ]
-        let elements = ctx.arena.alloc_from_iter(arguments.iter().zip(argmap).map(
-            |(arg, ((_, ty), placeholder_span))| {
+        //     super let args = [<core::fmt::Argument>::new_display(&arg)];
+        let args = ctx.arena.alloc_from_iter(argmap.iter().map(
+            |(&(arg_index, ty), &placeholder_span)| {
+                let arg = &arguments[arg_index];
                 let placeholder_span =
                     placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
-                let arg_span = match arg.kind {
-                    FormatArgumentKind::Captured(_) => placeholder_span,
-                    _ => arg.expr.span.with_ctxt(macsp.ctxt()),
-                };
                 let arg = ctx.lower_expr(&arg.expr);
-                let ref_arg = ctx.arena.alloc(ctx.expr(
-                    arg_span,
-                    hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
-                ));
+                let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg));
                 make_argument(ctx, placeholder_span, ref_arg, ty)
             },
         ));
-        ctx.expr_array_ref(macsp, elements)
-    } else {
-        // Generate:
-        //     &match (&arg0, &arg1, &…) {
-        //         args => [
-        //             <core::fmt::Argument>::new_display(args.0),
-        //             <core::fmt::Argument>::new_lower_hex(args.1),
-        //             <core::fmt::Argument>::new_debug(args.0),
-        //             …
-        //         ]
-        //     }
+        let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
         let args_ident = Ident::new(sym::args, macsp);
         let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+        let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
+        (vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)))
+    } else {
+        // Generate:
+        //     super let args = (&arg0, &arg1, &…);
+        let args_ident = Ident::new(sym::args, macsp);
+        let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+        let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
+            let arg_expr = ctx.lower_expr(&arg.expr);
+            ctx.expr(
+                arg.expr.span.with_ctxt(macsp.ctxt()),
+                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
+            )
+        }));
+        let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
+        let let_statement_1 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args_tuple));
+
+        // Generate:
+        //     super let args = [
+        //         <core::fmt::Argument>::new_display(args.0),
+        //         <core::fmt::Argument>::new_lower_hex(args.1),
+        //         <core::fmt::Argument>::new_debug(args.0),
+        //         …
+        //     ];
         let args = ctx.arena.alloc_from_iter(argmap.iter().map(
             |(&(arg_index, ty), &placeholder_span)| {
                 let arg = &arguments[arg_index];
@@ -567,58 +540,47 @@ fn expand_format_args<'hir>(
                 make_argument(ctx, placeholder_span, arg, ty)
             },
         ));
-        let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
-            let arg_expr = ctx.lower_expr(&arg.expr);
-            ctx.expr(
-                arg.expr.span.with_ctxt(macsp.ctxt()),
-                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
-            )
-        }));
-        let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
-        let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
-        let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
-        let match_expr = ctx.arena.alloc(ctx.expr_match(
-            macsp,
-            args_tuple,
-            match_arms,
-            hir::MatchSource::FormatArgs,
-        ));
-        ctx.expr(
-            macsp,
-            hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
+        let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
+        let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+        let let_statement_2 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
+        (
+            vec![let_statement_1, let_statement_2],
+            ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)),
         )
     };
 
-    if let Some(format_options) = format_options {
+    // Generate:
+    //     &args
+    let args = ctx.expr_ref(macsp, args);
+
+    let call = if let Some(format_options) = format_options {
         // Generate:
-        //     <core::fmt::Arguments>::new_v1_formatted(
-        //         lit_pieces,
-        //         args,
-        //         format_options,
-        //         unsafe { ::core::fmt::UnsafeArg::new() }
-        //     )
+        //     unsafe {
+        //         <core::fmt::Arguments>::new_v1_formatted(
+        //             lit_pieces,
+        //             args,
+        //             format_options,
+        //         )
+        //     }
         let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
             macsp,
             hir::LangItem::FormatArguments,
             sym::new_v1_formatted,
         ));
-        let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
-            macsp,
-            hir::LangItem::FormatUnsafeArg,
-            sym::new,
-        ));
-        let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
+        let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options]);
+        let call = ctx.expr_call(macsp, new_v1_formatted, args);
         let hir_id = ctx.next_id();
-        let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
-            stmts: &[],
-            expr: Some(unsafe_arg_new_call),
-            hir_id,
-            rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
-            span: macsp,
-            targeted_by_break: false,
-        }));
-        let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
-        hir::ExprKind::Call(new_v1_formatted, args)
+        hir::ExprKind::Block(
+            ctx.arena.alloc(hir::Block {
+                stmts: &[],
+                expr: Some(call),
+                hir_id,
+                rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
+                span: macsp,
+                targeted_by_break: false,
+            }),
+            None,
+        )
     } else {
         // Generate:
         //     <core::fmt::Arguments>::new_v1(
@@ -632,37 +594,23 @@ fn expand_format_args<'hir>(
         ));
         let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
         hir::ExprKind::Call(new_v1, new_args)
+    };
+
+    if !let_statements.is_empty() {
+        // Generate:
+        //     {
+        //         super let …
+        //         super let …
+        //         <core::fmt::Arguments>::new_…(…)
+        //     }
+        let call = ctx.arena.alloc(ctx.expr(macsp, call));
+        let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call));
+        hir::ExprKind::Block(block, None)
+    } else {
+        call
     }
 }
 
-fn may_contain_yield_point(e: &ast::Expr) -> bool {
-    struct MayContainYieldPoint;
-
-    impl Visitor<'_> for MayContainYieldPoint {
-        type Result = ControlFlow<()>;
-
-        fn visit_expr(&mut self, e: &ast::Expr) -> ControlFlow<()> {
-            if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
-                ControlFlow::Break(())
-            } else {
-                visit::walk_expr(self, e)
-            }
-        }
-
-        fn visit_mac_call(&mut self, _: &ast::MacCall) -> ControlFlow<()> {
-            // Macros should be expanded at this point.
-            unreachable!("unexpanded macro in ast lowering");
-        }
-
-        fn visit_item(&mut self, _: &ast::Item) -> ControlFlow<()> {
-            // Do not recurse into nested items.
-            ControlFlow::Continue(())
-        }
-    }
-
-    MayContainYieldPoint.visit_expr(e).is_break()
-}
-
 fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {
     for piece in template {
         let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index e74fd1d..26d7c0c 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -2292,6 +2292,26 @@ fn stmt_let_pat(
         self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
     }
 
+    fn stmt_super_let_pat(
+        &mut self,
+        span: Span,
+        pat: &'hir hir::Pat<'hir>,
+        init: Option<&'hir hir::Expr<'hir>>,
+    ) -> hir::Stmt<'hir> {
+        let hir_id = self.next_id();
+        let local = hir::LetStmt {
+            super_: Some(span),
+            hir_id,
+            init,
+            pat,
+            els: None,
+            source: hir::LocalSource::Normal,
+            span: self.lower_span(span),
+            ty: None,
+        };
+        self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
+    }
+
     fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
         self.block_all(expr.span, &[], Some(expr))
     }
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index cdc01dc..9ebf4c1 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -38,7 +38,8 @@ pub enum InstructionSetAttr {
     ArmT32,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)]
+#[derive(Encodable, Decodable, HashStable_Generic)]
 pub enum OptimizeAttr {
     /// No `#[optimize(..)]` attribute
     #[default]
@@ -201,6 +202,9 @@ pub enum AttributeKind {
         span: Span,
     },
 
+    /// Represents `#[cold]`.
+    Cold(Span),
+
     /// Represents `#[rustc_confusables]`.
     Confusables {
         symbols: ThinVec<Symbol>,
@@ -229,7 +233,8 @@ pub enum AttributeKind {
 
     /// Represents `#[rustc_macro_transparency]`.
     MacroTransparency(Transparency),
-
+    /// Represents `#[optimize(size|speed)]`
+    Optimize(OptimizeAttr, Span),
     /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
     Repr(ThinVec<(ReprAttr, Span)>),
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
new file mode 100644
index 0000000..1b03525
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -0,0 +1,58 @@
+use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::sym;
+
+use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
+pub(crate) struct OptimizeParser;
+
+impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
+    const PATH: &[rustc_span::Symbol] = &[sym::optimize];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
+    const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        let Some(list) = args.list() else {
+            cx.expected_list(cx.attr_span);
+            return None;
+        };
+
+        let Some(single) = list.single() else {
+            cx.expected_single_argument(list.span);
+            return None;
+        };
+
+        let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
+            Some(sym::size) => OptimizeAttr::Size,
+            Some(sym::speed) => OptimizeAttr::Speed,
+            Some(sym::none) => OptimizeAttr::DoNotOptimize,
+            _ => {
+                cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
+                OptimizeAttr::Default
+            }
+        };
+
+        Some(AttributeKind::Optimize(res, cx.attr_span))
+    }
+}
+
+pub(crate) struct ColdParser;
+
+impl<S: Stage> SingleAttributeParser<S> for ColdParser {
+    const PATH: &[rustc_span::Symbol] = &[sym::cold];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if !args.no_args() {
+            cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
+            return None;
+        };
+
+        Some(AttributeKind::Cold(cx.attr_span))
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index fa2a608..78a696a 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -28,6 +28,7 @@
 
 pub(crate) mod allow_unstable;
 pub(crate) mod cfg;
+pub(crate) mod codegen_attrs;
 pub(crate) mod confusables;
 pub(crate) mod deprecation;
 pub(crate) mod inline;
@@ -86,8 +87,19 @@ pub(crate) trait AttributeParser<S: Stage>: Default + 'static {
 /// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
 /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
 pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
+    /// The single path of the attribute this parser accepts.
+    ///
+    /// If you need the parser to accept more than one path, use [`AttributeParser`] instead
     const PATH: &[Symbol];
+
+    /// Configures the precedence of attributes with the same `PATH` on a syntax node.
     const ATTRIBUTE_ORDER: AttributeOrder;
+
+    /// Configures what to do when when the same attribute is
+    /// applied more than once on the same syntax node.
+    ///
+    /// [`ATTRIBUTE_ORDER`](Self::ATTRIBUTE_ORDER) specified which one is assumed to be correct,
+    /// and this specified whether to, for example, warn or error on the other one.
     const ON_DUPLICATE: OnDuplicate<S>;
 
     /// The template this attribute parser should implement. Used for diagnostics.
@@ -97,6 +109,8 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
 }
 
+/// Use in combination with [`SingleAttributeParser`].
+/// `Single<T: SingleAttributeParser>` implements [`AttributeParser`].
 pub(crate) struct Single<T: SingleAttributeParser<S>, S: Stage>(
     PhantomData<(S, T)>,
     Option<(AttributeKind, Span)>,
@@ -229,6 +243,10 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
     const PATH: &[rustc_span::Symbol];
 
     type Item;
+    /// A function that converts individual items (of type [`Item`](Self::Item)) into the final attribute.
+    ///
+    /// For example, individual representations fomr `#[repr(...)]` attributes into an `AttributeKind::Repr(x)`,
+    ///  where `x` is a vec of these individual reprs.
     const CONVERT: ConvertFn<Self::Item>;
 
     /// The template this attribute parser should implement. Used for diagnostics.
@@ -241,6 +259,8 @@ fn extend<'c>(
     ) -> impl IntoIterator<Item = Self::Item> + 'c;
 }
 
+/// Use in combination with [`CombineAttributeParser`].
+/// `Combine<T: CombineAttributeParser>` implements [`AttributeParser`].
 pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>(
     PhantomData<(S, T)>,
     ThinVec<<T as CombineAttributeParser<S>>::Item>,
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index c9f9f34..a31ca8f 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -25,7 +25,8 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
     const PATH: &[Symbol] = &[sym::repr];
     const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
     // FIXME(jdonszelmann): never used
-    const TEMPLATE: AttributeTemplate = template!(List: "C");
+    const TEMPLATE: AttributeTemplate =
+        template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");
 
     fn extend<'c>(
         cx: &'c mut AcceptContext<'_, '_, S>,
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index d757063..648bd43 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -15,6 +15,7 @@
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
 
 use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
+use crate::attributes::codegen_attrs::{ColdParser, OptimizeParser};
 use crate::attributes::confusables::ConfusablesParser;
 use crate::attributes::deprecation::DeprecationParser;
 use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
@@ -105,9 +106,11 @@ mod late {
 
         // tidy-alphabetical-start
         Single<AsPtrParser>,
+        Single<ColdParser>,
         Single<ConstStabilityIndirectParser>,
         Single<DeprecationParser>,
         Single<InlineParser>,
+        Single<OptimizeParser>,
         Single<RustcForceInlineParser>,
         Single<TransparencyParser>,
         // tidy-alphabetical-end
@@ -232,6 +235,16 @@ pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
         })
     }
 
+    pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span: args_span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedNoArgs,
+        })
+    }
+
     /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
     /// a nicer error message talking about the specific name that was found lacking a value.
     pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 337921a..29f2e44 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -474,6 +474,7 @@ pub(crate) struct UnrecognizedReprHint {
 }
 
 pub(crate) enum AttributeParseErrorReason {
+    ExpectedNoArgs,
     ExpectedStringLiteral { byte_string: Option<Span> },
     ExpectedSingleArgument,
     ExpectedList,
@@ -529,6 +530,10 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
                 diag.span_label(self.span, format!("didn't expect a literal here"));
                 diag.code(E0565);
             }
+            AttributeParseErrorReason::ExpectedNoArgs => {
+                diag.span_label(self.span, format!("didn't expect any arguments here"));
+                diag.code(E0565);
+            }
             AttributeParseErrorReason::ExpectedNameValue(None) => {
                 diag.span_label(
                     self.span,
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 34d3684..98dc898 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -3201,14 +3201,6 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
                         let expr_ty: Option<Ty<'_>> =
                             visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
 
-                        let is_format_arguments_item = if let Some(expr_ty) = expr_ty
-                            && let ty::Adt(adt, _) = expr_ty.kind()
-                        {
-                            self.infcx.tcx.is_lang_item(adt.did(), LangItem::FormatArguments)
-                        } else {
-                            false
-                        };
-
                         if visitor.found == 0
                             && stmt.span.contains(proper_span)
                             && let Some(p) = sm.span_to_margin(stmt.span)
@@ -3236,25 +3228,17 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
                                 ""
                             };
 
-                            if !is_format_arguments_item {
-                                let addition = format!(
-                                    "let {}binding = {};\n{}",
-                                    mutability,
-                                    s,
-                                    " ".repeat(p)
-                                );
-                                err.multipart_suggestion_verbose(
-                                    msg,
-                                    vec![
-                                        (stmt.span.shrink_to_lo(), addition),
-                                        (proper_span, "binding".to_string()),
-                                    ],
-                                    Applicability::MaybeIncorrect,
-                                );
-                            } else {
-                                err.note("the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used");
-                                err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
-                            }
+                            let addition =
+                                format!("let {}binding = {};\n{}", mutability, s, " ".repeat(p));
+                            err.multipart_suggestion_verbose(
+                                msg,
+                                vec![
+                                    (stmt.span.shrink_to_lo(), addition),
+                                    (proper_span, "binding".to_string()),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+
                             suggested = true;
                             break;
                         }
diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 18a8a5a..55a28bc 100644
--- a/compiler/rustc_codegen_gcc/messages.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
@@ -1,7 +1,3 @@
-codegen_gcc_unknown_ctarget_feature_prefix =
-    unknown feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = features must begin with a `+` to enable or `-` to disable it
-
 codegen_gcc_unwinding_inline_asm =
     GCC backend does not support unwinding from inline asm
 
@@ -16,15 +12,3 @@
 codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
 
 codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})
-
-codegen_gcc_unknown_ctarget_feature =
-    unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
-    .possible_feature = you might have meant: `{$rust_feature}`
-    .consider_filing_feature_request = consider filing a feature request
-
-codegen_gcc_missing_features =
-    add the missing features in a `target_feature` attribute
-
-codegen_gcc_target_feature_disable_or_enable =
-    the target features {$features} must all be either enabled or disabled together
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index 7786be9..b7e7343 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -1,31 +1,7 @@
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::Diagnostic;
 use rustc_span::Span;
 
 #[derive(Diagnostic)]
-#[diag(codegen_gcc_unknown_ctarget_feature_prefix)]
-#[note]
-pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
-    pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_unknown_ctarget_feature)]
-#[note]
-pub(crate) struct UnknownCTargetFeature<'a> {
-    pub feature: &'a str,
-    #[subdiagnostic]
-    pub rust_feature: PossibleFeature<'a>,
-}
-
-#[derive(Subdiagnostic)]
-pub(crate) enum PossibleFeature<'a> {
-    #[help(codegen_gcc_possible_feature)]
-    Some { rust_feature: &'a str },
-    #[help(codegen_gcc_consider_filing_feature_request)]
-    None,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_gcc_unwinding_inline_asm)]
 pub(crate) struct UnwindingInlineAsm {
     #[primary_span]
diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs
index 2e00d5f..42ba406 100644
--- a/compiler/rustc_codegen_gcc/src/gcc_util.rs
+++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs
@@ -1,20 +1,12 @@
 #[cfg(feature = "master")]
 use gccjit::Context;
-use rustc_codegen_ssa::codegen_attrs::check_tied_features;
-use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::unord::UnordSet;
+use rustc_codegen_ssa::target_features;
 use rustc_session::Session;
-use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
-use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
 use smallvec::{SmallVec, smallvec};
 
-use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
-
-fn gcc_features_by_flags(sess: &Session) -> Vec<&str> {
-    let mut features: Vec<&str> = Vec::new();
-    retpoline_features_by_flags(sess, &mut features);
-    features
+fn gcc_features_by_flags(sess: &Session, features: &mut Vec<String>) {
+    target_features::retpoline_features_by_flags(sess, features);
+    // FIXME: LLVM also sets +reserve-x18 here under some conditions.
 }
 
 /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
@@ -44,98 +36,29 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
     features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from));
 
     // -Ctarget-features
-    let known_features = sess.target.rust_target_features();
-    let mut featsmap = FxHashMap::default();
-
-    // Compute implied features
-    let mut all_rust_features = vec![];
-    for feature in sess.opts.cg.target_feature.split(',').chain(gcc_features_by_flags(sess)) {
-        if let Some(feature) = feature.strip_prefix('+') {
-            all_rust_features.extend(
-                UnordSet::from(sess.target.implied_target_features(feature))
-                    .to_sorted_stable_ord()
-                    .iter()
-                    .map(|&&s| (true, s)),
-            )
-        } else if let Some(feature) = feature.strip_prefix('-') {
-            // FIXME: Why do we not remove implied features on "-" here?
-            // We do the equivalent above in `target_config`.
-            // See <https://github.com/rust-lang/rust/issues/134792>.
-            all_rust_features.push((false, feature));
-        } else if !feature.is_empty() && diagnostics {
-            sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
-        }
-    }
-    // Remove features that are meant for rustc, not codegen.
-    all_rust_features.retain(|&(_, feature)| {
-        // Retain if it is not a rustc feature
-        !RUSTC_SPECIFIC_FEATURES.contains(&feature)
-    });
-
-    // Check feature validity.
-    if diagnostics {
-        for &(enable, feature) in &all_rust_features {
-            let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
-            match feature_state {
-                None => {
-                    let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| {
-                        let gcc_features = to_gcc_features(sess, rust_feature);
-                        if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature)
-                        {
-                            Some(rust_feature)
-                        } else {
-                            None
-                        }
-                    });
-                    let unknown_feature = if let Some(rust_feature) = rust_feature {
-                        UnknownCTargetFeature {
-                            feature,
-                            rust_feature: PossibleFeature::Some { rust_feature },
-                        }
-                    } else {
-                        UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
-                    };
-                    sess.dcx().emit_warn(unknown_feature);
-                }
-                Some(&(_, stability, _)) => {
-                    stability.verify_feature_enabled_by_flag(sess, enable, feature);
-                }
-            }
-
-            // FIXME(nagisa): figure out how to not allocate a full hashset here.
-            featsmap.insert(feature, enable);
-        }
-    }
-
-    // Translate this into GCC features.
-    let feats =
-        all_rust_features.iter().flat_map(|&(enable, feature)| {
-            let enable_disable = if enable { '+' } else { '-' };
+    target_features::flag_to_backend_features(
+        sess,
+        diagnostics,
+        |feature| to_gcc_features(sess, feature),
+        |feature, enable| {
             // We run through `to_gcc_features` when
             // passing requests down to GCC. This means that all in-language
             // features also work on the command line instead of having two
             // different names when the GCC name and the Rust name differ.
-            to_gcc_features(sess, feature)
-                .iter()
-                .flat_map(|feat| to_gcc_features(sess, feat).into_iter())
-                .map(|feature| {
-                    if enable_disable == '-' {
-                        format!("-{}", feature)
-                    } else {
-                        feature.to_string()
-                    }
-                })
-                .collect::<Vec<_>>()
-        });
-    features.extend(feats);
+            features.extend(
+                to_gcc_features(sess, feature)
+                    .iter()
+                    .flat_map(|feat| to_gcc_features(sess, feat).into_iter())
+                    .map(
+                        |feature| {
+                            if !enable { format!("-{}", feature) } else { feature.to_string() }
+                        },
+                    ),
+            );
+        },
+    );
 
-    if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
-        sess.dcx().emit_err(TargetFeatureDisableOrEnable {
-            features: f,
-            span: None,
-            missing_features: None,
-        });
-    }
+    gcc_features_by_flags(sess, &mut features);
 
     features
 }
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index aa57655..a912678 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -102,6 +102,7 @@
     CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn,
 };
 use rustc_codegen_ssa::base::codegen_crate;
+use rustc_codegen_ssa::target_features::cfg_target_feature;
 use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods};
 use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig};
 use rustc_data_structures::fx::FxIndexMap;
@@ -476,42 +477,21 @@ fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
 
 /// Returns the features that should be set in `cfg(target_feature)`.
 fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig {
-    // TODO(antoyo): use global_gcc_features.
-    let f = |allow_unstable| {
-        sess.target
-            .rust_target_features()
-            .iter()
-            .filter_map(|&(feature, gate, _)| {
-                if allow_unstable
-                    || (gate.in_cfg()
-                        && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
-                {
-                    Some(feature)
-                } else {
-                    None
-                }
-            })
-            .filter(|feature| {
-                // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it.
-                if *feature == "neon" {
-                    return false;
-                }
-                target_info.cpu_supports(feature)
-                // cSpell:disable
-                /*
-                  adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
-                  avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
-                  bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
-                  sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
-                */
-                // cSpell:enable
-            })
-            .map(Symbol::intern)
-            .collect()
-    };
-
-    let target_features = f(false);
-    let unstable_target_features = f(true);
+    let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| {
+        // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it.
+        if feature == "neon" {
+            return false;
+        }
+        target_info.cpu_supports(feature)
+        // cSpell:disable
+        /*
+          adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
+          avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
+          bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
+          sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
+        */
+        // cSpell:enable
+    });
 
     let has_reliable_f16 = target_info.supports_target_dependent_type(CType::Float16);
     let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128);
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index 3faeb9b..3885f18 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -59,16 +59,6 @@
 codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple}
 codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}
 
-codegen_llvm_unknown_ctarget_feature =
-    unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
-    .possible_feature = you might have meant: `{$rust_feature}`
-    .consider_filing_feature_request = consider filing a feature request
-
-codegen_llvm_unknown_ctarget_feature_prefix =
-    unknown feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = features must begin with a `+` to enable or `-` to disable it
-
 codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
 
 codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 8bc74fb..d50ad8a 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -3,36 +3,12 @@
 
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::Diagnostic;
 use rustc_span::Span;
 
 use crate::fluent_generated as fluent;
 
 #[derive(Diagnostic)]
-#[diag(codegen_llvm_unknown_ctarget_feature_prefix)]
-#[note]
-pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
-    pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_unknown_ctarget_feature)]
-#[note]
-pub(crate) struct UnknownCTargetFeature<'a> {
-    pub feature: &'a str,
-    #[subdiagnostic]
-    pub rust_feature: PossibleFeature<'a>,
-}
-
-#[derive(Subdiagnostic)]
-pub(crate) enum PossibleFeature<'a> {
-    #[help(codegen_llvm_possible_feature)]
-    Some { rust_feature: &'a str },
-    #[help(codegen_llvm_consider_filing_feature_request)]
-    None,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_llvm_symbol_already_defined)]
 pub(crate) struct SymbolAlreadyDefined<'a> {
     #[primary_span]
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 0e77bc4..6fd07d5 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -6,27 +6,20 @@
 use std::{ptr, slice, str};
 
 use libc::c_int;
-use rustc_codegen_ssa::TargetConfig;
 use rustc_codegen_ssa::base::wants_wasm_eh;
-use rustc_codegen_ssa::codegen_attrs::check_tied_features;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_codegen_ssa::target_features::cfg_target_feature;
+use rustc_codegen_ssa::{TargetConfig, target_features};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_data_structures::unord::UnordSet;
 use rustc_fs_util::path_to_c_string;
 use rustc_middle::bug;
 use rustc_session::Session;
 use rustc_session::config::{PrintKind, PrintRequest};
-use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
-use rustc_span::Symbol;
 use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
-use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
 use smallvec::{SmallVec, smallvec};
 
 use crate::back::write::create_informational_target_machine;
-use crate::errors::{
-    FixedX18InvalidArch, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
-};
-use crate::llvm;
+use crate::{errors, llvm};
 
 static INIT: Once = Once::new();
 
@@ -195,15 +188,6 @@ fn with_dependencies(
     ) -> Self {
         Self { llvm_feature_name, dependencies }
     }
-
-    fn contains(&'a self, feat: &str) -> bool {
-        self.iter().any(|dep| dep == feat)
-    }
-
-    fn iter(&'a self) -> impl Iterator<Item = &'a str> {
-        let dependencies = self.dependencies.iter().map(|feat| feat.as_str());
-        std::iter::once(self.llvm_feature_name).chain(dependencies)
-    }
 }
 
 impl<'a> IntoIterator for LLVMFeature<'a> {
@@ -216,18 +200,22 @@ fn into_iter(self) -> Self::IntoIter {
     }
 }
 
-// WARNING: the features after applying `to_llvm_features` must be known
-// to LLVM or the feature detection code will walk past the end of the feature
-// array, leading to crashes.
-//
-// To find a list of LLVM's names, see llvm-project/llvm/lib/Target/{ARCH}/*.td
-// where `{ARCH}` is the architecture name. Look for instances of `SubtargetFeature`.
-//
-// Check the current rustc fork of LLVM in the repo at https://github.com/rust-lang/llvm-project/.
-// The commit in use can be found via the `llvm-project` submodule in
-// https://github.com/rust-lang/rust/tree/master/src Though note that Rust can also be build with
-// an external precompiled version of LLVM which might lead to failures if the oldest tested /
-// supported LLVM version doesn't yet support the relevant intrinsics.
+/// Convert a Rust feature name to an LLVM feature name. Returning `None` means the
+/// feature should be skipped, usually because it is not supported by the current
+/// LLVM version.
+///
+/// WARNING: the features after applying `to_llvm_features` must be known
+/// to LLVM or the feature detection code will walk past the end of the feature
+/// array, leading to crashes.
+///
+/// To find a list of LLVM's names, see llvm-project/llvm/lib/Target/{ARCH}/*.td
+/// where `{ARCH}` is the architecture name. Look for instances of `SubtargetFeature`.
+///
+/// Check the current rustc fork of LLVM in the repo at
+/// <https://github.com/rust-lang/llvm-project/>. The commit in use can be found via the
+/// `llvm-project` submodule in <https://github.com/rust-lang/rust/tree/master/src> Though note that
+/// Rust can also be build with an external precompiled version of LLVM which might lead to failures
+/// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics.
 pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFeature<'a>> {
     let arch = if sess.target.arch == "x86_64" {
         "x86"
@@ -343,98 +331,25 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig {
     // the target CPU, that is still expanded to target features (with all their implied features)
     // by LLVM.
     let target_machine = create_informational_target_machine(sess, true);
-    // Compute which of the known target features are enabled in the 'base' target machine. We only
-    // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
-    let mut features: FxHashSet<Symbol> = sess
-        .target
-        .rust_target_features()
-        .iter()
-        .filter(|(feature, _, _)| {
-            // skip checking special features, as LLVM may not understand them
-            if RUSTC_SPECIAL_FEATURES.contains(feature) {
-                return true;
-            }
-            if let Some(feat) = to_llvm_features(sess, feature) {
-                for llvm_feature in feat {
-                    let cstr = SmallCStr::new(llvm_feature);
-                    // `LLVMRustHasFeature` is moderately expensive. On targets with many
-                    // features (e.g. x86) these calls take a non-trivial fraction of runtime
-                    // when compiling very small programs.
-                    if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } {
-                        return false;
-                    }
+
+    let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| {
+        if let Some(feat) = to_llvm_features(sess, feature) {
+            // All the LLVM features this expands to must be enabled.
+            for llvm_feature in feat {
+                let cstr = SmallCStr::new(llvm_feature);
+                // `LLVMRustHasFeature` is moderately expensive. On targets with many
+                // features (e.g. x86) these calls take a non-trivial fraction of runtime
+                // when compiling very small programs.
+                if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } {
+                    return false;
                 }
-                true
-            } else {
-                false
             }
-        })
-        .map(|(feature, _, _)| Symbol::intern(feature))
-        .collect();
-
-    // Add enabled and remove disabled features.
-    for (enabled, feature) in
-        sess.opts.cg.target_feature.split(',').filter_map(|s| match s.chars().next() {
-            Some('+') => Some((true, Symbol::intern(&s[1..]))),
-            Some('-') => Some((false, Symbol::intern(&s[1..]))),
-            _ => None,
-        })
-    {
-        if enabled {
-            // Also add all transitively implied features.
-
-            // We don't care about the order in `features` since the only thing we use it for is the
-            // `features.contains` below.
-            #[allow(rustc::potential_query_instability)]
-            features.extend(
-                sess.target
-                    .implied_target_features(feature.as_str())
-                    .iter()
-                    .map(|s| Symbol::intern(s)),
-            );
+            true
         } else {
-            // Remove transitively reverse-implied features.
-
-            // We don't care about the order in `features` since the only thing we use it for is the
-            // `features.contains` below.
-            #[allow(rustc::potential_query_instability)]
-            features.retain(|f| {
-                if sess.target.implied_target_features(f.as_str()).contains(&feature.as_str()) {
-                    // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to
-                    // remove `f`. (This is the standard logical contraposition principle.)
-                    false
-                } else {
-                    // We can keep `f`.
-                    true
-                }
-            });
+            false
         }
-    }
+    });
 
-    // Filter enabled features based on feature gates.
-    let f = |allow_unstable| {
-        sess.target
-            .rust_target_features()
-            .iter()
-            .filter_map(|(feature, gate, _)| {
-                // The `allow_unstable` set is used by rustc internally to determined which target
-                // features are truly available, so we want to return even perma-unstable
-                // "forbidden" features.
-                if allow_unstable
-                    || (gate.in_cfg()
-                        && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
-                {
-                    Some(Symbol::intern(feature))
-                } else {
-                    None
-                }
-            })
-            .filter(|feature| features.contains(&feature))
-            .collect()
-    };
-
-    let target_features = f(false);
-    let unstable_target_features = f(true);
     let mut cfg = TargetConfig {
         target_features,
         unstable_target_features,
@@ -707,10 +622,18 @@ pub(crate) fn target_cpu(sess: &Session) -> &str {
     handle_native(cpu_name)
 }
 
-fn llvm_features_by_flags(sess: &Session) -> Vec<&str> {
-    let mut features: Vec<&str> = Vec::new();
-    retpoline_features_by_flags(sess, &mut features);
-    features
+/// The target features for compiler flags other than `-Ctarget-features`.
+fn llvm_features_by_flags(sess: &Session, features: &mut Vec<String>) {
+    target_features::retpoline_features_by_flags(sess, features);
+
+    // -Zfixed-x18
+    if sess.opts.unstable_opts.fixed_x18 {
+        if sess.target.arch != "aarch64" {
+            sess.dcx().emit_fatal(errors::FixedX18InvalidArch { arch: &sess.target.arch });
+        } else {
+            features.push("+reserve-x18".into());
+        }
+    }
 }
 
 /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
@@ -777,6 +700,8 @@ pub(crate) fn global_llvm_features(
             .split(',')
             .filter(|v| !v.is_empty())
             // Drop +v8plus feature introduced in LLVM 20.
+            // (Hard-coded target features do not go through `to_llvm_feature` since they already
+            // are LLVM feature names, hence we need a special case here.)
             .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0))
             .map(String::from),
     );
@@ -787,86 +712,23 @@ pub(crate) fn global_llvm_features(
 
     // -Ctarget-features
     if !only_base_features {
-        let known_features = sess.target.rust_target_features();
-        // Will only be filled when `diagnostics` is set!
-        let mut featsmap = FxHashMap::default();
-
-        // Compute implied features
-        let mut all_rust_features = vec![];
-        for feature in sess.opts.cg.target_feature.split(',').chain(llvm_features_by_flags(sess)) {
-            if let Some(feature) = feature.strip_prefix('+') {
-                all_rust_features.extend(
-                    UnordSet::from(sess.target.implied_target_features(feature))
-                        .to_sorted_stable_ord()
-                        .iter()
-                        .map(|&&s| (true, s)),
-                )
-            } else if let Some(feature) = feature.strip_prefix('-') {
-                // FIXME: Why do we not remove implied features on "-" here?
-                // We do the equivalent above in `target_config`.
-                // See <https://github.com/rust-lang/rust/issues/134792>.
-                all_rust_features.push((false, feature));
-            } else if !feature.is_empty() {
-                if diagnostics {
-                    sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
-                }
-            }
-        }
-        // Remove features that are meant for rustc, not LLVM.
-        all_rust_features.retain(|(_, feature)| {
-            // Retain if it is not a rustc feature
-            !RUSTC_SPECIFIC_FEATURES.contains(feature)
-        });
-
-        // Check feature validity.
-        if diagnostics {
-            for &(enable, feature) in &all_rust_features {
-                let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
-                match feature_state {
-                    None => {
-                        let rust_feature =
-                            known_features.iter().find_map(|&(rust_feature, _, _)| {
-                                let llvm_features = to_llvm_features(sess, rust_feature)?;
-                                if llvm_features.contains(feature)
-                                    && !llvm_features.contains(rust_feature)
-                                {
-                                    Some(rust_feature)
-                                } else {
-                                    None
-                                }
-                            });
-                        let unknown_feature = if let Some(rust_feature) = rust_feature {
-                            UnknownCTargetFeature {
-                                feature,
-                                rust_feature: PossibleFeature::Some { rust_feature },
-                            }
-                        } else {
-                            UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
-                        };
-                        sess.dcx().emit_warn(unknown_feature);
-                    }
-                    Some((_, stability, _)) => {
-                        stability.verify_feature_enabled_by_flag(sess, enable, feature);
-                    }
-                }
-
-                // FIXME(nagisa): figure out how to not allocate a full hashset here.
-                featsmap.insert(feature, enable);
-            }
-        }
-
-        // Translate this into LLVM features.
-        let feats = all_rust_features
-            .iter()
-            .filter_map(|&(enable, feature)| {
+        target_features::flag_to_backend_features(
+            sess,
+            diagnostics,
+            |feature| {
+                to_llvm_features(sess, feature)
+                    .map(|f| SmallVec::<[&str; 2]>::from_iter(f.into_iter()))
+                    .unwrap_or_default()
+            },
+            |feature, enable| {
                 let enable_disable = if enable { '+' } else { '-' };
                 // We run through `to_llvm_features` when
                 // passing requests down to LLVM. This means that all in-language
                 // features also work on the command line instead of having two
                 // different names when the LLVM name and the Rust name differ.
-                let llvm_feature = to_llvm_features(sess, feature)?;
+                let Some(llvm_feature) = to_llvm_features(sess, feature) else { return };
 
-                Some(
+                features.extend(
                     std::iter::once(format!(
                         "{}{}",
                         enable_disable, llvm_feature.llvm_feature_name
@@ -881,27 +743,12 @@ pub(crate) fn global_llvm_features(
                         },
                     )),
                 )
-            })
-            .flatten();
-        features.extend(feats);
-
-        if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
-            sess.dcx().emit_err(rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable {
-                features: f,
-                span: None,
-                missing_features: None,
-            });
-        }
+            },
+        );
     }
 
-    // -Zfixed-x18
-    if sess.opts.unstable_opts.fixed_x18 {
-        if sess.target.arch != "aarch64" {
-            sess.dcx().emit_fatal(FixedX18InvalidArch { arch: &sess.target.arch });
-        } else {
-            features.push("+reserve-x18".into());
-        }
-    }
+    // We add this in the "base target" so that these show up in `sess.unstable_target_features`.
+    llvm_features_by_flags(sess, &mut features);
 
     features
 }
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 5322fe5..2bd8644 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -48,8 +48,6 @@
 
 codegen_ssa_expected_name_value_pair = expected name value pair
 
-codegen_ssa_expected_one_argument = expected one argument
-
 codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
 
 codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
@@ -68,6 +66,11 @@
 
 codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
 
+codegen_ssa_forbidden_ctarget_feature =
+    target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
+    .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+codegen_ssa_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
 codegen_ssa_forbidden_target_feature_attr =
     target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason}
 
@@ -86,9 +89,6 @@
 
 codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient.
 
-codegen_ssa_invalid_argument = invalid argument
-    .help = valid inline arguments are `always` and `never`
-
 codegen_ssa_invalid_instruction_set = invalid instruction set specified
 
 codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]`
@@ -368,8 +368,22 @@
 codegen_ssa_unknown_archive_kind =
     Don't know how to build archive of type: {$kind}
 
+codegen_ssa_unknown_ctarget_feature =
+    unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
+    .possible_feature = you might have meant: `{$rust_feature}`
+    .consider_filing_feature_request = consider filing a feature request
+
+codegen_ssa_unknown_ctarget_feature_prefix =
+    unknown feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = features must begin with a `+` to enable or `-` to disable it
+
 codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified
 
+codegen_ssa_unstable_ctarget_feature =
+    unstable feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = this feature is not stably supported; its behavior can change in the future
+
 codegen_ssa_unsupported_instruction_set = target does not support `#[instruction_set]`
 
 codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index bbf9cce..c3bfe4c 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -14,10 +14,10 @@
 use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
 use rustc_errors::emitter::Emitter;
-use rustc_errors::translation::Translate;
+use rustc_errors::translation::Translator;
 use rustc_errors::{
-    Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FluentBundle, Level, MultiSpan,
-    Style, Suggestions,
+    Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, Level, MultiSpan, Style,
+    Suggestions,
 };
 use rustc_fs_util::link_or_copy;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
@@ -1889,16 +1889,6 @@ fn fatal(&self, msg: &str) {
     }
 }
 
-impl Translate for SharedEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        None
-    }
-
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        panic!("shared emitter attempted to translate a diagnostic");
-    }
-}
-
 impl Emitter for SharedEmitter {
     fn emit_diagnostic(
         &mut self,
@@ -1932,6 +1922,10 @@ fn emit_diagnostic(
     fn source_map(&self) -> Option<&SourceMap> {
         None
     }
+
+    fn translator(&self) -> &Translator {
+        panic!("shared emitter attempted to translate a diagnostic");
+    }
 }
 
 impl SharedEmitterMain {
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 9874225..39818be 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -4,9 +4,8 @@
 use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
 use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
 use rustc_attr_data_structures::{
-    AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, find_attr,
+    AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, ReprAttr, find_attr,
 };
-use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
@@ -18,13 +17,15 @@
 use rustc_middle::query::Providers;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self as ty, TyCtxt};
+use rustc_session::lint;
 use rustc_session::parse::feature_err;
-use rustc_session::{Session, lint};
 use rustc_span::{Ident, Span, sym};
 use rustc_target::spec::SanitizerSet;
 
 use crate::errors;
-use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
+use crate::target_features::{
+    check_target_feature_trait_unsafe, check_tied_features, from_target_feature_attr,
+};
 
 fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
     use rustc_middle::mir::mono::Linkage::*;
@@ -109,8 +110,20 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             }
         };
 
-        if let hir::Attribute::Parsed(AttributeKind::Align { align, .. }) = attr {
-            codegen_fn_attrs.alignment = Some(*align);
+        if let hir::Attribute::Parsed(p) = attr {
+            match p {
+                AttributeKind::Repr(reprs) => {
+                    codegen_fn_attrs.alignment = reprs
+                        .iter()
+                        .filter_map(
+                            |(r, _)| if let ReprAttr::ReprAlign(x) = r { Some(*x) } else { None },
+                        )
+                        .max();
+                }
+                AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
+                AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
+                _ => {}
+            }
         }
 
         let Some(Ident { name, .. }) = attr.ident() else {
@@ -118,7 +131,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         };
 
         match name {
-            sym::cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
             sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR,
             sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
             sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
@@ -455,33 +467,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         codegen_fn_attrs.inline = InlineAttr::Never;
     }
 
-    codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
-        if !attr.has_name(sym::optimize) {
-            return ia;
-        }
-        if attr.is_word() {
-            tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
-            return ia;
-        }
-        let Some(ref items) = attr.meta_item_list() else {
-            return OptimizeAttr::Default;
-        };
-
-        let [item] = &items[..] else {
-            tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
-            return OptimizeAttr::Default;
-        };
-        if item.has_name(sym::size) {
-            OptimizeAttr::Size
-        } else if item.has_name(sym::speed) {
-            OptimizeAttr::Speed
-        } else if item.has_name(sym::none) {
-            OptimizeAttr::DoNotOptimize
-        } else {
-            tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() });
-            OptimizeAttr::Default
-        }
-    });
+    codegen_fn_attrs.optimize =
+        find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default);
 
     // #73631: closures inherit `#[target_feature]` annotations
     //
@@ -615,25 +602,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     codegen_fn_attrs
 }
 
-/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
-/// combinations are allowed.
-pub fn check_tied_features(
-    sess: &Session,
-    features: &FxHashMap<&str, bool>,
-) -> Option<&'static [&'static str]> {
-    if !features.is_empty() {
-        for tied in sess.target.tied_target_features() {
-            // Tied features must be set to the same value, or not set at all
-            let mut tied_iter = tied.iter();
-            let enabled = features.get(tied_iter.next().unwrap());
-            if tied_iter.any(|f| enabled != features.get(f)) {
-                return Some(tied);
-            }
-        }
-    }
-    None
-}
-
 /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
 /// applied to the method prototype.
 fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 5387b2a..72e71b9 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -209,20 +209,6 @@ pub(crate) struct OutOfRangeInteger {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_ssa_expected_one_argument, code = E0722)]
-pub(crate) struct ExpectedOneArgumentOptimize {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_ssa_invalid_argument, code = E0722)]
-pub(crate) struct InvalidArgumentOptimize {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_ssa_copy_path_buf)]
 pub(crate) struct CopyPathBuf {
     pub source_file: PathBuf,
@@ -1217,30 +1203,6 @@ pub(crate) struct ErrorCreatingImportLibrary<'a> {
     pub error: String,
 }
 
-pub struct TargetFeatureDisableOrEnable<'a> {
-    pub features: &'a [&'a str],
-    pub span: Option<Span>,
-    pub missing_features: Option<MissingFeatures>,
-}
-
-#[derive(Subdiagnostic)]
-#[help(codegen_ssa_missing_features)]
-pub struct MissingFeatures;
-
-impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
-        let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable);
-        if let Some(span) = self.span {
-            diag.span(span);
-        };
-        if let Some(missing_features) = self.missing_features {
-            diag.subdiagnostic(missing_features);
-        }
-        diag.arg("features", self.features.join(", "));
-        diag
-    }
-}
-
 #[derive(Diagnostic)]
 #[diag(codegen_ssa_aix_strip_not_used)]
 pub(crate) struct AixStripNotUsed;
@@ -1283,3 +1245,68 @@ pub(crate) struct XcrunSdkPathWarning {
 #[derive(LintDiagnostic)]
 #[diag(codegen_ssa_aarch64_softfloat_neon)]
 pub(crate) struct Aarch64SoftfloatNeon;
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unknown_ctarget_feature_prefix)]
+#[note]
+pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
+    pub feature: &'a str,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum PossibleFeature<'a> {
+    #[help(codegen_ssa_possible_feature)]
+    Some { rust_feature: &'a str },
+    #[help(codegen_ssa_consider_filing_feature_request)]
+    None,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unknown_ctarget_feature)]
+#[note]
+pub(crate) struct UnknownCTargetFeature<'a> {
+    pub feature: &'a str,
+    #[subdiagnostic]
+    pub rust_feature: PossibleFeature<'a>,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unstable_ctarget_feature)]
+#[note]
+pub(crate) struct UnstableCTargetFeature<'a> {
+    pub feature: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_forbidden_ctarget_feature)]
+#[note]
+#[note(codegen_ssa_forbidden_ctarget_feature_issue)]
+pub(crate) struct ForbiddenCTargetFeature<'a> {
+    pub feature: &'a str,
+    pub enabled: &'a str,
+    pub reason: &'a str,
+}
+
+pub struct TargetFeatureDisableOrEnable<'a> {
+    pub features: &'a [&'a str],
+    pub span: Option<Span>,
+    pub missing_features: Option<MissingFeatures>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(codegen_ssa_missing_features)]
+pub struct MissingFeatures;
+
+impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
+        let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable);
+        if let Some(span) = self.span {
+            diag.span(span);
+        };
+        if let Some(missing_features) = self.missing_features {
+            diag.subdiagnostic(missing_features);
+        }
+        diag.arg("features", self.features.join(", "));
+        diag
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 640d197..67ac619 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -1,5 +1,5 @@
 use rustc_attr_data_structures::InstructionSetAttr;
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -8,11 +8,12 @@
 use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::features::StabilityExt;
+use rustc_session::Session;
 use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
 use rustc_session::parse::feature_err;
 use rustc_span::{Span, Symbol, sym};
-use rustc_target::target_features::{self, Stability};
+use rustc_target::target_features::{self, RUSTC_SPECIFIC_FEATURES, Stability};
+use smallvec::SmallVec;
 
 use crate::errors;
 
@@ -67,7 +68,7 @@ pub(crate) fn from_target_feature_attr(
 
             // Only allow target features whose feature gates have been enabled
             // and which are permitted to be toggled.
-            if let Err(reason) = stability.is_toggle_permitted(tcx.sess) {
+            if let Err(reason) = stability.toggle_allowed() {
                 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
                     span: item.span(),
                     feature,
@@ -88,7 +89,7 @@ pub(crate) fn from_target_feature_attr(
                 let feature_sym = Symbol::intern(feature);
                 for &name in tcx.implied_target_features(feature_sym) {
                     // But ensure the ABI does not forbid enabling this.
-                    // Here we do assume that LLVM doesn't add even more implied features
+                    // Here we do assume that the backend doesn't add even more implied features
                     // we don't know about, at least no features that would have ABI effects!
                     // We skip this logic in rustdoc, where we want to allow all target features of
                     // all targets, so we can't check their ABI compatibility and anyway we are not
@@ -156,6 +157,276 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId,
     }
 }
 
+/// Parse the value of `-Ctarget-feature`, also expanding implied features,
+/// and call the closure for each (expanded) Rust feature. If the list contains
+/// a syntactically invalid item (not starting with `+`/`-`), the error callback is invoked.
+fn parse_rust_feature_flag<'a>(
+    sess: &'a Session,
+    err_callback: impl Fn(&'a str),
+    mut callback: impl FnMut(
+        /* base_feature */ &'a str,
+        /* with_implied */ FxHashSet<&'a str>,
+        /* enable */ bool,
+    ),
+) {
+    // A cache for the backwards implication map.
+    let mut inverse_implied_features: Option<FxHashMap<&str, FxHashSet<&str>>> = None;
+
+    for feature in sess.opts.cg.target_feature.split(',') {
+        if let Some(base_feature) = feature.strip_prefix('+') {
+            // Skip features that are not target features, but rustc features.
+            if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
+                return;
+            }
+
+            callback(base_feature, sess.target.implied_target_features(base_feature), true)
+        } else if let Some(base_feature) = feature.strip_prefix('-') {
+            // Skip features that are not target features, but rustc features.
+            if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
+                return;
+            }
+
+            // If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical
+            // contraposition. So we have to find all the reverse implications of `base_feature` and
+            // disable them, too.
+
+            let inverse_implied_features = inverse_implied_features.get_or_insert_with(|| {
+                let mut set: FxHashMap<&str, FxHashSet<&str>> = FxHashMap::default();
+                for (f, _, is) in sess.target.rust_target_features() {
+                    for i in is.iter() {
+                        set.entry(i).or_default().insert(f);
+                    }
+                }
+                set
+            });
+
+            // Inverse implied target features have their own inverse implied target features, so we
+            // traverse the map until there are no more features to add.
+            let mut features = FxHashSet::default();
+            let mut new_features = vec![base_feature];
+            while let Some(new_feature) = new_features.pop() {
+                if features.insert(new_feature) {
+                    if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
+                        new_features.extend(implied_features)
+                    }
+                }
+            }
+
+            callback(base_feature, features, false)
+        } else if !feature.is_empty() {
+            err_callback(feature)
+        }
+    }
+}
+
+/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
+/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
+/// 2nd component of the return value, respectively).
+///
+/// `target_base_has_feature` should check whether the given feature (a Rust feature name!) is
+/// enabled in the "base" target machine, i.e., without applying `-Ctarget-feature`.
+///
+/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere.
+pub fn cfg_target_feature(
+    sess: &Session,
+    mut target_base_has_feature: impl FnMut(&str) -> bool,
+) -> (Vec<Symbol>, Vec<Symbol>) {
+    // Compute which of the known target features are enabled in the 'base' target machine. We only
+    // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
+    let mut features: UnordSet<Symbol> = sess
+        .target
+        .rust_target_features()
+        .iter()
+        .filter(|(feature, _, _)| target_base_has_feature(feature))
+        .map(|(feature, _, _)| Symbol::intern(feature))
+        .collect();
+
+    // Add enabled and remove disabled features.
+    parse_rust_feature_flag(
+        sess,
+        /* err_callback */
+        |_| {
+            // Errors are already emitted in `flag_to_backend_features`; avoid duplicates.
+        },
+        |_base_feature, new_features, enabled| {
+            // Iteration order is irrelevant since this only influences an `UnordSet`.
+            #[allow(rustc::potential_query_instability)]
+            if enabled {
+                features.extend(new_features.into_iter().map(|f| Symbol::intern(f)));
+            } else {
+                // Remove `new_features` from `features`.
+                for new in new_features {
+                    features.remove(&Symbol::intern(new));
+                }
+            }
+        },
+    );
+
+    // Filter enabled features based on feature gates.
+    let f = |allow_unstable| {
+        sess.target
+            .rust_target_features()
+            .iter()
+            .filter_map(|(feature, gate, _)| {
+                // The `allow_unstable` set is used by rustc internally to determine which target
+                // features are truly available, so we want to return even perma-unstable
+                // "forbidden" features.
+                if allow_unstable
+                    || (gate.in_cfg()
+                        && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
+                {
+                    Some(Symbol::intern(feature))
+                } else {
+                    None
+                }
+            })
+            .filter(|feature| features.contains(&feature))
+            .collect()
+    };
+
+    (f(true), f(false))
+}
+
+/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
+/// combinations are allowed.
+pub fn check_tied_features(
+    sess: &Session,
+    features: &FxHashMap<&str, bool>,
+) -> Option<&'static [&'static str]> {
+    if !features.is_empty() {
+        for tied in sess.target.tied_target_features() {
+            // Tied features must be set to the same value, or not set at all
+            let mut tied_iter = tied.iter();
+            let enabled = features.get(tied_iter.next().unwrap());
+            if tied_iter.any(|f| enabled != features.get(f)) {
+                return Some(tied);
+            }
+        }
+    }
+    None
+}
+
+/// Translates the `-Ctarget-feature` flag into a backend target feature list.
+///
+/// `to_backend_features` converts a Rust feature name into a list of backend feature names; this is
+/// used for diagnostic purposes only.
+///
+/// `extend_backend_features` extends the set of backend features (assumed to be in mutable state
+/// accessible by that closure) to enable/disable the given Rust feature name.
+pub fn flag_to_backend_features<'a, const N: usize>(
+    sess: &'a Session,
+    diagnostics: bool,
+    to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>,
+    mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool),
+) {
+    let known_features = sess.target.rust_target_features();
+
+    // Compute implied features
+    let mut rust_features = vec![];
+    parse_rust_feature_flag(
+        sess,
+        /* err_callback */
+        |feature| {
+            if diagnostics {
+                sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature });
+            }
+        },
+        |base_feature, new_features, enable| {
+            rust_features.extend(
+                UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)),
+            );
+            // Check feature validity.
+            if diagnostics {
+                let feature_state = known_features.iter().find(|&&(v, _, _)| v == base_feature);
+                match feature_state {
+                    None => {
+                        // This is definitely not a valid Rust feature name. Maybe it is a backend
+                        // feature name? If so, give a better error message.
+                        let rust_feature =
+                            known_features.iter().find_map(|&(rust_feature, _, _)| {
+                                let backend_features = to_backend_features(rust_feature);
+                                if backend_features.contains(&base_feature)
+                                    && !backend_features.contains(&rust_feature)
+                                {
+                                    Some(rust_feature)
+                                } else {
+                                    None
+                                }
+                            });
+                        let unknown_feature = if let Some(rust_feature) = rust_feature {
+                            errors::UnknownCTargetFeature {
+                                feature: base_feature,
+                                rust_feature: errors::PossibleFeature::Some { rust_feature },
+                            }
+                        } else {
+                            errors::UnknownCTargetFeature {
+                                feature: base_feature,
+                                rust_feature: errors::PossibleFeature::None,
+                            }
+                        };
+                        sess.dcx().emit_warn(unknown_feature);
+                    }
+                    Some((_, stability, _)) => {
+                        if let Err(reason) = stability.toggle_allowed() {
+                            sess.dcx().emit_warn(errors::ForbiddenCTargetFeature {
+                                feature: base_feature,
+                                enabled: if enable { "enabled" } else { "disabled" },
+                                reason,
+                            });
+                        } else if stability.requires_nightly().is_some() {
+                            // An unstable feature. Warn about using it. It makes little sense
+                            // to hard-error here since we just warn about fully unknown
+                            // features above.
+                            sess.dcx().emit_warn(errors::UnstableCTargetFeature {
+                                feature: base_feature,
+                            });
+                        }
+                    }
+                }
+            }
+        },
+    );
+
+    if diagnostics {
+        // FIXME(nagisa): figure out how to not allocate a full hashmap here.
+        if let Some(f) = check_tied_features(
+            sess,
+            &FxHashMap::from_iter(rust_features.iter().map(|&(enable, feature)| (feature, enable))),
+        ) {
+            sess.dcx().emit_err(errors::TargetFeatureDisableOrEnable {
+                features: f,
+                span: None,
+                missing_features: None,
+            });
+        }
+    }
+
+    // Add this to the backend features.
+    for (enable, feature) in rust_features {
+        extend_backend_features(feature, enable);
+    }
+}
+
+/// Computes the backend target features to be added to account for retpoline flags.
+/// Used by both LLVM and GCC since their target features are, conveniently, the same.
+pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<String>) {
+    // -Zretpoline without -Zretpoline-external-thunk enables
+    // retpoline-indirect-branches and retpoline-indirect-calls target features
+    let unstable_opts = &sess.opts.unstable_opts;
+    if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
+        features.push("+retpoline-indirect-branches".into());
+        features.push("+retpoline-indirect-calls".into());
+    }
+    // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables
+    // retpoline-external-thunk, retpoline-indirect-branches and
+    // retpoline-indirect-calls target features
+    if unstable_opts.retpoline_external_thunk {
+        features.push("+retpoline-external-thunk".into());
+        features.push("+retpoline-indirect-branches".into());
+        features.push("+retpoline-indirect-calls".into());
+    }
+}
+
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         rust_target_features: |tcx, cnum| {
@@ -182,7 +453,8 @@ pub(crate) fn provide(providers: &mut Providers) {
                                     Stability::Unstable { .. } | Stability::Forbidden { .. },
                                 )
                                 | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
-                                    // The stability in the entry is at least as good as the new one, just keep it.
+                                    // The stability in the entry is at least as good as the new
+                                    // one, just keep it.
                                 }
                                 _ => {
                                     // Overwrite stabilite.
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 4f252f3..576b174 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -463,12 +463,6 @@ pub fn check_drop_terminator(
         );
     }
 
-    fn crate_inject_span(&self) -> Option<Span> {
-        self.tcx.hir_crate_items(()).definitions().next().and_then(|id| {
-            self.tcx.crate_level_attribute_injection_span(self.tcx.local_def_id_to_hir_id(id))
-        })
-    }
-
     /// Check the const stability of the given item (fn or trait).
     fn check_callee_stability(&mut self, def_id: DefId) {
         match self.tcx.lookup_const_stability(def_id) {
@@ -543,7 +537,6 @@ fn check_callee_stability(&mut self, def_id: DefId) {
                         feature,
                         feature_enabled,
                         safe_to_expose_on_stable: callee_safe_to_expose_on_stable,
-                        suggestion_span: self.crate_inject_span(),
                         is_function_call: self.tcx.def_kind(def_id) != DefKind::Trait,
                     });
                 }
@@ -919,7 +912,6 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
                                 name: intrinsic.name,
                                 feature,
                                 const_stable_indirect: is_const_stable,
-                                suggestion: self.crate_inject_span(),
                             });
                         }
                         Some(attrs::ConstStability {
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index 9c30dbf..887275e 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -1,8 +1,8 @@
 //! Concrete error types for all operations which may be invalid in a certain const context.
 
 use hir::{ConstContext, LangItem};
+use rustc_errors::Diag;
 use rustc_errors::codes::*;
-use rustc_errors::{Applicability, Diag};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::TyCtxtInferExt;
@@ -384,7 +384,6 @@ pub(crate) struct CallUnstable {
     /// expose on stable.
     pub feature_enabled: bool,
     pub safe_to_expose_on_stable: bool,
-    pub suggestion_span: Option<Span>,
     /// true if `def_id` is the function we are calling, false if `def_id` is an unstable trait.
     pub is_function_call: bool,
 }
@@ -412,20 +411,7 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
                 def_path: ccx.tcx.def_path_str(self.def_id),
             })
         };
-        // FIXME: make this translatable
-        let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature);
-        #[allow(rustc::untranslatable_diagnostic)]
-        if let Some(span) = self.suggestion_span {
-            err.span_suggestion_verbose(
-                span,
-                msg,
-                format!("#![feature({})]\n", self.feature),
-                Applicability::MachineApplicable,
-            );
-        } else {
-            err.help(msg);
-        }
-
+        ccx.tcx.disabled_nightly_features(&mut err, [(String::new(), self.feature)]);
         err
     }
 }
@@ -452,7 +438,6 @@ pub(crate) struct IntrinsicUnstable {
     pub name: Symbol,
     pub feature: Symbol,
     pub const_stable_indirect: bool,
-    pub suggestion: Option<Span>,
 }
 
 impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
@@ -472,8 +457,7 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
             span,
             name: self.name,
             feature: self.feature,
-            suggestion: self.suggestion,
-            help: self.suggestion.is_none(),
+            suggestion: ccx.tcx.crate_level_attribute_injection_span(),
         })
     }
 }
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 037cbf7..69c71ae 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -136,9 +136,7 @@ pub(crate) struct UnstableIntrinsic {
         code = "#![feature({feature})]\n",
         applicability = "machine-applicable"
     )]
-    pub suggestion: Option<Span>,
-    #[help(const_eval_unstable_intrinsic_suggestion)]
-    pub help: bool,
+    pub suggestion: Span,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 99a4bc1..36d1a41 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -1412,8 +1412,13 @@ pub fn mem_copy_repeatedly(
         let src_alloc = self.get_alloc_raw(src_alloc_id)?;
         let src_range = alloc_range(src_offset, size);
         assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation");
-        // For the overlapping case, it is crucial that we trigger the read hook
+
+        // Trigger read hooks.
+        // For the overlapping case, it is crucial that we trigger the read hooks
         // before the write hook -- the aliasing model cares about the order.
+        if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(src, size.bytes() as i64) {
+            M::before_alloc_read(self, alloc_id)?;
+        }
         M::before_memory_read(
             tcx,
             &self.machine,
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index d53126d..daeca43 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -38,6 +38,7 @@
 };
 use rustc_errors::emitter::stderr_destination;
 use rustc_errors::registry::Registry;
+use rustc_errors::translation::Translator;
 use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown};
 use rustc_feature::find_gated_cfg;
 // This avoids a false positive with `-Wunused_crate_dependencies`.
@@ -109,6 +110,10 @@ pub(super) fn install() {}
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
+pub fn default_translator() -> Translator {
+    Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false)
+}
+
 pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
     // tidy-alphabetical-start
     crate::DEFAULT_LOCALE_RESOURCE,
@@ -1413,11 +1418,10 @@ fn report_ice(
     extra_info: fn(&DiagCtxt),
     using_internal_features: &AtomicBool,
 ) {
-    let fallback_bundle =
-        rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
+    let translator = default_translator();
     let emitter = Box::new(rustc_errors::emitter::HumanEmitter::new(
         stderr_destination(rustc_errors::ColorConfig::Auto),
-        fallback_bundle,
+        translator,
     ));
     let dcx = rustc_errors::DiagCtxt::new(emitter);
     let dcx = dcx.handle();
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index ec77043..688307a 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -292,7 +292,11 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
         }
         HirTree => {
             debug!("pretty printing HIR tree");
-            format!("{:#?}", ex.tcx().hir_crate(()))
+            ex.tcx()
+                .hir_crate_items(())
+                .owners()
+                .map(|owner| format!("{:#?} => {:#?}\n", owner, ex.tcx().hir_owner_nodes(owner)))
+                .collect()
         }
         Mir => {
             let mut out = Vec::new();
diff --git a/compiler/rustc_error_codes/src/error_codes/E0722.md b/compiler/rustc_error_codes/src/error_codes/E0722.md
index 570717a..1799458 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0722.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0722.md
@@ -1,8 +1,14 @@
+#### Note: this error code is no longer emitted by the compiler
+
+This is because it was too specific to the `optimize` attribute.
+Similar diagnostics occur for other attributes too.
+The example here will now emit `E0539`
+
 The `optimize` attribute was malformed.
 
 Erroneous code example:
 
-```compile_fail,E0722
+```compile_fail,E0539
 #![feature(optimize_attribute)]
 
 #[optimize(something)] // error: invalid argument
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index 6f5e482..22cc1e8 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -686,6 +686,7 @@ macro_rules! error_codes {
 //  E0707, // multiple elided lifetimes used in arguments of `async fn`
 //  E0709, // multiple different lifetimes used in arguments of `async fn`
 //  E0721, // `await` keyword
+//  E0722, // replaced with a generic attribute input check
 //  E0723, // unstable feature in `const` context
 //  E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
 //  E0744, // merged into E0728
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 1d3b5b2..194fc24 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -18,7 +18,7 @@
 use fluent_syntax::parser::ParserError;
 use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker};
 use intl_memoizer::concurrent::IntlLangMemoizer;
-use rustc_data_structures::sync::IntoDynSyncSend;
+use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
 use rustc_macros::{Decodable, Encodable};
 use rustc_span::Span;
 use smallvec::SmallVec;
@@ -204,16 +204,16 @@ fn register_functions(bundle: &mut FluentBundle) {
 
 /// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily
 /// evaluated fluent bundle.
-pub type LazyFallbackBundle = Arc<LazyLock<FluentBundle, impl FnOnce() -> FluentBundle>>;
+pub type LazyFallbackBundle =
+    Arc<LazyLock<FluentBundle, Box<dyn FnOnce() -> FluentBundle + DynSend>>>;
 
 /// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
 #[instrument(level = "trace", skip(resources))]
-#[define_opaque(LazyFallbackBundle)]
 pub fn fallback_fluent_bundle(
     resources: Vec<&'static str>,
     with_directionality_markers: bool,
 ) -> LazyFallbackBundle {
-    Arc::new(LazyLock::new(move || {
+    Arc::new(LazyLock::new(Box::new(move || {
         let mut fallback_bundle = new_bundle(vec![langid!("en-US")]);
 
         register_functions(&mut fallback_bundle);
@@ -228,7 +228,7 @@ pub fn fallback_fluent_bundle(
         }
 
         fallback_bundle
-    }))
+    })))
 }
 
 /// Identifier for the Fluent message/attribute corresponding to a diagnostic message.
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index f3aeb8d..2eb3c23 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -15,17 +15,15 @@
 use crate::emitter::FileWithAnnotatedLines;
 use crate::registry::Registry;
 use crate::snippet::Line;
-use crate::translation::{Translate, to_fluent_args};
+use crate::translation::{Translator, to_fluent_args};
 use crate::{
-    CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, FluentBundle, LazyFallbackBundle,
-    Level, MultiSpan, Style, Subdiag,
+    CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag,
 };
 
 /// Generates diagnostics using annotate-snippet
 pub struct AnnotateSnippetEmitter {
     source_map: Option<Arc<SourceMap>>,
-    fluent_bundle: Option<Arc<FluentBundle>>,
-    fallback_bundle: LazyFallbackBundle,
+    translator: Translator,
 
     /// If true, hides the longer explanation text
     short_message: bool,
@@ -35,16 +33,6 @@ pub struct AnnotateSnippetEmitter {
     macro_backtrace: bool,
 }
 
-impl Translate for AnnotateSnippetEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        self.fluent_bundle.as_deref()
-    }
-
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        &self.fallback_bundle
-    }
-}
-
 impl Emitter for AnnotateSnippetEmitter {
     /// The entry point for the diagnostics generation
     fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
@@ -78,6 +66,10 @@ fn source_map(&self) -> Option<&SourceMap> {
     fn should_show_explain(&self) -> bool {
         !self.short_message
     }
+
+    fn translator(&self) -> &Translator {
+        &self.translator
+    }
 }
 
 /// Provides the source string for the given `line` of `file`
@@ -104,19 +96,11 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
 impl AnnotateSnippetEmitter {
     pub fn new(
         source_map: Option<Arc<SourceMap>>,
-        fluent_bundle: Option<Arc<FluentBundle>>,
-        fallback_bundle: LazyFallbackBundle,
+        translator: Translator,
         short_message: bool,
         macro_backtrace: bool,
     ) -> Self {
-        Self {
-            source_map,
-            fluent_bundle,
-            fallback_bundle,
-            short_message,
-            ui_testing: false,
-            macro_backtrace,
-        }
+        Self { source_map, translator, short_message, ui_testing: false, macro_backtrace }
     }
 
     /// Allows to modify `Self` to enable or disable the `ui_testing` flag.
@@ -137,7 +121,7 @@ fn emit_messages_default(
         _children: &[Subdiag],
         _suggestions: &[CodeSuggestion],
     ) {
-        let message = self.translate_messages(messages, args);
+        let message = self.translator.translate_messages(messages, args);
         if let Some(source_map) = &self.source_map {
             // Make sure our primary file comes first
             let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 6ab6f96..e333de4 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -35,10 +35,10 @@
 };
 use crate::styled_buffer::StyledBuffer;
 use crate::timings::TimingRecord;
-use crate::translation::{Translate, to_fluent_args};
+use crate::translation::{Translator, to_fluent_args};
 use crate::{
-    CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level,
-    MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl,
+    CodeSuggestion, DiagInner, DiagMessage, ErrCode, Level, MultiSpan, Subdiag,
+    SubstitutionHighlight, SuggestionStyle, TerminalUrl,
 };
 
 /// Default column width, used in tests and when terminal dimensions cannot be determined.
@@ -175,7 +175,7 @@ pub enum TimingEvent {
 pub type DynEmitter = dyn Emitter + DynSend;
 
 /// Emitter trait for emitting errors and other structured information.
-pub trait Emitter: Translate {
+pub trait Emitter {
     /// Emit a structured diagnostic.
     fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry);
 
@@ -212,6 +212,8 @@ fn supports_color(&self) -> bool {
 
     fn source_map(&self) -> Option<&SourceMap>;
 
+    fn translator(&self) -> &Translator;
+
     /// Formats the substitutions of the primary_span
     ///
     /// There are a lot of conditions to this method, but in short:
@@ -224,13 +226,17 @@ fn supports_color(&self) -> bool {
     /// * If the current `DiagInner` has multiple suggestions,
     ///   we leave `primary_span` and the suggestions untouched.
     fn primary_span_formatted(
-        &mut self,
+        &self,
         primary_span: &mut MultiSpan,
         suggestions: &mut Vec<CodeSuggestion>,
         fluent_args: &FluentArgs<'_>,
     ) {
         if let Some((sugg, rest)) = suggestions.split_first() {
-            let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap();
+            let msg = self
+                .translator()
+                .translate_message(&sugg.msg, fluent_args)
+                .map_err(Report::new)
+                .unwrap();
             if rest.is_empty()
                // ^ if there is only one suggestion
                // don't display multi-suggestions as labels
@@ -491,16 +497,6 @@ fn fix_multispan_in_extern_macros(&self, span: &mut MultiSpan) {
     }
 }
 
-impl Translate for HumanEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        self.fluent_bundle.as_deref()
-    }
-
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        &self.fallback_bundle
-    }
-}
-
 impl Emitter for HumanEmitter {
     fn source_map(&self) -> Option<&SourceMap> {
         self.sm.as_deref()
@@ -538,25 +534,41 @@ fn should_show_explain(&self) -> bool {
     fn supports_color(&self) -> bool {
         self.dst.supports_color()
     }
+
+    fn translator(&self) -> &Translator {
+        &self.translator
+    }
 }
 
 /// An emitter that does nothing when emitting a non-fatal diagnostic.
 /// Fatal diagnostics are forwarded to `fatal_emitter` to avoid silent
 /// failures of rustc, as witnessed e.g. in issue #89358.
-pub struct SilentEmitter {
+pub struct FatalOnlyEmitter {
     pub fatal_emitter: Box<dyn Emitter + DynSend>,
     pub fatal_note: Option<String>,
-    pub emit_fatal_diagnostic: bool,
 }
 
-impl Translate for SilentEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
+impl Emitter for FatalOnlyEmitter {
+    fn source_map(&self) -> Option<&SourceMap> {
         None
     }
 
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        self.fatal_emitter.fallback_fluent_bundle()
+    fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) {
+        if diag.level == Level::Fatal {
+            if let Some(fatal_note) = &self.fatal_note {
+                diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
+            }
+            self.fatal_emitter.emit_diagnostic(diag, registry);
+        }
     }
+
+    fn translator(&self) -> &Translator {
+        self.fatal_emitter.translator()
+    }
+}
+
+pub struct SilentEmitter {
+    pub translator: Translator,
 }
 
 impl Emitter for SilentEmitter {
@@ -564,13 +576,10 @@ fn source_map(&self) -> Option<&SourceMap> {
         None
     }
 
-    fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) {
-        if self.emit_fatal_diagnostic && diag.level == Level::Fatal {
-            if let Some(fatal_note) = &self.fatal_note {
-                diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
-            }
-            self.fatal_emitter.emit_diagnostic(diag, registry);
-        }
+    fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {}
+
+    fn translator(&self) -> &Translator {
+        &self.translator
     }
 }
 
@@ -615,9 +624,8 @@ pub struct HumanEmitter {
     #[setters(skip)]
     dst: IntoDynSyncSend<Destination>,
     sm: Option<Arc<SourceMap>>,
-    fluent_bundle: Option<Arc<FluentBundle>>,
     #[setters(skip)]
-    fallback_bundle: LazyFallbackBundle,
+    translator: Translator,
     short_message: bool,
     ui_testing: bool,
     ignored_directories_in_source_blocks: Vec<String>,
@@ -637,12 +645,11 @@ pub(crate) struct FileWithAnnotatedLines {
 }
 
 impl HumanEmitter {
-    pub fn new(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter {
+    pub fn new(dst: Destination, translator: Translator) -> HumanEmitter {
         HumanEmitter {
             dst: IntoDynSyncSend(dst),
             sm: None,
-            fluent_bundle: None,
-            fallback_bundle,
+            translator,
             short_message: false,
             ui_testing: false,
             ignored_directories_in_source_blocks: Vec::new(),
@@ -1433,7 +1440,7 @@ fn style_or_override(style: Style, override_: Option<Style>) -> Style {
         //                very *weird* formats
         //                see?
         for (text, style) in msgs.iter() {
-            let text = self.translate_message(text, args).map_err(Report::new).unwrap();
+            let text = self.translator.translate_message(text, args).map_err(Report::new).unwrap();
             let text = &normalize_whitespace(&text);
             let lines = text.split('\n').collect::<Vec<_>>();
             if lines.len() > 1 {
@@ -1528,7 +1535,8 @@ fn emit_messages_default_inner(
             }
             let mut line = 0;
             for (text, style) in msgs.iter() {
-                let text = self.translate_message(text, args).map_err(Report::new).unwrap();
+                let text =
+                    self.translator.translate_message(text, args).map_err(Report::new).unwrap();
                 // Account for newlines to align output to its label.
                 for text in normalize_whitespace(&text).lines() {
                     buffer.append(
@@ -1560,7 +1568,7 @@ fn emit_messages_default_inner(
                     .into_iter()
                     .filter_map(|label| match label.label {
                         Some(msg) if label.is_primary => {
-                            let text = self.translate_message(&msg, args).ok()?;
+                            let text = self.translator.translate_message(&msg, args).ok()?;
                             if !text.trim().is_empty() { Some(text.to_string()) } else { None }
                         }
                         _ => None,
@@ -3104,7 +3112,11 @@ fn add_annotation_to_file(
 
                 let label = label.as_ref().map(|m| {
                     normalize_whitespace(
-                        &emitter.translate_message(m, args).map_err(Report::new).unwrap(),
+                        &emitter
+                            .translator()
+                            .translate_message(m, args)
+                            .map_err(Report::new)
+                            .unwrap(),
                     )
                 });
 
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index d67e2ba..6d600f8 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -32,11 +32,8 @@
 };
 use crate::registry::Registry;
 use crate::timings::{TimingRecord, TimingSection};
-use crate::translation::{Translate, to_fluent_args};
-use crate::{
-    CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions,
-    TerminalUrl,
-};
+use crate::translation::{Translator, to_fluent_args};
+use crate::{CodeSuggestion, MultiSpan, SpanLabel, Subdiag, Suggestions, TerminalUrl};
 
 #[cfg(test)]
 mod tests;
@@ -47,9 +44,8 @@ pub struct JsonEmitter {
     dst: IntoDynSyncSend<Box<dyn Write + Send>>,
     #[setters(skip)]
     sm: Option<Arc<SourceMap>>,
-    fluent_bundle: Option<Arc<FluentBundle>>,
     #[setters(skip)]
-    fallback_bundle: LazyFallbackBundle,
+    translator: Translator,
     #[setters(skip)]
     pretty: bool,
     ui_testing: bool,
@@ -67,7 +63,7 @@ impl JsonEmitter {
     pub fn new(
         dst: Box<dyn Write + Send>,
         sm: Option<Arc<SourceMap>>,
-        fallback_bundle: LazyFallbackBundle,
+        translator: Translator,
         pretty: bool,
         json_rendered: HumanReadableErrorType,
         color_config: ColorConfig,
@@ -75,8 +71,7 @@ pub fn new(
         JsonEmitter {
             dst: IntoDynSyncSend(dst),
             sm,
-            fluent_bundle: None,
-            fallback_bundle,
+            translator,
             pretty,
             ui_testing: false,
             ignored_directories_in_source_blocks: Vec::new(),
@@ -110,16 +105,6 @@ enum EmitTyped<'a> {
     UnusedExtern(UnusedExterns<'a>),
 }
 
-impl Translate for JsonEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        self.fluent_bundle.as_deref()
-    }
-
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        &self.fallback_bundle
-    }
-}
-
 impl Emitter for JsonEmitter {
     fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) {
         let data = Diagnostic::from_errors_diagnostic(diag, self, registry);
@@ -194,6 +179,10 @@ fn source_map(&self) -> Option<&SourceMap> {
     fn should_show_explain(&self) -> bool {
         !self.json_rendered.short()
     }
+
+    fn translator(&self) -> &Translator {
+        &self.translator
+    }
 }
 
 // The following data types are provided just for serialisation.
@@ -324,7 +313,7 @@ fn from_errors_diagnostic(
         let args = to_fluent_args(diag.args.iter());
         let sugg_to_diag = |sugg: &CodeSuggestion| {
             let translated_message =
-                je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
+                je.translator.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
             Diagnostic {
                 message: translated_message.to_string(),
                 code: None,
@@ -368,7 +357,7 @@ fn reset(&mut self) -> io::Result<()> {
             }
         }
 
-        let translated_message = je.translate_messages(&diag.messages, &args);
+        let translated_message = je.translator.translate_messages(&diag.messages, &args);
 
         let code = if let Some(code) = diag.code {
             Some(DiagnosticCode {
@@ -396,10 +385,9 @@ fn reset(&mut self) -> io::Result<()> {
             ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)),
             ColorConfig::Never => {}
         }
-        HumanEmitter::new(dst, Arc::clone(&je.fallback_bundle))
+        HumanEmitter::new(dst, je.translator.clone())
             .short_message(short)
             .sm(je.sm.clone())
-            .fluent_bundle(je.fluent_bundle.clone())
             .diagnostic_width(je.diagnostic_width)
             .macro_backtrace(je.macro_backtrace)
             .track_diagnostics(je.track_diagnostics)
@@ -430,7 +418,7 @@ fn from_sub_diagnostic(
         args: &FluentArgs<'_>,
         je: &JsonEmitter,
     ) -> Diagnostic {
-        let translated_message = je.translate_messages(&subdiag.messages, args);
+        let translated_message = je.translator.translate_messages(&subdiag.messages, args);
         Diagnostic {
             message: translated_message.to_string(),
             code: None,
@@ -454,7 +442,7 @@ fn from_span_label(
             span.is_primary,
             span.label
                 .as_ref()
-                .map(|m| je.translate_message(m, args).unwrap())
+                .map(|m| je.translator.translate_message(m, args).unwrap())
                 .map(|m| m.to_string()),
             suggestion,
             je,
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index 40973e8..8cf81f4 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -41,14 +41,14 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
     rustc_span::create_default_session_globals_then(|| {
         let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
         sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned());
-        let fallback_bundle =
-            crate::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
+        let translator =
+            Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
 
         let output = Arc::new(Mutex::new(Vec::new()));
         let je = JsonEmitter::new(
             Box::new(Shared { data: output.clone() }),
             Some(sm),
-            fallback_bundle,
+            translator,
             true, // pretty
             HumanReadableErrorType::Short,
             ColorConfig::Never,
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 0bd2593..207aed8 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -748,40 +748,10 @@ pub fn new(emitter: Box<DynEmitter>) -> Self {
         Self { inner: Lock::new(DiagCtxtInner::new(emitter)) }
     }
 
-    pub fn make_silent(&self, fatal_note: Option<String>, emit_fatal_diagnostic: bool) {
-        // An empty type that implements `Emitter` to temporarily swap in place of the real one,
-        // which will be used in constructing its replacement.
-        struct FalseEmitter;
-
-        impl Emitter for FalseEmitter {
-            fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) {
-                unimplemented!("false emitter must only used during `make_silent`")
-            }
-
-            fn source_map(&self) -> Option<&SourceMap> {
-                unimplemented!("false emitter must only used during `make_silent`")
-            }
-        }
-
-        impl translation::Translate for FalseEmitter {
-            fn fluent_bundle(&self) -> Option<&FluentBundle> {
-                unimplemented!("false emitter must only used during `make_silent`")
-            }
-
-            fn fallback_fluent_bundle(&self) -> &FluentBundle {
-                unimplemented!("false emitter must only used during `make_silent`")
-            }
-        }
-
+    pub fn make_silent(&self) {
         let mut inner = self.inner.borrow_mut();
-        let mut prev_emitter = Box::new(FalseEmitter) as Box<dyn Emitter + DynSend>;
-        std::mem::swap(&mut inner.emitter, &mut prev_emitter);
-        let new_emitter = Box::new(emitter::SilentEmitter {
-            fatal_emitter: prev_emitter,
-            fatal_note,
-            emit_fatal_diagnostic,
-        });
-        inner.emitter = new_emitter;
+        let translator = inner.emitter.translator().clone();
+        inner.emitter = Box::new(emitter::SilentEmitter { translator });
     }
 
     pub fn set_emitter(&self, emitter: Box<dyn Emitter + DynSend>) {
@@ -1771,7 +1741,12 @@ fn eagerly_translate_to_string<'a>(
         args: impl Iterator<Item = DiagArg<'a>>,
     ) -> String {
         let args = crate::translation::to_fluent_args(args);
-        self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
+        self.emitter
+            .translator()
+            .translate_message(&message, &args)
+            .map_err(Report::new)
+            .unwrap()
+            .to_string()
     }
 
     fn eagerly_translate_for_subdiag(
diff --git a/compiler/rustc_errors/src/tests.rs b/compiler/rustc_errors/src/tests.rs
index 376fd24..34ebac0 100644
--- a/compiler/rustc_errors/src/tests.rs
+++ b/compiler/rustc_errors/src/tests.rs
@@ -1,3 +1,5 @@
+use std::sync::{Arc, LazyLock};
+
 use rustc_data_structures::sync::IntoDynSyncSend;
 use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError};
 use rustc_error_messages::{DiagMessage, langid};
@@ -5,23 +7,9 @@
 use crate::FluentBundle;
 use crate::error::{TranslateError, TranslateErrorKind};
 use crate::fluent_bundle::*;
-use crate::translation::Translate;
+use crate::translation::Translator;
 
-struct Dummy {
-    bundle: FluentBundle,
-}
-
-impl Translate for Dummy {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        None
-    }
-
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        &self.bundle
-    }
-}
-
-fn make_dummy(ftl: &'static str) -> Dummy {
+fn make_translator(ftl: &'static str) -> Translator {
     let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string.");
 
     let langid_en = langid!("en-US");
@@ -33,12 +21,15 @@ fn make_dummy(ftl: &'static str) -> Dummy {
 
     bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle.");
 
-    Dummy { bundle }
+    Translator {
+        fluent_bundle: None,
+        fallback_fluent_bundle: Arc::new(LazyLock::new(Box::new(|| bundle))),
+    }
 }
 
 #[test]
 fn wellformed_fluent() {
-    let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
+    let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
     .label = value moved into `{$name}` here
     .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait
     .value_borrowed_label = value borrowed here after move
@@ -54,7 +45,7 @@ fn wellformed_fluent() {
         );
 
         assert_eq!(
-            dummy.translate_message(&message, &args).unwrap(),
+            translator.translate_message(&message, &args).unwrap(),
             "borrow this binding in the pattern to avoid moving the value"
         );
     }
@@ -66,7 +57,7 @@ fn wellformed_fluent() {
         );
 
         assert_eq!(
-            dummy.translate_message(&message, &args).unwrap(),
+            translator.translate_message(&message, &args).unwrap(),
             "value borrowed here after move"
         );
     }
@@ -78,7 +69,7 @@ fn wellformed_fluent() {
         );
 
         assert_eq!(
-            dummy.translate_message(&message, &args).unwrap(),
+            translator.translate_message(&message, &args).unwrap(),
             "move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait"
         );
 
@@ -89,7 +80,7 @@ fn wellformed_fluent() {
             );
 
             assert_eq!(
-                dummy.translate_message(&message, &args).unwrap(),
+                translator.translate_message(&message, &args).unwrap(),
                 "value moved into `\u{2068}Foo\u{2069}` here"
             );
         }
@@ -98,7 +89,7 @@ fn wellformed_fluent() {
 
 #[test]
 fn misformed_fluent() {
-    let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
+    let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
     .label = value moved into `{name}` here
     .occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait
     .suggestion = borrow this binding in the pattern to avoid moving the value");
@@ -112,7 +103,7 @@ fn misformed_fluent() {
             Some("value_borrowed_label".into()),
         );
 
-        let err = dummy.translate_message(&message, &args).unwrap_err();
+        let err = translator.translate_message(&message, &args).unwrap_err();
         assert!(
             matches!(
                 &err,
@@ -141,7 +132,7 @@ fn misformed_fluent() {
             Some("label".into()),
         );
 
-        let err = dummy.translate_message(&message, &args).unwrap_err();
+        let err = translator.translate_message(&message, &args).unwrap_err();
         if let TranslateError::Two {
             primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
             fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
@@ -168,7 +159,7 @@ fn misformed_fluent() {
             Some("occurs_because_label".into()),
         );
 
-        let err = dummy.translate_message(&message, &args).unwrap_err();
+        let err = translator.translate_message(&message, &args).unwrap_err();
         if let TranslateError::Two {
             primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
             fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs
index 156f5e5..c0bcec0 100644
--- a/compiler/rustc_errors/src/translation.rs
+++ b/compiler/rustc_errors/src/translation.rs
@@ -1,8 +1,9 @@
 use std::borrow::Cow;
 use std::env;
 use std::error::Report;
+use std::sync::Arc;
 
-pub use rustc_error_messages::FluentArgs;
+pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle};
 use tracing::{debug, trace};
 
 use crate::error::{TranslateError, TranslateErrorKind};
@@ -28,19 +29,33 @@ pub fn to_fluent_args<'iter>(iter: impl Iterator<Item = DiagArg<'iter>>) -> Flue
     args
 }
 
-pub trait Translate {
-    /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
-    /// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
-    /// should be used.
-    fn fluent_bundle(&self) -> Option<&FluentBundle>;
-
+#[derive(Clone)]
+pub struct Translator {
+    /// Localized diagnostics for the locale requested by the user. If no language was requested by
+    /// the user then this will be `None` and `fallback_fluent_bundle` should be used.
+    pub fluent_bundle: Option<Arc<FluentBundle>>,
     /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
     /// Used when the user has not requested a specific language or when a localized diagnostic is
     /// unavailable for the requested locale.
-    fn fallback_fluent_bundle(&self) -> &FluentBundle;
+    pub fallback_fluent_bundle: LazyFallbackBundle,
+}
+
+impl Translator {
+    pub fn with_fallback_bundle(
+        resources: Vec<&'static str>,
+        with_directionality_markers: bool,
+    ) -> Translator {
+        Translator {
+            fluent_bundle: None,
+            fallback_fluent_bundle: crate::fallback_fluent_bundle(
+                resources,
+                with_directionality_markers,
+            ),
+        }
+    }
 
     /// Convert `DiagMessage`s to a string, performing translation if necessary.
-    fn translate_messages(
+    pub fn translate_messages(
         &self,
         messages: &[(DiagMessage, Style)],
         args: &FluentArgs<'_>,
@@ -54,7 +69,7 @@ fn translate_messages(
     }
 
     /// Convert a `DiagMessage` to a string, performing translation if necessary.
-    fn translate_message<'a>(
+    pub fn translate_message<'a>(
         &'a self,
         message: &'a DiagMessage,
         args: &'a FluentArgs<'_>,
@@ -91,7 +106,7 @@ fn translate_message<'a>(
             };
 
         try {
-            match self.fluent_bundle().map(|b| translate_with_bundle(b)) {
+            match self.fluent_bundle.as_ref().map(|b| translate_with_bundle(b)) {
                 // The primary bundle was present and translation succeeded
                 Some(Ok(t)) => t,
 
@@ -102,7 +117,7 @@ fn translate_message<'a>(
                     primary @ TranslateError::One {
                         kind: TranslateErrorKind::MessageMissing, ..
                     },
-                )) => translate_with_bundle(self.fallback_fluent_bundle())
+                )) => translate_with_bundle(&self.fallback_fluent_bundle)
                     .map_err(|fallback| primary.and(fallback))?,
 
                 // Always yeet out for errors on debug (unless
@@ -118,11 +133,11 @@ fn translate_message<'a>(
 
                 // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
                 // just hide it and try with the fallback bundle.
-                Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle())
+                Some(Err(primary)) => translate_with_bundle(&self.fallback_fluent_bundle)
                     .map_err(|fallback| primary.and(fallback))?,
 
                 // The primary bundle is missing, proceed to the fallback bundle
-                None => translate_with_bundle(self.fallback_fluent_bundle())
+                None => translate_with_bundle(&self.fallback_fluent_bundle)
                     .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?,
             }
         }
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 2d3fd77..0520be5 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -9,7 +9,7 @@
 use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize};
 use rustc_parse::lexer::nfc_normalize;
 use rustc_parse::parser::ParseNtResult;
-use rustc_session::parse::{ParseSess, SymbolGallery};
+use rustc_session::parse::ParseSess;
 use rustc_span::hygiene::{LocalExpnId, Transparency};
 use rustc_span::{
     Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, sym, with_metavar_spans,
@@ -25,20 +25,77 @@
 use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR};
 use crate::mbe::{self, KleeneOp, MetaVarExpr};
 
-// A Marker adds the given mark to the syntax context.
-struct Marker(LocalExpnId, Transparency, FxHashMap<SyntaxContext, SyntaxContext>);
+/// Context needed to perform transcription of metavariable expressions.
+struct TranscrCtx<'psess, 'itp> {
+    psess: &'psess ParseSess,
+
+    /// Map from metavars to matched tokens
+    interp: &'itp FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
+
+    /// Allow marking spans.
+    marker: Marker,
+
+    /// The stack of things yet to be completely expanded.
+    ///
+    /// We descend into the RHS (`src`), expanding things as we go. This stack contains the things
+    /// we have yet to expand/are still expanding. We start the stack off with the whole RHS. The
+    /// choice of spacing values doesn't matter.
+    stack: SmallVec<[Frame<'itp>; 1]>,
+
+    /// A stack of where we are in the repeat expansion.
+    ///
+    /// As we descend in the RHS, we will need to be able to match nested sequences of matchers.
+    /// `repeats` keeps track of where we are in matching at each level, with the last element
+    /// being the most deeply nested sequence. This is used as a stack.
+    repeats: Vec<(usize, usize)>,
+
+    /// The resulting token stream from the `TokenTree` we just finished processing.
+    ///
+    /// At the end, this will contain the full result of transcription, but at arbitrary points
+    /// during `transcribe`, `result` will contain subsets of the final result.
+    ///
+    /// Specifically, as we descend into each TokenTree, we will push the existing results onto the
+    /// `result_stack` and clear `results`. We will then produce the results of transcribing the
+    /// TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
+    /// `result_stack` and append `results` too it to produce the new `results` up to that point.
+    ///
+    /// Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
+    /// again, and we are done transcribing.
+    result: Vec<TokenTree>,
+
+    /// The in-progress `result` lives at the top of this stack. Each entered `TokenTree` adds a
+    /// new entry.
+    result_stack: Vec<Vec<TokenTree>>,
+}
+
+impl<'psess> TranscrCtx<'psess, '_> {
+    /// Span marked with the correct expansion and transparency.
+    fn visited_dspan(&mut self, dspan: DelimSpan) -> Span {
+        let mut span = dspan.entire();
+        self.marker.mark_span(&mut span);
+        span
+    }
+}
+
+/// A Marker adds the given mark to the syntax context.
+struct Marker {
+    expand_id: LocalExpnId,
+    transparency: Transparency,
+    cache: FxHashMap<SyntaxContext, SyntaxContext>,
+}
 
 impl Marker {
+    /// Mark a span with the stored expansion ID and transparency.
     fn mark_span(&mut self, span: &mut Span) {
         // `apply_mark` is a relatively expensive operation, both due to taking hygiene lock, and
         // by itself. All tokens in a macro body typically have the same syntactic context, unless
         // it's some advanced case with macro-generated macros. So if we cache the marked version
         // of that context once, we'll typically have a 100% cache hit rate after that.
-        let Marker(expn_id, transparency, ref mut cache) = *self;
         *span = span.map_ctxt(|ctxt| {
-            *cache
+            *self
+                .cache
                 .entry(ctxt)
-                .or_insert_with(|| ctxt.apply_mark(expn_id.to_expn_id(), transparency))
+                .or_insert_with(|| ctxt.apply_mark(self.expand_id.to_expn_id(), self.transparency))
         });
     }
 }
@@ -116,52 +173,36 @@ pub(super) fn transcribe<'a>(
         return Ok(TokenStream::default());
     }
 
-    // We descend into the RHS (`src`), expanding things as we go. This stack contains the things
-    // we have yet to expand/are still expanding. We start the stack off with the whole RHS. The
-    // choice of spacing values doesn't matter.
-    let mut stack: SmallVec<[Frame<'_>; 1]> = smallvec![Frame::new_delimited(
-        src,
-        src_span,
-        DelimSpacing::new(Spacing::Alone, Spacing::Alone)
-    )];
+    let mut tscx = TranscrCtx {
+        psess,
+        interp,
+        marker: Marker { expand_id, transparency, cache: Default::default() },
+        repeats: Vec::new(),
+        stack: smallvec![Frame::new_delimited(
+            src,
+            src_span,
+            DelimSpacing::new(Spacing::Alone, Spacing::Alone)
+        )],
+        result: Vec::new(),
+        result_stack: Vec::new(),
+    };
 
-    // As we descend in the RHS, we will need to be able to match nested sequences of matchers.
-    // `repeats` keeps track of where we are in matching at each level, with the last element being
-    // the most deeply nested sequence. This is used as a stack.
-    let mut repeats: Vec<(usize, usize)> = Vec::new();
-
-    // `result` contains resulting token stream from the TokenTree we just finished processing. At
-    // the end, this will contain the full result of transcription, but at arbitrary points during
-    // `transcribe`, `result` will contain subsets of the final result.
-    //
-    // Specifically, as we descend into each TokenTree, we will push the existing results onto the
-    // `result_stack` and clear `results`. We will then produce the results of transcribing the
-    // TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
-    // `result_stack` and append `results` too it to produce the new `results` up to that point.
-    //
-    // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
-    // again, and we are done transcribing.
-    let mut result: Vec<TokenTree> = Vec::new();
-    let mut result_stack = Vec::new();
-    let mut marker = Marker(expand_id, transparency, Default::default());
-
-    let dcx = psess.dcx();
     loop {
         // Look at the last frame on the stack.
         // If it still has a TokenTree we have not looked at yet, use that tree.
-        let Some(tree) = stack.last_mut().unwrap().next() else {
+        let Some(tree) = tscx.stack.last_mut().unwrap().next() else {
             // This else-case never produces a value for `tree` (it `continue`s or `return`s).
 
             // Otherwise, if we have just reached the end of a sequence and we can keep repeating,
             // go back to the beginning of the sequence.
-            let frame = stack.last_mut().unwrap();
+            let frame = tscx.stack.last_mut().unwrap();
             if let FrameKind::Sequence { sep, .. } = &frame.kind {
-                let (repeat_idx, repeat_len) = repeats.last_mut().unwrap();
+                let (repeat_idx, repeat_len) = tscx.repeats.last_mut().unwrap();
                 *repeat_idx += 1;
                 if repeat_idx < repeat_len {
                     frame.idx = 0;
                     if let Some(sep) = sep {
-                        result.push(TokenTree::Token(*sep, Spacing::Alone));
+                        tscx.result.push(TokenTree::Token(*sep, Spacing::Alone));
                     }
                     continue;
                 }
@@ -170,10 +211,10 @@ pub(super) fn transcribe<'a>(
             // We are done with the top of the stack. Pop it. Depending on what it was, we do
             // different things. Note that the outermost item must be the delimited, wrapped RHS
             // that was passed in originally to `transcribe`.
-            match stack.pop().unwrap().kind {
+            match tscx.stack.pop().unwrap().kind {
                 // Done with a sequence. Pop from repeats.
                 FrameKind::Sequence { .. } => {
-                    repeats.pop();
+                    tscx.repeats.pop();
                 }
 
                 // We are done processing a Delimited. If this is the top-level delimited, we are
@@ -185,15 +226,16 @@ pub(super) fn transcribe<'a>(
                     if delim == Delimiter::Bracket {
                         spacing.close = Spacing::Alone;
                     }
-                    if result_stack.is_empty() {
+                    if tscx.result_stack.is_empty() {
                         // No results left to compute! We are back at the top-level.
-                        return Ok(TokenStream::new(result));
+                        return Ok(TokenStream::new(tscx.result));
                     }
 
                     // Step back into the parent Delimited.
-                    let tree = TokenTree::Delimited(span, spacing, delim, TokenStream::new(result));
-                    result = result_stack.pop().unwrap();
-                    result.push(tree);
+                    let tree =
+                        TokenTree::Delimited(span, spacing, delim, TokenStream::new(tscx.result));
+                    tscx.result = tscx.result_stack.pop().unwrap();
+                    tscx.result.push(tree);
                 }
             }
             continue;
@@ -202,223 +244,19 @@ pub(super) fn transcribe<'a>(
         // At this point, we know we are in the middle of a TokenTree (the last one on `stack`).
         // `tree` contains the next `TokenTree` to be processed.
         match tree {
-            // We are descending into a sequence. We first make sure that the matchers in the RHS
-            // and the matches in `interp` have the same shape. Otherwise, either the caller or the
-            // macro writer has made a mistake.
+            // Replace the sequence with its expansion.
             seq @ mbe::TokenTree::Sequence(_, seq_rep) => {
-                match lockstep_iter_size(seq, interp, &repeats) {
-                    LockstepIterSize::Unconstrained => {
-                        return Err(dcx.create_err(NoSyntaxVarsExprRepeat { span: seq.span() }));
-                    }
-
-                    LockstepIterSize::Contradiction(msg) => {
-                        // FIXME: this really ought to be caught at macro definition time... It
-                        // happens when two meta-variables are used in the same repetition in a
-                        // sequence, but they come from different sequence matchers and repeat
-                        // different amounts.
-                        return Err(
-                            dcx.create_err(MetaVarsDifSeqMatchers { span: seq.span(), msg })
-                        );
-                    }
-
-                    LockstepIterSize::Constraint(len, _) => {
-                        // We do this to avoid an extra clone above. We know that this is a
-                        // sequence already.
-                        let mbe::TokenTree::Sequence(sp, seq) = seq else { unreachable!() };
-
-                        // Is the repetition empty?
-                        if len == 0 {
-                            if seq.kleene.op == KleeneOp::OneOrMore {
-                                // FIXME: this really ought to be caught at macro definition
-                                // time... It happens when the Kleene operator in the matcher and
-                                // the body for the same meta-variable do not match.
-                                return Err(dcx.create_err(MustRepeatOnce { span: sp.entire() }));
-                            }
-                        } else {
-                            // 0 is the initial counter (we have done 0 repetitions so far). `len`
-                            // is the total number of repetitions we should generate.
-                            repeats.push((0, len));
-
-                            // The first time we encounter the sequence we push it to the stack. It
-                            // then gets reused (see the beginning of the loop) until we are done
-                            // repeating.
-                            stack.push(Frame::new_sequence(
-                                seq_rep,
-                                seq.separator.clone(),
-                                seq.kleene.op,
-                            ));
-                        }
-                    }
-                }
+                transcribe_sequence(&mut tscx, seq, seq_rep)?;
             }
 
             // Replace the meta-var with the matched token tree from the invocation.
-            &mbe::TokenTree::MetaVar(mut sp, mut original_ident) => {
-                // Find the matched nonterminal from the macro invocation, and use it to replace
-                // the meta-var.
-                //
-                // We use `Spacing::Alone` everywhere here, because that's the conservative choice
-                // and spacing of declarative macros is tricky. E.g. in this macro:
-                // ```
-                // macro_rules! idents {
-                //     ($($a:ident,)*) => { stringify!($($a)*) }
-                // }
-                // ```
-                // `$a` has no whitespace after it and will be marked `JointHidden`. If you then
-                // call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So
-                // if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up
-                // producing "xyz", which is bad because it effectively merges tokens.
-                // `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid
-                // some of the unnecessary whitespace.
-                let ident = MacroRulesNormalizedIdent::new(original_ident);
-                if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
-                    // We wrap the tokens in invisible delimiters, unless they are already wrapped
-                    // in invisible delimiters with the same `MetaVarKind`. Because some proc
-                    // macros can't handle multiple layers of invisible delimiters of the same
-                    // `MetaVarKind`. This loses some span info, though it hopefully won't matter.
-                    let mut mk_delimited = |mk_span, mv_kind, mut stream: TokenStream| {
-                        if stream.len() == 1 {
-                            let tree = stream.iter().next().unwrap();
-                            if let TokenTree::Delimited(_, _, delim, inner) = tree
-                                && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mvk)) = delim
-                                && mv_kind == *mvk
-                            {
-                                stream = inner.clone();
-                            }
-                        }
-
-                        // Emit as a token stream within `Delimiter::Invisible` to maintain
-                        // parsing priorities.
-                        marker.mark_span(&mut sp);
-                        with_metavar_spans(|mspans| mspans.insert(mk_span, sp));
-                        // Both the open delim and close delim get the same span, which covers the
-                        // `$foo` in the decl macro RHS.
-                        TokenTree::Delimited(
-                            DelimSpan::from_single(sp),
-                            DelimSpacing::new(Spacing::Alone, Spacing::Alone),
-                            Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)),
-                            stream,
-                        )
-                    };
-                    let tt = match cur_matched {
-                        MatchedSingle(ParseNtResult::Tt(tt)) => {
-                            // `tt`s are emitted into the output stream directly as "raw tokens",
-                            // without wrapping them into groups. Other variables are emitted into
-                            // the output stream as groups with `Delimiter::Invisible` to maintain
-                            // parsing priorities.
-                            maybe_use_metavar_location(psess, &stack, sp, tt, &mut marker)
-                        }
-                        MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => {
-                            marker.mark_span(&mut sp);
-                            with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
-                            let kind = token::NtIdent(*ident, *is_raw);
-                            TokenTree::token_alone(kind, sp)
-                        }
-                        MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
-                            marker.mark_span(&mut sp);
-                            with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
-                            let kind = token::NtLifetime(*ident, *is_raw);
-                            TokenTree::token_alone(kind, sp)
-                        }
-                        MatchedSingle(ParseNtResult::Item(item)) => {
-                            mk_delimited(item.span, MetaVarKind::Item, TokenStream::from_ast(item))
-                        }
-                        MatchedSingle(ParseNtResult::Block(block)) => mk_delimited(
-                            block.span,
-                            MetaVarKind::Block,
-                            TokenStream::from_ast(block),
-                        ),
-                        MatchedSingle(ParseNtResult::Stmt(stmt)) => {
-                            let stream = if let StmtKind::Empty = stmt.kind {
-                                // FIXME: Properly collect tokens for empty statements.
-                                TokenStream::token_alone(token::Semi, stmt.span)
-                            } else {
-                                TokenStream::from_ast(stmt)
-                            };
-                            mk_delimited(stmt.span, MetaVarKind::Stmt, stream)
-                        }
-                        MatchedSingle(ParseNtResult::Pat(pat, pat_kind)) => mk_delimited(
-                            pat.span,
-                            MetaVarKind::Pat(*pat_kind),
-                            TokenStream::from_ast(pat),
-                        ),
-                        MatchedSingle(ParseNtResult::Expr(expr, kind)) => {
-                            let (can_begin_literal_maybe_minus, can_begin_string_literal) =
-                                match &expr.kind {
-                                    ExprKind::Lit(_) => (true, true),
-                                    ExprKind::Unary(UnOp::Neg, e)
-                                        if matches!(&e.kind, ExprKind::Lit(_)) =>
-                                    {
-                                        (true, false)
-                                    }
-                                    _ => (false, false),
-                                };
-                            mk_delimited(
-                                expr.span,
-                                MetaVarKind::Expr {
-                                    kind: *kind,
-                                    can_begin_literal_maybe_minus,
-                                    can_begin_string_literal,
-                                },
-                                TokenStream::from_ast(expr),
-                            )
-                        }
-                        MatchedSingle(ParseNtResult::Literal(lit)) => {
-                            mk_delimited(lit.span, MetaVarKind::Literal, TokenStream::from_ast(lit))
-                        }
-                        MatchedSingle(ParseNtResult::Ty(ty)) => {
-                            let is_path = matches!(&ty.kind, TyKind::Path(None, _path));
-                            mk_delimited(
-                                ty.span,
-                                MetaVarKind::Ty { is_path },
-                                TokenStream::from_ast(ty),
-                            )
-                        }
-                        MatchedSingle(ParseNtResult::Meta(attr_item)) => {
-                            let has_meta_form = attr_item.meta_kind().is_some();
-                            mk_delimited(
-                                attr_item.span(),
-                                MetaVarKind::Meta { has_meta_form },
-                                TokenStream::from_ast(attr_item),
-                            )
-                        }
-                        MatchedSingle(ParseNtResult::Path(path)) => {
-                            mk_delimited(path.span, MetaVarKind::Path, TokenStream::from_ast(path))
-                        }
-                        MatchedSingle(ParseNtResult::Vis(vis)) => {
-                            mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis))
-                        }
-                        MatchedSeq(..) => {
-                            // We were unable to descend far enough. This is an error.
-                            return Err(dcx.create_err(VarStillRepeating { span: sp, ident }));
-                        }
-                    };
-                    result.push(tt)
-                } else {
-                    // If we aren't able to match the meta-var, we push it back into the result but
-                    // with modified syntax context. (I believe this supports nested macros).
-                    marker.mark_span(&mut sp);
-                    marker.mark_span(&mut original_ident.span);
-                    result.push(TokenTree::token_joint_hidden(token::Dollar, sp));
-                    result.push(TokenTree::Token(
-                        Token::from_ast_ident(original_ident),
-                        Spacing::Alone,
-                    ));
-                }
+            &mbe::TokenTree::MetaVar(sp, original_ident) => {
+                transcribe_metavar(&mut tscx, sp, original_ident)?;
             }
 
             // Replace meta-variable expressions with the result of their expansion.
-            mbe::TokenTree::MetaVarExpr(sp, expr) => {
-                transcribe_metavar_expr(
-                    dcx,
-                    expr,
-                    interp,
-                    &mut marker,
-                    &repeats,
-                    &mut result,
-                    sp,
-                    &psess.symbol_gallery,
-                )?;
+            mbe::TokenTree::MetaVarExpr(dspan, expr) => {
+                transcribe_metavar_expr(&mut tscx, *dspan, expr)?;
             }
 
             // If we are entering a new delimiter, we push its contents to the `stack` to be
@@ -427,21 +265,21 @@ pub(super) fn transcribe<'a>(
             // jump back out of the Delimited, pop the result_stack and add the new results back to
             // the previous results (from outside the Delimited).
             &mbe::TokenTree::Delimited(mut span, ref spacing, ref delimited) => {
-                marker.mark_span(&mut span.open);
-                marker.mark_span(&mut span.close);
-                stack.push(Frame::new_delimited(delimited, span, *spacing));
-                result_stack.push(mem::take(&mut result));
+                tscx.marker.mark_span(&mut span.open);
+                tscx.marker.mark_span(&mut span.close);
+                tscx.stack.push(Frame::new_delimited(delimited, span, *spacing));
+                tscx.result_stack.push(mem::take(&mut tscx.result));
             }
 
             // Nothing much to do here. Just push the token to the result, being careful to
             // preserve syntax context.
             &mbe::TokenTree::Token(mut token) => {
-                marker.mark_span(&mut token.span);
+                tscx.marker.mark_span(&mut token.span);
                 if let token::NtIdent(ident, _) | token::NtLifetime(ident, _) = &mut token.kind {
-                    marker.mark_span(&mut ident.span);
+                    tscx.marker.mark_span(&mut ident.span);
                 }
                 let tt = TokenTree::Token(token, Spacing::Alone);
-                result.push(tt);
+                tscx.result.push(tt);
             }
 
             // There should be no meta-var declarations in the invocation of a macro.
@@ -450,6 +288,305 @@ pub(super) fn transcribe<'a>(
     }
 }
 
+/// Turn `$(...)*` sequences into tokens.
+fn transcribe_sequence<'tx, 'itp>(
+    tscx: &mut TranscrCtx<'tx, 'itp>,
+    seq: &mbe::TokenTree,
+    seq_rep: &'itp mbe::SequenceRepetition,
+) -> PResult<'tx, ()> {
+    let dcx = tscx.psess.dcx();
+
+    // We are descending into a sequence. We first make sure that the matchers in the RHS
+    // and the matches in `interp` have the same shape. Otherwise, either the caller or the
+    // macro writer has made a mistake.
+    match lockstep_iter_size(seq, tscx.interp, &tscx.repeats) {
+        LockstepIterSize::Unconstrained => {
+            return Err(dcx.create_err(NoSyntaxVarsExprRepeat { span: seq.span() }));
+        }
+
+        LockstepIterSize::Contradiction(msg) => {
+            // FIXME: this really ought to be caught at macro definition time... It
+            // happens when two meta-variables are used in the same repetition in a
+            // sequence, but they come from different sequence matchers and repeat
+            // different amounts.
+            return Err(dcx.create_err(MetaVarsDifSeqMatchers { span: seq.span(), msg }));
+        }
+
+        LockstepIterSize::Constraint(len, _) => {
+            // We do this to avoid an extra clone above. We know that this is a
+            // sequence already.
+            let mbe::TokenTree::Sequence(sp, seq) = seq else { unreachable!() };
+
+            // Is the repetition empty?
+            if len == 0 {
+                if seq.kleene.op == KleeneOp::OneOrMore {
+                    // FIXME: this really ought to be caught at macro definition
+                    // time... It happens when the Kleene operator in the matcher and
+                    // the body for the same meta-variable do not match.
+                    return Err(dcx.create_err(MustRepeatOnce { span: sp.entire() }));
+                }
+            } else {
+                // 0 is the initial counter (we have done 0 repetitions so far). `len`
+                // is the total number of repetitions we should generate.
+                tscx.repeats.push((0, len));
+
+                // The first time we encounter the sequence we push it to the stack. It
+                // then gets reused (see the beginning of the loop) until we are done
+                // repeating.
+                tscx.stack.push(Frame::new_sequence(seq_rep, seq.separator.clone(), seq.kleene.op));
+            }
+        }
+    }
+
+    Ok(())
+}
+
+/// Find the matched nonterminal from the macro invocation, and use it to replace
+/// the meta-var.
+///
+/// We use `Spacing::Alone` everywhere here, because that's the conservative choice
+/// and spacing of declarative macros is tricky. E.g. in this macro:
+/// ```
+/// macro_rules! idents {
+///     ($($a:ident,)*) => { stringify!($($a)*) }
+/// }
+/// ```
+/// `$a` has no whitespace after it and will be marked `JointHidden`. If you then
+/// call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So
+/// if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up
+/// producing "xyz", which is bad because it effectively merges tokens.
+/// `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid
+/// some of the unnecessary whitespace.
+fn transcribe_metavar<'tx>(
+    tscx: &mut TranscrCtx<'tx, '_>,
+    mut sp: Span,
+    mut original_ident: Ident,
+) -> PResult<'tx, ()> {
+    let dcx = tscx.psess.dcx();
+
+    let ident = MacroRulesNormalizedIdent::new(original_ident);
+    let Some(cur_matched) = lookup_cur_matched(ident, tscx.interp, &tscx.repeats) else {
+        // If we aren't able to match the meta-var, we push it back into the result but
+        // with modified syntax context. (I believe this supports nested macros).
+        tscx.marker.mark_span(&mut sp);
+        tscx.marker.mark_span(&mut original_ident.span);
+        tscx.result.push(TokenTree::token_joint_hidden(token::Dollar, sp));
+        tscx.result.push(TokenTree::Token(Token::from_ast_ident(original_ident), Spacing::Alone));
+        return Ok(());
+    };
+
+    // We wrap the tokens in invisible delimiters, unless they are already wrapped
+    // in invisible delimiters with the same `MetaVarKind`. Because some proc
+    // macros can't handle multiple layers of invisible delimiters of the same
+    // `MetaVarKind`. This loses some span info, though it hopefully won't matter.
+    let mut mk_delimited = |mk_span, mv_kind, mut stream: TokenStream| {
+        if stream.len() == 1 {
+            let tree = stream.iter().next().unwrap();
+            if let TokenTree::Delimited(_, _, delim, inner) = tree
+                && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mvk)) = delim
+                && mv_kind == *mvk
+            {
+                stream = inner.clone();
+            }
+        }
+
+        // Emit as a token stream within `Delimiter::Invisible` to maintain
+        // parsing priorities.
+        tscx.marker.mark_span(&mut sp);
+        with_metavar_spans(|mspans| mspans.insert(mk_span, sp));
+        // Both the open delim and close delim get the same span, which covers the
+        // `$foo` in the decl macro RHS.
+        TokenTree::Delimited(
+            DelimSpan::from_single(sp),
+            DelimSpacing::new(Spacing::Alone, Spacing::Alone),
+            Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)),
+            stream,
+        )
+    };
+
+    let tt = match cur_matched {
+        MatchedSingle(ParseNtResult::Tt(tt)) => {
+            // `tt`s are emitted into the output stream directly as "raw tokens",
+            // without wrapping them into groups. Other variables are emitted into
+            // the output stream as groups with `Delimiter::Invisible` to maintain
+            // parsing priorities.
+            maybe_use_metavar_location(tscx.psess, &tscx.stack, sp, tt, &mut tscx.marker)
+        }
+        MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => {
+            tscx.marker.mark_span(&mut sp);
+            with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
+            let kind = token::NtIdent(*ident, *is_raw);
+            TokenTree::token_alone(kind, sp)
+        }
+        MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
+            tscx.marker.mark_span(&mut sp);
+            with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
+            let kind = token::NtLifetime(*ident, *is_raw);
+            TokenTree::token_alone(kind, sp)
+        }
+        MatchedSingle(ParseNtResult::Item(item)) => {
+            mk_delimited(item.span, MetaVarKind::Item, TokenStream::from_ast(item))
+        }
+        MatchedSingle(ParseNtResult::Block(block)) => {
+            mk_delimited(block.span, MetaVarKind::Block, TokenStream::from_ast(block))
+        }
+        MatchedSingle(ParseNtResult::Stmt(stmt)) => {
+            let stream = if let StmtKind::Empty = stmt.kind {
+                // FIXME: Properly collect tokens for empty statements.
+                TokenStream::token_alone(token::Semi, stmt.span)
+            } else {
+                TokenStream::from_ast(stmt)
+            };
+            mk_delimited(stmt.span, MetaVarKind::Stmt, stream)
+        }
+        MatchedSingle(ParseNtResult::Pat(pat, pat_kind)) => {
+            mk_delimited(pat.span, MetaVarKind::Pat(*pat_kind), TokenStream::from_ast(pat))
+        }
+        MatchedSingle(ParseNtResult::Expr(expr, kind)) => {
+            let (can_begin_literal_maybe_minus, can_begin_string_literal) = match &expr.kind {
+                ExprKind::Lit(_) => (true, true),
+                ExprKind::Unary(UnOp::Neg, e) if matches!(&e.kind, ExprKind::Lit(_)) => {
+                    (true, false)
+                }
+                _ => (false, false),
+            };
+            mk_delimited(
+                expr.span,
+                MetaVarKind::Expr {
+                    kind: *kind,
+                    can_begin_literal_maybe_minus,
+                    can_begin_string_literal,
+                },
+                TokenStream::from_ast(expr),
+            )
+        }
+        MatchedSingle(ParseNtResult::Literal(lit)) => {
+            mk_delimited(lit.span, MetaVarKind::Literal, TokenStream::from_ast(lit))
+        }
+        MatchedSingle(ParseNtResult::Ty(ty)) => {
+            let is_path = matches!(&ty.kind, TyKind::Path(None, _path));
+            mk_delimited(ty.span, MetaVarKind::Ty { is_path }, TokenStream::from_ast(ty))
+        }
+        MatchedSingle(ParseNtResult::Meta(attr_item)) => {
+            let has_meta_form = attr_item.meta_kind().is_some();
+            mk_delimited(
+                attr_item.span(),
+                MetaVarKind::Meta { has_meta_form },
+                TokenStream::from_ast(attr_item),
+            )
+        }
+        MatchedSingle(ParseNtResult::Path(path)) => {
+            mk_delimited(path.span, MetaVarKind::Path, TokenStream::from_ast(path))
+        }
+        MatchedSingle(ParseNtResult::Vis(vis)) => {
+            mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis))
+        }
+        MatchedSeq(..) => {
+            // We were unable to descend far enough. This is an error.
+            return Err(dcx.create_err(VarStillRepeating { span: sp, ident }));
+        }
+    };
+
+    tscx.result.push(tt);
+    Ok(())
+}
+
+/// Turn `${expr(...)}` metavariable expressionss into tokens.
+fn transcribe_metavar_expr<'tx>(
+    tscx: &mut TranscrCtx<'tx, '_>,
+    dspan: DelimSpan,
+    expr: &MetaVarExpr,
+) -> PResult<'tx, ()> {
+    let dcx = tscx.psess.dcx();
+    let tt = match *expr {
+        MetaVarExpr::Concat(ref elements) => metavar_expr_concat(tscx, dspan, elements)?,
+        MetaVarExpr::Count(original_ident, depth) => {
+            let matched = matched_from_ident(dcx, original_ident, tscx.interp)?;
+            let count = count_repetitions(dcx, depth, matched, &tscx.repeats, &dspan)?;
+            TokenTree::token_alone(
+                TokenKind::lit(token::Integer, sym::integer(count), None),
+                tscx.visited_dspan(dspan),
+            )
+        }
+        MetaVarExpr::Ignore(original_ident) => {
+            // Used to ensure that `original_ident` is present in the LHS
+            let _ = matched_from_ident(dcx, original_ident, tscx.interp)?;
+            return Ok(());
+        }
+        MetaVarExpr::Index(depth) => match tscx.repeats.iter().nth_back(depth) {
+            Some((index, _)) => TokenTree::token_alone(
+                TokenKind::lit(token::Integer, sym::integer(*index), None),
+                tscx.visited_dspan(dspan),
+            ),
+            None => {
+                return Err(out_of_bounds_err(dcx, tscx.repeats.len(), dspan.entire(), "index"));
+            }
+        },
+        MetaVarExpr::Len(depth) => match tscx.repeats.iter().nth_back(depth) {
+            Some((_, length)) => TokenTree::token_alone(
+                TokenKind::lit(token::Integer, sym::integer(*length), None),
+                tscx.visited_dspan(dspan),
+            ),
+            None => {
+                return Err(out_of_bounds_err(dcx, tscx.repeats.len(), dspan.entire(), "len"));
+            }
+        },
+    };
+    tscx.result.push(tt);
+    Ok(())
+}
+
+/// Handle the `${concat(...)}` metavariable expression.
+fn metavar_expr_concat<'tx>(
+    tscx: &mut TranscrCtx<'tx, '_>,
+    dspan: DelimSpan,
+    elements: &[MetaVarExprConcatElem],
+) -> PResult<'tx, TokenTree> {
+    let dcx = tscx.psess.dcx();
+    let mut concatenated = String::new();
+    for element in elements.into_iter() {
+        let symbol = match element {
+            MetaVarExprConcatElem::Ident(elem) => elem.name,
+            MetaVarExprConcatElem::Literal(elem) => *elem,
+            MetaVarExprConcatElem::Var(ident) => {
+                match matched_from_ident(dcx, *ident, tscx.interp)? {
+                    NamedMatch::MatchedSeq(named_matches) => {
+                        let Some((curr_idx, _)) = tscx.repeats.last() else {
+                            return Err(dcx.struct_span_err(dspan.entire(), "invalid syntax"));
+                        };
+                        match &named_matches[*curr_idx] {
+                            // FIXME(c410-f3r) Nested repetitions are unimplemented
+                            MatchedSeq(_) => unimplemented!(),
+                            MatchedSingle(pnr) => extract_symbol_from_pnr(dcx, pnr, ident.span)?,
+                        }
+                    }
+                    NamedMatch::MatchedSingle(pnr) => {
+                        extract_symbol_from_pnr(dcx, pnr, ident.span)?
+                    }
+                }
+            }
+        };
+        concatenated.push_str(symbol.as_str());
+    }
+    let symbol = nfc_normalize(&concatenated);
+    let concatenated_span = tscx.visited_dspan(dspan);
+    if !rustc_lexer::is_ident(symbol.as_str()) {
+        return Err(dcx.struct_span_err(
+            concatenated_span,
+            "`${concat(..)}` is not generating a valid identifier",
+        ));
+    }
+    tscx.psess.symbol_gallery.insert(symbol, concatenated_span);
+
+    // The current implementation marks the span as coming from the macro regardless of
+    // contexts of the concatenated identifiers but this behavior may change in the
+    // future.
+    Ok(TokenTree::Token(
+        Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
+        Spacing::Alone,
+    ))
+}
+
 /// Store the metavariable span for this original span into a side table.
 /// FIXME: Try to put the metavariable span into `SpanData` instead of a side table (#118517).
 /// An optimal encoding for inlined spans will need to be selected to minimize regressions.
@@ -671,13 +808,13 @@ fn lockstep_iter_size(
 /// * `[ $( ${count(foo, 0)} ),* ]` will be the same as `[ $( ${count(foo)} ),* ]`
 /// * `[ $( ${count(foo, 1)} ),* ]` will return an error because `${count(foo, 1)}` is
 ///   declared inside a single repetition and the index `1` implies two nested repetitions.
-fn count_repetitions<'a>(
-    dcx: DiagCtxtHandle<'a>,
+fn count_repetitions<'dx>(
+    dcx: DiagCtxtHandle<'dx>,
     depth_user: usize,
     mut matched: &NamedMatch,
     repeats: &[(usize, usize)],
     sp: &DelimSpan,
-) -> PResult<'a, usize> {
+) -> PResult<'dx, usize> {
     // Recursively count the number of matches in `matched` at given depth
     // (or at the top-level of `matched` if no depth is given).
     fn count<'a>(depth_curr: usize, depth_max: usize, matched: &NamedMatch) -> PResult<'a, usize> {
@@ -762,102 +899,6 @@ fn out_of_bounds_err<'a>(dcx: DiagCtxtHandle<'a>, max: usize, span: Span, ty: &s
     dcx.struct_span_err(span, msg)
 }
 
-fn transcribe_metavar_expr<'a>(
-    dcx: DiagCtxtHandle<'a>,
-    expr: &MetaVarExpr,
-    interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
-    marker: &mut Marker,
-    repeats: &[(usize, usize)],
-    result: &mut Vec<TokenTree>,
-    sp: &DelimSpan,
-    symbol_gallery: &SymbolGallery,
-) -> PResult<'a, ()> {
-    let mut visited_span = || {
-        let mut span = sp.entire();
-        marker.mark_span(&mut span);
-        span
-    };
-    match *expr {
-        MetaVarExpr::Concat(ref elements) => {
-            let mut concatenated = String::new();
-            for element in elements.into_iter() {
-                let symbol = match element {
-                    MetaVarExprConcatElem::Ident(elem) => elem.name,
-                    MetaVarExprConcatElem::Literal(elem) => *elem,
-                    MetaVarExprConcatElem::Var(ident) => {
-                        match matched_from_ident(dcx, *ident, interp)? {
-                            NamedMatch::MatchedSeq(named_matches) => {
-                                let Some((curr_idx, _)) = repeats.last() else {
-                                    return Err(dcx.struct_span_err(sp.entire(), "invalid syntax"));
-                                };
-                                match &named_matches[*curr_idx] {
-                                    // FIXME(c410-f3r) Nested repetitions are unimplemented
-                                    MatchedSeq(_) => unimplemented!(),
-                                    MatchedSingle(pnr) => {
-                                        extract_symbol_from_pnr(dcx, pnr, ident.span)?
-                                    }
-                                }
-                            }
-                            NamedMatch::MatchedSingle(pnr) => {
-                                extract_symbol_from_pnr(dcx, pnr, ident.span)?
-                            }
-                        }
-                    }
-                };
-                concatenated.push_str(symbol.as_str());
-            }
-            let symbol = nfc_normalize(&concatenated);
-            let concatenated_span = visited_span();
-            if !rustc_lexer::is_ident(symbol.as_str()) {
-                return Err(dcx.struct_span_err(
-                    concatenated_span,
-                    "`${concat(..)}` is not generating a valid identifier",
-                ));
-            }
-            symbol_gallery.insert(symbol, concatenated_span);
-            // The current implementation marks the span as coming from the macro regardless of
-            // contexts of the concatenated identifiers but this behavior may change in the
-            // future.
-            result.push(TokenTree::Token(
-                Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
-                Spacing::Alone,
-            ));
-        }
-        MetaVarExpr::Count(original_ident, depth) => {
-            let matched = matched_from_ident(dcx, original_ident, interp)?;
-            let count = count_repetitions(dcx, depth, matched, repeats, sp)?;
-            let tt = TokenTree::token_alone(
-                TokenKind::lit(token::Integer, sym::integer(count), None),
-                visited_span(),
-            );
-            result.push(tt);
-        }
-        MetaVarExpr::Ignore(original_ident) => {
-            // Used to ensure that `original_ident` is present in the LHS
-            let _ = matched_from_ident(dcx, original_ident, interp)?;
-        }
-        MetaVarExpr::Index(depth) => match repeats.iter().nth_back(depth) {
-            Some((index, _)) => {
-                result.push(TokenTree::token_alone(
-                    TokenKind::lit(token::Integer, sym::integer(*index), None),
-                    visited_span(),
-                ));
-            }
-            None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "index")),
-        },
-        MetaVarExpr::Len(depth) => match repeats.iter().nth_back(depth) {
-            Some((_, length)) => {
-                result.push(TokenTree::token_alone(
-                    TokenKind::lit(token::Integer, sym::integer(*length), None),
-                    visited_span(),
-                ));
-            }
-            None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "len")),
-        },
-    }
-    Ok(())
-}
-
 /// Extracts an metavariable symbol that can be an identifier, a token tree or a literal.
 fn extract_symbol_from_pnr<'a>(
     dcx: DiagCtxtHandle<'a>,
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index ca24d5a..bf2d4f6 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -311,9 +311,7 @@ fn default_body_is_unstable(
         reason: reason_str,
     });
 
-    let inject_span = item_did
-        .as_local()
-        .and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
+    let inject_span = item_did.is_local().then(|| tcx.crate_level_attribute_injection_span());
     rustc_session::parse::add_feature_diagnostics_for_issue(
         &mut err,
         &tcx.sess,
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 20d0e87..13f9502 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1064,7 +1064,7 @@ fn ty_is_local(ty: Ty<'_>) -> bool {
                     Ok(..) => Some(vec![(adt_const_params_feature_string, sym::adt_const_params)]),
                 };
                 if let Some(features) = may_suggest_feature {
-                    tcx.disabled_nightly_features(&mut diag, Some(param.hir_id), features);
+                    tcx.disabled_nightly_features(&mut diag, features);
                 }
 
                 Err(diag.emit())
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index abb8cdc..5f59b3a 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -30,8 +30,6 @@ pub(crate) struct BaseExpressionDoubleDot {
     )]
     pub default_field_values_suggestion: Option<Span>,
     #[subdiagnostic]
-    pub default_field_values_help: Option<BaseExpressionDoubleDotEnableDefaultFieldValues>,
-    #[subdiagnostic]
     pub add_expr: Option<BaseExpressionDoubleDotAddExpr>,
     #[subdiagnostic]
     pub remove_dots: Option<BaseExpressionDoubleDotRemove>,
@@ -61,10 +59,6 @@ pub(crate) struct BaseExpressionDoubleDotAddExpr {
     pub span: Span,
 }
 
-#[derive(Subdiagnostic)]
-#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)]
-pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues;
-
 #[derive(Diagnostic)]
 #[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)]
 pub(crate) struct FieldMultiplySpecifiedInInitializer {
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 55c39d9..eca9c5f 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -43,10 +43,9 @@
 use crate::coercion::{CoerceMany, DynamicCoerceMany};
 use crate::errors::{
     AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
-    BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove,
-    CantDereference, FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct,
-    HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType, NoFieldOnVariant,
-    ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
+    BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer,
+    FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType,
+    NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
     TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
 };
 use crate::{
@@ -2158,7 +2157,7 @@ fn check_expr_struct_fields(
                 }
             }
             if !self.tcx.features().default_field_values() {
-                let sugg = self.tcx.crate_level_attribute_injection_span(expr.hir_id);
+                let sugg = self.tcx.crate_level_attribute_injection_span();
                 self.dcx().emit_err(BaseExpressionDoubleDot {
                     span: span.shrink_to_hi(),
                     // We only mention enabling the feature if this is a nightly rustc *and* the
@@ -2166,18 +2165,8 @@ fn check_expr_struct_fields(
                     default_field_values_suggestion: if self.tcx.sess.is_nightly_build()
                         && missing_mandatory_fields.is_empty()
                         && !missing_optional_fields.is_empty()
-                        && sugg.is_some()
                     {
-                        sugg
-                    } else {
-                        None
-                    },
-                    default_field_values_help: if self.tcx.sess.is_nightly_build()
-                        && missing_mandatory_fields.is_empty()
-                        && !missing_optional_fields.is_empty()
-                        && sugg.is_none()
-                    {
-                        Some(BaseExpressionDoubleDotEnableDefaultFieldValues)
+                        Some(sugg)
                     } else {
                         None
                     },
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index a3fdf20..589dbb5 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1727,7 +1727,6 @@ pub(crate) fn maybe_emit_unstable_name_collision_hint(
             }
             tcx.disabled_nightly_features(
                 lint,
-                Some(scope_expr_id),
                 self.unstable_candidates.iter().map(|(candidate, feature)| {
                     (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature)
                 }),
diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs
index 67f1319..2385c68 100644
--- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs
+++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs
@@ -7,8 +7,11 @@
 //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
 
 use rustc_macros::extension;
-use rustc_middle::bug;
-use rustc_middle::ty::{self, FnMutDelegate, GenericArgKind, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{
+    self, DelayedMap, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
+    TypeVisitableExt, TypeVisitor,
+};
+use rustc_type_ir::TypeVisitable;
 
 use crate::infer::canonical::{Canonical, CanonicalVarValues};
 
@@ -58,23 +61,169 @@ pub(super) fn instantiate_value<'tcx, T>(
     T: TypeFoldable<TyCtxt<'tcx>>,
 {
     if var_values.var_values.is_empty() {
-        value
-    } else {
-        let delegate = FnMutDelegate {
-            regions: &mut |br: ty::BoundRegion| match var_values[br.var].kind() {
-                GenericArgKind::Lifetime(l) => l,
-                r => bug!("{:?} is a region but value is {:?}", br, r),
-            },
-            types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].kind() {
-                GenericArgKind::Type(ty) => ty,
-                r => bug!("{:?} is a type but value is {:?}", bound_ty, r),
-            },
-            consts: &mut |bound_ct: ty::BoundVar| match var_values[bound_ct].kind() {
-                GenericArgKind::Const(ct) => ct,
-                c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
-            },
-        };
-
-        tcx.replace_escaping_bound_vars_uncached(value, delegate)
+        return value;
     }
+
+    value.fold_with(&mut CanonicalInstantiator {
+        tcx,
+        current_index: ty::INNERMOST,
+        var_values: var_values.var_values,
+        cache: Default::default(),
+    })
+}
+
+/// Replaces the bound vars in a canonical binder with var values.
+struct CanonicalInstantiator<'tcx> {
+    tcx: TyCtxt<'tcx>,
+
+    // The values that the bound vars are are being instantiated with.
+    var_values: ty::GenericArgsRef<'tcx>,
+
+    /// As with `BoundVarReplacer`, represents the index of a binder *just outside*
+    /// the ones we have visited.
+    current_index: ty::DebruijnIndex,
+
+    // Instantiation is a pure function of `DebruijnIndex` and `Ty`.
+    cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match *t.kind() {
+            ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
+                self.var_values[bound_ty.var.as_usize()].expect_ty()
+            }
+            _ => {
+                if !t.has_vars_bound_at_or_above(self.current_index) {
+                    t
+                } else if let Some(&t) = self.cache.get(&(self.current_index, t)) {
+                    t
+                } else {
+                    let res = t.super_fold_with(self);
+                    assert!(self.cache.insert((self.current_index, t), res));
+                    res
+                }
+            }
+        }
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match r.kind() {
+            ty::ReBound(debruijn, br) if debruijn == self.current_index => {
+                self.var_values[br.var.as_usize()].expect_region()
+            }
+            _ => r,
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        match ct.kind() {
+            ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
+                self.var_values[bound_const.as_usize()].expect_const()
+            }
+            _ => ct.super_fold_with(self),
+        }
+    }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
+
+    fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
+        if !c.has_vars_bound_at_or_above(self.current_index) {
+            return c;
+        }
+
+        // Since instantiation is a function of `DebruijnIndex`, we don't want
+        // to have to cache more copies of clauses when we're inside of binders.
+        // Since we currently expect to only have clauses in the outermost
+        // debruijn index, we just fold if we're inside of a binder.
+        if self.current_index > ty::INNERMOST {
+            return c.super_fold_with(self);
+        }
+
+        // Our cache key is `(clauses, var_values)`, but we also don't care about
+        // var values that aren't named in the clauses, since they can change without
+        // affecting the output. Since `ParamEnv`s are cached first, we compute the
+        // last var value that is mentioned in the clauses, and cut off the list so
+        // that we have more hits in the cache.
+
+        // We also cache the computation of "highest var named by clauses" since that
+        // is both expensive (depending on the size of the clauses) and a pure function.
+        let index = *self
+            .tcx
+            .highest_var_in_clauses_cache
+            .lock()
+            .entry(c)
+            .or_insert_with(|| highest_var_in_clauses(c));
+        let c_args = &self.var_values[..=index];
+
+        if let Some(c) = self.tcx.clauses_cache.lock().get(&(c, c_args)) {
+            c
+        } else {
+            let folded = c.super_fold_with(self);
+            self.tcx.clauses_cache.lock().insert((c, c_args), folded);
+            folded
+        }
+    }
+}
+
+fn highest_var_in_clauses<'tcx>(c: ty::Clauses<'tcx>) -> usize {
+    struct HighestVarInClauses {
+        max_var: usize,
+        current_index: ty::DebruijnIndex,
+    }
+    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HighestVarInClauses {
+        fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+            &mut self,
+            t: &ty::Binder<'tcx, T>,
+        ) -> Self::Result {
+            self.current_index.shift_in(1);
+            let t = t.super_visit_with(self);
+            self.current_index.shift_out(1);
+            t
+        }
+        fn visit_ty(&mut self, t: Ty<'tcx>) {
+            if let ty::Bound(debruijn, bound_ty) = *t.kind()
+                && debruijn == self.current_index
+            {
+                self.max_var = self.max_var.max(bound_ty.var.as_usize());
+            } else if t.has_vars_bound_at_or_above(self.current_index) {
+                t.super_visit_with(self);
+            }
+        }
+        fn visit_region(&mut self, r: ty::Region<'tcx>) {
+            if let ty::ReBound(debruijn, bound_region) = r.kind()
+                && debruijn == self.current_index
+            {
+                self.max_var = self.max_var.max(bound_region.var.as_usize());
+            }
+        }
+        fn visit_const(&mut self, ct: ty::Const<'tcx>) {
+            if let ty::ConstKind::Bound(debruijn, bound_const) = ct.kind()
+                && debruijn == self.current_index
+            {
+                self.max_var = self.max_var.max(bound_const.as_usize());
+            } else if ct.has_vars_bound_at_or_above(self.current_index) {
+                ct.super_visit_with(self);
+            }
+        }
+    }
+    let mut visitor = HighestVarInClauses { max_var: 0, current_index: ty::INNERMOST };
+    c.visit_with(&mut visitor);
+    visitor.max_var
 }
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index e824e9d..d62bf7f 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -52,10 +52,9 @@ pub struct Compiler {
 pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
     cfgs.into_iter()
         .map(|s| {
-            let psess = ParseSess::with_silent_emitter(
+            let psess = ParseSess::with_fatal_emitter(
                 vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
                 format!("this error occurred on the command line: `--cfg={s}`"),
-                true,
             );
             let filename = FileName::cfg_spec_source_code(&s);
 
@@ -116,10 +115,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
     let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
 
     for s in specs {
-        let psess = ParseSess::with_silent_emitter(
+        let psess = ParseSess::with_fatal_emitter(
             vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
             format!("this error occurred on the command line: `--check-cfg={s}`"),
-            true,
         );
         let filename = FileName::cfg_spec_source_code(&s);
 
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 02d1ebd..70ae914 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -1011,8 +1011,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
 
     // Prefetch this to prevent multiple threads from blocking on it later.
     // This is needed since the `hir_id_validator::check_crate` call above is not guaranteed
-    // to use `hir_crate`.
-    tcx.ensure_done().hir_crate(());
+    // to use `hir_crate_items`.
+    tcx.ensure_done().hir_crate_items(());
 
     let sess = tcx.sess;
     sess.time("misc_checking_1", || {
diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs
index 4c2b82a..481e116 100644
--- a/compiler/rustc_lint/src/expect.rs
+++ b/compiler/rustc_lint/src/expect.rs
@@ -1,5 +1,4 @@
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::CRATE_OWNER_ID;
 use rustc_middle::lint::LintExpectation;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
@@ -18,7 +17,7 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp
 
     let mut expectations = Vec::new();
 
-    for owner in std::iter::once(CRATE_OWNER_ID).chain(krate.owners()) {
+    for owner in krate.owners() {
         let lints = tcx.shallow_lint_levels_on(owner);
         expectations.extend_from_slice(&lints.expectations);
     }
diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs
index e5e1ae5..03bb970 100644
--- a/compiler/rustc_middle/src/hir/map.rs
+++ b/compiler/rustc_middle/src/hir/map.rs
@@ -328,8 +328,7 @@ pub fn hir_body_const_context(self, def_id: LocalDefId) -> Option<ConstContext>
     }
 
     /// Returns an iterator of the `DefId`s for all body-owners in this
-    /// crate. If you would prefer to iterate over the bodies
-    /// themselves, you can do `self.hir_crate(()).body_ids.iter()`.
+    /// crate.
     #[inline]
     pub fn hir_body_owners(self) -> impl Iterator<Item = LocalDefId> {
         self.hir_crate_items(()).body_owners.iter().copied()
@@ -396,12 +395,11 @@ pub fn hir_walk_attributes<V>(self, visitor: &mut V) -> V::Result
     where
         V: Visitor<'tcx>,
     {
-        let krate = self.hir_crate(());
-        for info in krate.owners.iter() {
-            if let MaybeOwner::Owner(info) = info {
-                for attrs in info.attrs.map.values() {
-                    walk_list!(visitor, visit_attribute, *attrs);
-                }
+        let krate = self.hir_crate_items(());
+        for owner in krate.owners() {
+            let attrs = self.hir_attr_map(owner);
+            for attrs in attrs.map.values() {
+                walk_list!(visitor, visit_attribute, *attrs);
             }
         }
         V::Result::output()
@@ -1225,6 +1223,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod
         ..
     } = collector;
     ModuleItems {
+        add_root: false,
         submodules: submodules.into_boxed_slice(),
         free_items: items.into_boxed_slice(),
         trait_items: trait_items.into_boxed_slice(),
@@ -1260,6 +1259,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
     } = collector;
 
     ModuleItems {
+        add_root: true,
         submodules: submodules.into_boxed_slice(),
         free_items: items.into_boxed_slice(),
         trait_items: trait_items.into_boxed_slice(),
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 9f79ed4..d7a8dce 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -24,6 +24,9 @@
 /// bodies. The Ids are in visitor order. This is used to partition a pass between modules.
 #[derive(Debug, HashStable, Encodable, Decodable)]
 pub struct ModuleItems {
+    /// Whether this represents the whole crate, in which case we need to add `CRATE_OWNER_ID` to
+    /// the iterators if we want to account for the crate root.
+    add_root: bool,
     submodules: Box<[OwnerId]>,
     free_items: Box<[ItemId]>,
     trait_items: Box<[TraitItemId]>,
@@ -66,9 +69,10 @@ pub fn foreign_items(&self) -> impl Iterator<Item = ForeignItemId> {
     }
 
     pub fn owners(&self) -> impl Iterator<Item = OwnerId> {
-        self.free_items
-            .iter()
-            .map(|id| id.owner_id)
+        self.add_root
+            .then_some(CRATE_OWNER_ID)
+            .into_iter()
+            .chain(self.free_items.iter().map(|id| id.owner_id))
             .chain(self.trait_items.iter().map(|id| id.owner_id))
             .chain(self.impl_items.iter().map(|id| id.owner_id))
             .chain(self.foreign_items.iter().map(|id| id.owner_id))
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index f1395c2..4f8cfd8 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1479,6 +1479,12 @@ pub struct GlobalCtxt<'tcx> {
 
     pub canonical_param_env_cache: CanonicalParamEnvCache<'tcx>,
 
+    /// Caches the index of the highest bound var in clauses in a canonical binder.
+    pub highest_var_in_clauses_cache: Lock<FxHashMap<ty::Clauses<'tcx>, usize>>,
+    /// Caches the instantiation of a canonical binder given a set of args.
+    pub clauses_cache:
+        Lock<FxHashMap<(ty::Clauses<'tcx>, &'tcx [ty::GenericArg<'tcx>]), ty::Clauses<'tcx>>>,
+
     /// Data layout specification for the current target.
     pub data_layout: TargetDataLayout,
 
@@ -1727,6 +1733,8 @@ pub fn create_global_ctxt<T>(
             new_solver_evaluation_cache: Default::default(),
             new_solver_canonical_param_env_cache: Default::default(),
             canonical_param_env_cache: Default::default(),
+            highest_var_in_clauses_cache: Default::default(),
+            clauses_cache: Default::default(),
             data_layout,
             alloc_map: interpret::AllocMap::new(),
             current_gcx,
@@ -2113,7 +2121,7 @@ pub fn def_path_hash_to_def_index_map(
     ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap {
         // Create a dependency to the crate to be sure we re-execute this when the amount of
         // definitions change.
-        self.ensure_ok().hir_crate(());
+        self.ensure_ok().hir_crate_items(());
         // Freeze definitions once we start iterating on them, to prevent adding new ones
         // while iterating. If some query needs to add definitions, it should be `ensure`d above.
         self.untracked.definitions.freeze().def_path_hash_to_def_index_map()
@@ -3160,42 +3168,33 @@ pub fn node_span_lint(
         lint_level(self.sess, lint, level, Some(span.into()), decorate);
     }
 
-    /// Find the crate root and the appropriate span where `use` and outer attributes can be
-    /// inserted at.
-    pub fn crate_level_attribute_injection_span(self, hir_id: HirId) -> Option<Span> {
-        for (_hir_id, node) in self.hir_parent_iter(hir_id) {
-            if let hir::Node::Crate(m) = node {
-                return Some(m.spans.inject_use_span.shrink_to_lo());
-            }
-        }
-        None
+    /// Find the appropriate span where `use` and outer attributes can be inserted at.
+    pub fn crate_level_attribute_injection_span(self) -> Span {
+        let node = self.hir_node(hir::CRATE_HIR_ID);
+        let hir::Node::Crate(m) = node else { bug!() };
+        m.spans.inject_use_span.shrink_to_lo()
     }
 
     pub fn disabled_nightly_features<E: rustc_errors::EmissionGuarantee>(
         self,
         diag: &mut Diag<'_, E>,
-        hir_id: Option<HirId>,
         features: impl IntoIterator<Item = (String, Symbol)>,
     ) {
         if !self.sess.is_nightly_build() {
             return;
         }
 
-        let span = hir_id.and_then(|id| self.crate_level_attribute_injection_span(id));
+        let span = self.crate_level_attribute_injection_span();
         for (desc, feature) in features {
             // FIXME: make this string translatable
             let msg =
                 format!("add `#![feature({feature})]` to the crate attributes to enable{desc}");
-            if let Some(span) = span {
-                diag.span_suggestion_verbose(
-                    span,
-                    msg,
-                    format!("#![feature({feature})]\n"),
-                    Applicability::MaybeIncorrect,
-                );
-            } else {
-                diag.help(msg);
-            }
+            diag.span_suggestion_verbose(
+                span,
+                msg,
+                format!("#![feature({feature})]\n"),
+                Applicability::MaybeIncorrect,
+            );
         }
     }
 
diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs
index 2a44c90..15679d2 100644
--- a/compiler/rustc_parse/src/parser/tests.rs
+++ b/compiler/rustc_parse/src/parser/tests.rs
@@ -14,6 +14,7 @@
 use rustc_ast::{self as ast, PatKind, visit};
 use rustc_ast_pretty::pprust::item_to_string;
 use rustc_errors::emitter::{HumanEmitter, OutputTheme};
+use rustc_errors::translation::Translator;
 use rustc_errors::{DiagCtxt, MultiSpan, PResult};
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::{FilePathMapping, SourceMap};
@@ -41,9 +42,8 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> {
 fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
     let output = Arc::new(Mutex::new(Vec::new()));
     let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
-    let fallback_bundle =
-        rustc_errors::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
-    let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle)
+    let translator = Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
+    let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), translator)
         .sm(Some(source_map.clone()))
         .diagnostic_width(Some(140));
     emitter = emitter.theme(theme);
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index a12215a..ed1737b 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -291,6 +291,8 @@ fn emit_malformed_attribute(
             | sym::repr
             | sym::align
             | sym::deprecated
+            | sym::optimize
+            | sym::cold
     ) {
         return;
     }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 50d6c5d..f25c9f3 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -128,6 +128,9 @@ fn check_attributes(
                 Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => {
                     self.check_inline(hir_id, *attr_span, span, kind, target)
                 }
+                Attribute::Parsed(AttributeKind::Optimize(_, attr_span)) => {
+                    self.check_optimize(hir_id, *attr_span, span, target)
+                }
                 Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
                     .check_allow_internal_unstable(
                         hir_id,
@@ -146,10 +149,12 @@ fn check_attributes(
                 }
                 Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */
                 }
+                Attribute::Parsed(AttributeKind::Cold(attr_span)) => {
+                    self.check_cold(hir_id, *attr_span, span, target)
+                }
                 Attribute::Parsed(AttributeKind::Align { align, span: repr_span }) => {
                     self.check_align(span, target, *align, *repr_span)
                 }
-
                 Attribute::Parsed(
                     AttributeKind::BodyStability { .. }
                     | AttributeKind::ConstStabilityIndirect
@@ -167,7 +172,6 @@ fn check_attributes(
                             self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
                         }
                         [sym::coverage, ..] => self.check_coverage(attr, span, target),
-                        [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target),
                         [sym::no_sanitize, ..] => {
                             self.check_no_sanitize(attr, span, target)
                         }
@@ -243,7 +247,6 @@ fn check_attributes(
                         [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
                         [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
                         [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
-                        [sym::cold, ..] => self.check_cold(hir_id, attr, span, target),
                         [sym::link, ..] => self.check_link(hir_id, attr, span, target),
                         [sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
                         [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target),
@@ -529,7 +532,7 @@ fn check_coverage(&self, attr: &Attribute, target_span: Span, target: Target) {
 
     /// Checks that `#[optimize(..)]` is applied to a function/closure/method,
     /// or to an impl block or module.
-    fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
+    fn check_optimize(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
         let is_valid = matches!(
             target,
             Target::Fn
@@ -538,7 +541,7 @@ fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Ta
         );
         if !is_valid {
             self.dcx().emit_err(errors::OptimizeInvalidTarget {
-                attr_span: attr.span(),
+                attr_span,
                 defn_span: span,
                 on_crate: hir_id == CRATE_HIR_ID,
             });
@@ -649,8 +652,6 @@ fn check_naked(
             sym::repr,
             sym::align,
             sym::rustc_std_internal_symbol,
-            // code generation
-            sym::cold,
             // documentation
             sym::doc,
         ];
@@ -686,7 +687,8 @@ fn check_naked(
                         Attribute::Parsed(
                             AttributeKind::Deprecation { .. }
                             | AttributeKind::Repr { .. }
-                            | AttributeKind::Align { .. },
+                            | AttributeKind::Align { .. }
+                            | AttributeKind::Cold(..),
                         ) => {
                             continue;
                         }
@@ -1635,7 +1637,7 @@ fn check_may_dangle(&self, hir_id: HirId, attr: &Attribute) {
     }
 
     /// Checks if `#[cold]` is applied to a non-function.
-    fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
+    fn check_cold(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
         match target {
             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
@@ -1643,7 +1645,7 @@ fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target
             // erroneously allowed it and some crates used it accidentally, to be compatible
             // with crates depending on them, we can't throw an error here.
             Target::Field | Target::Arm | Target::MacroDef => {
-                self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "cold");
+                self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "cold");
             }
             _ => {
                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
@@ -1651,7 +1653,7 @@ fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target
                 self.tcx.emit_node_span_lint(
                     UNUSED_ATTRIBUTES,
                     hir_id,
-                    attr.span(),
+                    attr_span,
                     errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID },
                 );
             }
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 6195361..528c52e 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -40,11 +40,6 @@
 
 session_file_write_fail = failed to write `{$path}` due to error `{$err}`
 
-session_forbidden_ctarget_feature =
-    target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
-    .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-session_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
-
 session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64
 
 session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models
@@ -137,9 +132,6 @@
 session_unleashed_feature_help_named = skipping check for `{$gate}` feature
 session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate
 
-session_unstable_ctarget_feature =
-    unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = this feature is not stably supported; its behavior can change in the future
 session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto`
 
 session_unsupported_crate_type_for_target =
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 9c591dc..bf95014 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -501,20 +501,3 @@ pub(crate) struct FailedToCreateProfiler {
 #[note]
 #[note(session_soft_float_deprecated_issue)]
 pub(crate) struct SoftFloatDeprecated;
-
-#[derive(Diagnostic)]
-#[diag(session_forbidden_ctarget_feature)]
-#[note]
-#[note(session_forbidden_ctarget_feature_issue)]
-pub(crate) struct ForbiddenCTargetFeature<'a> {
-    pub feature: &'a str,
-    pub enabled: &'a str,
-    pub reason: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(session_unstable_ctarget_feature)]
-#[note]
-pub(crate) struct UnstableCTargetFeature<'a> {
-    pub feature: &'a str,
-}
diff --git a/compiler/rustc_session/src/features.rs b/compiler/rustc_session/src/features.rs
deleted file mode 100644
index 70a088a..0000000
--- a/compiler/rustc_session/src/features.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use rustc_target::target_features::Stability;
-
-use crate::Session;
-use crate::errors::{ForbiddenCTargetFeature, UnstableCTargetFeature};
-
-pub trait StabilityExt {
-    /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
-    /// Otherwise, some features also may only be enabled by flag (target modifier).
-    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
-    /// `requires_nightly`.)
-    fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str>;
-
-    /// Check that feature is correctly enabled/disabled by command line flag (emits warnings)
-    fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str);
-}
-
-impl StabilityExt for Stability {
-    fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str> {
-        match self {
-            Stability::Forbidden { reason } => Err(reason),
-            Stability::TargetModifierOnly { reason, flag } => {
-                if !sess.opts.target_feature_flag_enabled(*flag) { Err(reason) } else { Ok(()) }
-            }
-            _ => Ok(()),
-        }
-    }
-    fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str) {
-        if let Err(reason) = self.is_toggle_permitted(sess) {
-            sess.dcx().emit_warn(ForbiddenCTargetFeature {
-                feature,
-                enabled: if enable { "enabled" } else { "disabled" },
-                reason,
-            });
-        } else if self.requires_nightly().is_some() {
-            // An unstable feature. Warn about using it. It makes little sense
-            // to hard-error here since we just warn about fully unknown
-            // features above.
-            sess.dcx().emit_warn(UnstableCTargetFeature { feature });
-        }
-    }
-}
-
-pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<&str>) {
-    // -Zretpoline without -Zretpoline-external-thunk enables
-    // retpoline-indirect-branches and retpoline-indirect-calls target features
-    let unstable_opts = &sess.opts.unstable_opts;
-    if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
-        features.push("+retpoline-indirect-branches");
-        features.push("+retpoline-indirect-calls");
-    }
-    // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables
-    // retpoline-external-thunk, retpoline-indirect-branches and
-    // retpoline-indirect-calls target features
-    if unstable_opts.retpoline_external_thunk {
-        features.push("+retpoline-external-thunk");
-        features.push("+retpoline-indirect-branches");
-        features.push("+retpoline-indirect-calls");
-    }
-}
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 4added1..5e5872e 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -29,7 +29,6 @@
 pub mod output;
 
 pub use getopts;
-pub mod features;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 31b4237..7fef942 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -290,14 +290,6 @@ pub fn gather_target_modifiers(&self) -> Vec<TargetModifier> {
                 mods.sort_by(|a, b| a.opt.cmp(&b.opt));
                 mods
             }
-
-            pub fn target_feature_flag_enabled(&self, flag: &str) -> bool {
-                match flag {
-                    "retpoline" => self.unstable_opts.retpoline,
-                    "retpoline-external-thunk" => self.unstable_opts.retpoline_external_thunk,
-                    _ => false,
-                }
-            }
         }
     );
 }
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 87c848c..0118cdb 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -8,10 +8,11 @@
 use rustc_ast::node_id::NodeId;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
 use rustc_data_structures::sync::{AppendOnlyVec, Lock};
-use rustc_errors::emitter::{HumanEmitter, SilentEmitter, stderr_destination};
+use rustc_errors::emitter::{FatalOnlyEmitter, HumanEmitter, stderr_destination};
+use rustc_errors::translation::Translator;
 use rustc_errors::{
     ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan,
-    StashKey, fallback_fluent_bundle,
+    StashKey,
 };
 use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue};
 use rustc_span::edition::Edition;
@@ -242,10 +243,10 @@ pub struct ParseSess {
 impl ParseSess {
     /// Used for testing.
     pub fn new(locale_resources: Vec<&'static str>) -> Self {
-        let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
+        let translator = Translator::with_fallback_bundle(locale_resources, false);
         let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
         let emitter = Box::new(
-            HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)
+            HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator)
                 .sm(Some(Arc::clone(&sm))),
         );
         let dcx = DiagCtxt::new(emitter);
@@ -274,19 +275,14 @@ pub fn with_dcx(dcx: DiagCtxt, source_map: Arc<SourceMap>) -> Self {
         }
     }
 
-    pub fn with_silent_emitter(
-        locale_resources: Vec<&'static str>,
-        fatal_note: String,
-        emit_fatal_diagnostic: bool,
-    ) -> Self {
-        let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
+    pub fn with_fatal_emitter(locale_resources: Vec<&'static str>, fatal_note: String) -> Self {
+        let translator = Translator::with_fallback_bundle(locale_resources, false);
         let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
         let fatal_emitter =
-            Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle));
-        let dcx = DiagCtxt::new(Box::new(SilentEmitter {
+            Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator));
+        let dcx = DiagCtxt::new(Box::new(FatalOnlyEmitter {
             fatal_emitter,
             fatal_note: Some(fatal_note),
-            emit_fatal_diagnostic,
         }))
         .disable_warnings();
         ParseSess::with_dcx(dcx, sm)
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index ca42c5a..ad58c3c 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -19,9 +19,10 @@
 };
 use rustc_errors::json::JsonEmitter;
 use rustc_errors::timings::TimingSectionHandler;
+use rustc_errors::translation::Translator;
 use rustc_errors::{
     Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort,
-    FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle,
+    TerminalUrl, fallback_fluent_bundle,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -948,8 +949,7 @@ pub fn apple_deployment_target(&self) -> apple::OSVersion {
 fn default_emitter(
     sopts: &config::Options,
     source_map: Arc<SourceMap>,
-    bundle: Option<Arc<FluentBundle>>,
-    fallback_bundle: LazyFallbackBundle,
+    translator: Translator,
 ) -> Box<DynEmitter> {
     let macro_backtrace = sopts.unstable_opts.macro_backtrace;
     let track_diagnostics = sopts.unstable_opts.track_diagnostics;
@@ -974,17 +974,11 @@ fn default_emitter(
             let short = kind.short();
 
             if let HumanReadableErrorType::AnnotateSnippet = kind {
-                let emitter = AnnotateSnippetEmitter::new(
-                    source_map,
-                    bundle,
-                    fallback_bundle,
-                    short,
-                    macro_backtrace,
-                );
+                let emitter =
+                    AnnotateSnippetEmitter::new(source_map, translator, short, macro_backtrace);
                 Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
             } else {
-                let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
-                    .fluent_bundle(bundle)
+                let emitter = HumanEmitter::new(stderr_destination(color_config), translator)
                     .sm(source_map)
                     .short_message(short)
                     .diagnostic_width(sopts.diagnostic_width)
@@ -1006,12 +1000,11 @@ fn default_emitter(
             JsonEmitter::new(
                 Box::new(io::BufWriter::new(io::stderr())),
                 source_map,
-                fallback_bundle,
+                translator,
                 pretty,
                 json_rendered,
                 color_config,
             )
-            .fluent_bundle(bundle)
             .ui_testing(sopts.unstable_opts.ui_testing)
             .ignored_directories_in_source_blocks(
                 sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(),
@@ -1030,7 +1023,7 @@ fn default_emitter(
 pub fn build_session(
     sopts: config::Options,
     io: CompilerIO,
-    bundle: Option<Arc<rustc_errors::FluentBundle>>,
+    fluent_bundle: Option<Arc<rustc_errors::FluentBundle>>,
     registry: rustc_errors::registry::Registry,
     fluent_resources: Vec<&'static str>,
     driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
@@ -1052,12 +1045,15 @@ pub fn build_session(
     let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow);
     let can_emit_warnings = !(warnings_allow || cap_lints_allow);
 
-    let fallback_bundle = fallback_fluent_bundle(
-        fluent_resources,
-        sopts.unstable_opts.translate_directionality_markers,
-    );
+    let translator = Translator {
+        fluent_bundle,
+        fallback_fluent_bundle: fallback_fluent_bundle(
+            fluent_resources,
+            sopts.unstable_opts.translate_directionality_markers,
+        ),
+    };
     let source_map = rustc_span::source_map::get_source_map().unwrap();
-    let emitter = default_emitter(&sopts, Arc::clone(&source_map), bundle, fallback_bundle);
+    let emitter = default_emitter(&sopts, Arc::clone(&source_map), translator);
 
     let mut dcx = DiagCtxt::new(emitter)
         .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings))
@@ -1500,13 +1496,13 @@ pub fn early_struct_warn(&self, msg: impl Into<DiagMessage>) -> Diag<'_, ()> {
 fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
     // FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will
     // need to reference every crate that might emit an early error for translation to work.
-    let fallback_bundle =
-        fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false);
+    let translator =
+        Translator::with_fallback_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false);
     let emitter: Box<DynEmitter> = match output {
         config::ErrorOutputType::HumanReadable { kind, color_config } => {
             let short = kind.short();
             Box::new(
-                HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
+                HumanEmitter::new(stderr_destination(color_config), translator)
                     .theme(if let HumanReadableErrorType::Unicode = kind {
                         OutputTheme::Unicode
                     } else {
@@ -1519,7 +1515,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
             Box::new(JsonEmitter::new(
                 Box::new(io::BufWriter::new(io::stderr())),
                 Some(Arc::new(SourceMap::new(FilePathMapping::empty()))),
-                fallback_bundle,
+                translator,
                 pretty,
                 json_rendered,
                 color_config,
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index a1eac1f..3eea1e0 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -11,11 +11,6 @@
 /// These exist globally and are not in the target-specific lists below.
 pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
 
-/// Features that require special handling when passing to LLVM:
-/// these are target-specific (i.e., must also be listed in the target-specific list below)
-/// but do not correspond to an LLVM target feature.
-pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"];
-
 /// Stability information for target features.
 #[derive(Debug, Copy, Clone)]
 pub enum Stability {
@@ -34,9 +29,6 @@ pub enum Stability {
     /// particular for features are actually ABI configuration flags (not all targets are as nice as
     /// RISC-V and have an explicit way to set the ABI separate from target features).
     Forbidden { reason: &'static str },
-    /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set
-    /// by target modifier flag. Target modifier flags are tracked to be consistent in linked modules.
-    TargetModifierOnly { reason: &'static str, flag: &'static str },
 }
 use Stability::*;
 
@@ -52,7 +44,6 @@ fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
             Stability::Forbidden { reason } => {
                 reason.hash_stable(hcx, hasher);
             }
-            Stability::TargetModifierOnly { .. } => {}
         }
     }
 }
@@ -62,7 +53,7 @@ impl Stability {
     /// (It might still be nightly-only even if this returns `true`, so make sure to also check
     /// `requires_nightly`.)
     pub fn in_cfg(&self) -> bool {
-        !matches!(self, Stability::Forbidden { .. })
+        matches!(self, Stability::Stable | Stability::Unstable { .. })
     }
 
     /// Returns the nightly feature that is required to toggle this target feature via
@@ -78,7 +69,16 @@ pub fn requires_nightly(&self) -> Option<Symbol> {
             Stability::Unstable(nightly_feature) => Some(nightly_feature),
             Stability::Stable { .. } => None,
             Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"),
-            Stability::TargetModifierOnly { .. } => None,
+        }
+    }
+
+    /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
+    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
+    /// `requires_nightly`.)
+    pub fn toggle_allowed(&self) -> Result<(), &'static str> {
+        match self {
+            Stability::Unstable(_) | Stability::Stable { .. } => Ok(()),
+            Stability::Forbidden { reason } => Err(reason),
         }
     }
 }
@@ -270,12 +270,7 @@ pub fn requires_nightly(&self) -> Option<Symbol> {
     ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]),
     // FEAT_RDM
     ("rdm", Stable, &["neon"]),
-    // This is needed for inline assembly, but shouldn't be stabilized as-is
-    // since it should be enabled globally using -Zfixed-x18, not
-    // #[target_feature].
-    // Note that cfg(target_feature = "reserve-x18") is currently not set for
-    // targets that reserve x18 by default.
-    ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]),
+    ("reserve-x18", Forbidden { reason: "use `-Zfixed-x18` compiler flag instead" }, &[]),
     // FEAT_SB
     ("sb", Stable, &[]),
     // FEAT_SHA1 & FEAT_SHA256
@@ -450,26 +445,17 @@ pub fn requires_nightly(&self) -> Option<Symbol> {
     ("rdseed", Stable, &[]),
     (
         "retpoline-external-thunk",
-        Stability::TargetModifierOnly {
-            reason: "use `retpoline-external-thunk` target modifier flag instead",
-            flag: "retpoline-external-thunk",
-        },
+        Stability::Forbidden { reason: "use `-Zretpoline-external-thunk` compiler flag instead" },
         &[],
     ),
     (
         "retpoline-indirect-branches",
-        Stability::TargetModifierOnly {
-            reason: "use `retpoline` target modifier flag instead",
-            flag: "retpoline",
-        },
+        Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" },
         &[],
     ),
     (
         "retpoline-indirect-calls",
-        Stability::TargetModifierOnly {
-            reason: "use `retpoline` target modifier flag instead",
-            flag: "retpoline",
-        },
+        Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" },
         &[],
     ),
     ("rtm", Unstable(sym::rtm_target_feature), &[]),
@@ -732,6 +718,7 @@ pub fn requires_nightly(&self) -> Option<Symbol> {
 #[rustfmt::skip]
 const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     // tidy-alphabetical-start
+    // For "backchain", https://github.com/rust-lang/rust/issues/142412 is a stabilization blocker
     ("backchain", Unstable(sym::s390x_target_feature), &[]),
     ("concurrent-functions", Unstable(sym::s390x_target_feature), &[]),
     ("deflate-conversion", Unstable(sym::s390x_target_feature), &[]),
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index ee5a5b2..6d07ae0 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3575,11 +3575,7 @@ pub(super) fn note_obligation_cause_code<G: EmissionGuarantee, T>(
             }
             ObligationCauseCode::TrivialBound => {
                 err.help("see issue #48214");
-                tcx.disabled_nightly_features(
-                    err,
-                    Some(tcx.local_def_id_to_hir_id(body_id)),
-                    [(String::new(), sym::trivial_bounds)],
-                );
+                tcx.disabled_nightly_features(err, [(String::new(), sym::trivial_bounds)]);
             }
             ObligationCauseCode::OpaqueReturnType(expr_info) => {
                 let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info {
diff --git a/library/Cargo.lock b/library/Cargo.lock
index 34012d6..4f0e7ae 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -219,21 +219,19 @@
 
 [[package]]
 name = "r-efi"
-version = "5.2.0"
+version = "5.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
 [[package]]
 name = "r-efi-alloc"
-version = "2.0.0"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e43c53ff1a01d423d1cb762fd991de07d32965ff0ca2e4f80444ac7804198203"
+checksum = "dc2f58ef3ca9bb0f9c44d9aa8537601bcd3df94cc9314a40178cadf7d4466354"
 dependencies = [
- "compiler_builtins",
  "r-efi",
  "rustc-std-workspace-core",
 ]
diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs
index 410e67d..3d6c740 100644
--- a/library/alloc/src/collections/linked_list/tests.rs
+++ b/library/alloc/src/collections/linked_list/tests.rs
@@ -1,4 +1,3 @@
-use std::cell::Cell;
 use std::panic::{AssertUnwindSafe, catch_unwind};
 use std::thread;
 
@@ -6,6 +5,7 @@
 
 use super::*;
 use crate::testing::crash_test::{CrashTestDummy, Panic};
+use crate::testing::macros::struct_with_counted_drop;
 use crate::vec::Vec;
 
 #[test]
@@ -1010,22 +1010,6 @@ fn extract_if_drop_panic_leak() {
     assert_eq!(d7.dropped(), 1);
 }
 
-macro_rules! struct_with_counted_drop {
-    ($struct_name:ident$(($elt_ty:ty))?, $drop_counter:ident $(=> $drop_stmt:expr)?) => {
-        thread_local! {static $drop_counter: Cell<u32> = Cell::new(0);}
-
-        struct $struct_name$(($elt_ty))?;
-
-        impl Drop for $struct_name {
-            fn drop(&mut self) {
-                $drop_counter.set($drop_counter.get() + 1);
-
-                $($drop_stmt(self))?
-            }
-        }
-    };
-}
-
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn extract_if_pred_panic_leak() {
diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs
index c90679f..ad76cb1 100644
--- a/library/alloc/src/collections/vec_deque/tests.rs
+++ b/library/alloc/src/collections/vec_deque/tests.rs
@@ -1,9 +1,7 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
 use core::iter::TrustedLen;
 
 use super::*;
+use crate::testing::macros::struct_with_counted_drop;
 
 #[bench]
 fn bench_push_back_100(b: &mut test::Bencher) {
@@ -1086,36 +1084,24 @@ fn test_clone_from() {
 
 #[test]
 fn test_vec_deque_truncate_drop() {
-    static mut DROPS: u32 = 0;
-    #[derive(Clone)]
-    struct Elem(#[allow(dead_code)] i32);
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem, DROPS);
 
-    let v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)];
-    for push_front in 0..=v.len() {
-        let v = v.clone();
-        let mut tester = VecDeque::with_capacity(5);
-        for (index, elem) in v.into_iter().enumerate() {
+    const LEN: usize = 5;
+    for push_front in 0..=LEN {
+        let mut tester = VecDeque::with_capacity(LEN);
+        for index in 0..LEN {
             if index < push_front {
-                tester.push_front(elem);
+                tester.push_front(Elem);
             } else {
-                tester.push_back(elem);
+                tester.push_back(Elem);
             }
         }
-        assert_eq!(unsafe { DROPS }, 0);
+        assert_eq!(DROPS.get(), 0);
         tester.truncate(3);
-        assert_eq!(unsafe { DROPS }, 2);
+        assert_eq!(DROPS.get(), 2);
         tester.truncate(0);
-        assert_eq!(unsafe { DROPS }, 5);
-        unsafe {
-            DROPS = 0;
-        }
+        assert_eq!(DROPS.get(), 5);
+        DROPS.set(0);
     }
 }
 
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index 7c71594..5197e40 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -2877,6 +2877,7 @@ fn spec_to_string(&self) -> String {
     i32, u32,
     i64, u64,
     isize, usize,
+    i128, u128,
 }
 
 #[cfg(not(no_global_oom_handling))]
diff --git a/library/alloctests/testing/macros.rs b/library/alloctests/testing/macros.rs
new file mode 100644
index 0000000..2433e53
--- /dev/null
+++ b/library/alloctests/testing/macros.rs
@@ -0,0 +1,37 @@
+macro_rules! struct_with_counted_drop {
+    ($struct_name:ident $(( $( $elt_ty:ty ),+ ))?, $drop_counter:ident $( => $drop_stmt:expr )? ) => {
+        thread_local! {static $drop_counter: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);}
+
+        #[derive(Clone, Debug, PartialEq)]
+        struct $struct_name $(( $( $elt_ty ),+ ))?;
+
+        impl ::std::ops::Drop for $struct_name {
+            fn drop(&mut self) {
+                $drop_counter.set($drop_counter.get() + 1);
+
+                $($drop_stmt(self))?
+            }
+        }
+    };
+    ($struct_name:ident $(( $( $elt_ty:ty ),+ ))?, $drop_counter:ident[ $drop_key:expr,$key_ty:ty ] $( => $drop_stmt:expr )? ) => {
+        thread_local! {
+            static $drop_counter: ::core::cell::RefCell<::std::collections::HashMap<$key_ty, u32>> =
+                ::core::cell::RefCell::new(::std::collections::HashMap::new());
+        }
+
+        #[derive(Clone, Debug, PartialEq)]
+        struct $struct_name $(( $( $elt_ty ),+ ))?;
+
+        impl ::std::ops::Drop for $struct_name {
+            fn drop(&mut self) {
+                $drop_counter.with_borrow_mut(|counter| {
+                    *counter.entry($drop_key(self)).or_default() += 1;
+                });
+
+                $($drop_stmt(self))?
+            }
+        }
+    };
+}
+
+pub(crate) use struct_with_counted_drop;
diff --git a/library/alloctests/testing/mod.rs b/library/alloctests/testing/mod.rs
index c8457da..66a4f66 100644
--- a/library/alloctests/testing/mod.rs
+++ b/library/alloctests/testing/mod.rs
@@ -1,3 +1,4 @@
 pub(crate) mod crash_test;
+pub(crate) mod macros;
 pub(crate) mod ord_chaos;
 pub(crate) mod rng;
diff --git a/library/alloctests/tests/fmt.rs b/library/alloctests/tests/fmt.rs
index dbcf0c3..0989a56 100644
--- a/library/alloctests/tests/fmt.rs
+++ b/library/alloctests/tests/fmt.rs
@@ -1,6 +1,4 @@
 #![deny(warnings)]
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
 #![allow(unnecessary_transmutes)]
 
 use std::cell::RefCell;
@@ -285,19 +283,32 @@ fn test_format_args() {
     t!(s, "args were: hello world");
 }
 
+macro_rules! counter_fn {
+    ($name:ident) => {
+        fn $name() -> u32 {
+            thread_local! {static COUNTER: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);}
+
+            COUNTER.set(COUNTER.get() + 1);
+            COUNTER.get()
+        }
+    };
+}
+
 #[test]
 fn test_order() {
-    // Make sure format!() arguments are always evaluated in a left-to-right
-    // ordering
-    fn foo() -> isize {
-        static mut FOO: isize = 0;
-        unsafe {
-            FOO += 1;
-            FOO
-        }
-    }
+    // Make sure format!() arguments are always evaluated in a left-to-right ordering
+    counter_fn!(count);
+
     assert_eq!(
-        format!("{} {} {a} {b} {} {c}", foo(), foo(), foo(), a = foo(), b = foo(), c = foo()),
+        format!(
+            "{} {} {a} {b} {} {c}",
+            count(),
+            count(),
+            count(),
+            a = count(),
+            b = count(),
+            c = count()
+        ),
         "1 2 4 5 3 6".to_string()
     );
 }
@@ -306,14 +317,9 @@ fn foo() -> isize {
 fn test_once() {
     // Make sure each argument are evaluated only once even though it may be
     // formatted multiple times
-    fn foo() -> isize {
-        static mut FOO: isize = 0;
-        unsafe {
-            FOO += 1;
-            FOO
-        }
-    }
-    assert_eq!(format!("{0} {0} {0} {a} {a} {a}", foo(), a = foo()), "1 1 1 2 2 2".to_string());
+    counter_fn!(count);
+
+    assert_eq!(format!("{0} {0} {0} {a} {a} {a}", count(), a = count()), "1 1 1 2 2 2".to_string());
 }
 
 #[test]
diff --git a/library/alloctests/tests/num.rs b/library/alloctests/tests/num.rs
index 3c76e68..a169bbe 100644
--- a/library/alloctests/tests/num.rs
+++ b/library/alloctests/tests/num.rs
@@ -52,6 +52,8 @@ fn $fn_name() {
     i32,
     test_i64_to_string,
     i64,
+    test_isize_to_string,
+    isize,
     test_i128_to_string,
     i128,
 );
@@ -64,6 +66,8 @@ fn $fn_name() {
     u32,
     test_u64_to_string,
     u64,
+    test_usize_to_string,
+    usize,
     test_u128_to_string,
     u128,
 );
diff --git a/library/alloctests/tests/testing/mod.rs b/library/alloctests/tests/testing/mod.rs
index 30275a5..08856e3 100644
--- a/library/alloctests/tests/testing/mod.rs
+++ b/library/alloctests/tests/testing/mod.rs
@@ -1,2 +1,4 @@
 #[path = "../../testing/crash_test.rs"]
 pub mod crash_test;
+#[path = "../../testing/macros.rs"]
+pub mod macros;
diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs
index 51b49b8..00f640c 100644
--- a/library/alloctests/tests/vec.rs
+++ b/library/alloctests/tests/vec.rs
@@ -1,6 +1,3 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
 use core::alloc::{Allocator, Layout};
 use core::num::NonZero;
 use core::ptr::NonNull;
@@ -20,6 +17,8 @@
 use std::sync::atomic::{AtomicU32, Ordering};
 use std::vec::{Drain, IntoIter};
 
+use crate::testing::macros::struct_with_counted_drop;
+
 struct DropCounter<'a> {
     count: &'a mut u32,
 }
@@ -548,32 +547,25 @@ fn test_cmp() {
 
 #[test]
 fn test_vec_truncate_drop() {
-    static mut DROPS: u32 = 0;
-    struct Elem(#[allow(dead_code)] i32);
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem(i32), DROPS);
 
     let mut v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)];
-    assert_eq!(unsafe { DROPS }, 0);
+
+    assert_eq!(DROPS.get(), 0);
     v.truncate(3);
-    assert_eq!(unsafe { DROPS }, 2);
+    assert_eq!(DROPS.get(), 2);
     v.truncate(0);
-    assert_eq!(unsafe { DROPS }, 5);
+    assert_eq!(DROPS.get(), 5);
 }
 
 #[test]
 #[should_panic]
 fn test_vec_truncate_fail() {
     struct BadElem(i32);
+
     impl Drop for BadElem {
         fn drop(&mut self) {
-            let BadElem(ref mut x) = *self;
-            if *x == 0xbadbeef {
+            if let BadElem(0xbadbeef) = self {
                 panic!("BadElem panic: 0xbadbeef")
             }
         }
@@ -812,22 +804,7 @@ fn test_drain_end_overflow() {
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn test_drain_leak() {
-    static mut DROPS: i32 = 0;
-
-    #[derive(Debug, PartialEq)]
-    struct D(u32, bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.1 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(u32, bool), DROPS => |this: &D| if this.1 { panic!("panic in `drop`"); });
 
     let mut v = vec![
         D(0, false),
@@ -844,7 +821,7 @@ fn drop(&mut self) {
     }))
     .ok();
 
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
     assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]);
 }
 
@@ -1057,27 +1034,13 @@ fn iter_equal<I: Iterator<Item = i32>>(it: I, slice: &[i32]) {
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn test_into_iter_leak() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); });
 
     let v = vec![D(false), D(true), D(false)];
 
     catch_unwind(move || drop(v.into_iter())).ok();
 
-    assert_eq!(unsafe { DROPS }, 3);
+    assert_eq!(DROPS.get(), 3);
 }
 
 #[test]
@@ -1274,55 +1237,31 @@ fn test_from_iter_specialization_panic_during_iteration_drops() {
 
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#[allow(static_mut_refs)]
 fn test_from_iter_specialization_panic_during_drop_doesnt_leak() {
-    static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5];
-    static mut DROP_COUNTER_NEW: [usize; 2] = [0; 2];
-
-    #[derive(Debug)]
-    struct Old(usize);
-
-    impl Drop for Old {
-        fn drop(&mut self) {
-            unsafe {
-                DROP_COUNTER_OLD[self.0] += 1;
+    struct_with_counted_drop!(
+        Old(usize), DROP_COUNTER_OLD[|this: &Old| this.0, usize] =>
+            |this: &Old| {
+                if this.0 == 3 { panic!(); } println!("Dropped Old: {}", this.0)
             }
-
-            if self.0 == 3 {
-                panic!();
-            }
-
-            println!("Dropped Old: {}", self.0);
-        }
-    }
-
-    #[derive(Debug)]
-    struct New(usize);
-
-    impl Drop for New {
-        fn drop(&mut self) {
-            unsafe {
-                DROP_COUNTER_NEW[self.0] += 1;
-            }
-
-            println!("Dropped New: {}", self.0);
-        }
-    }
+    );
+    struct_with_counted_drop!(
+        New(usize), DROP_COUNTER_NEW[|this: &New| this.0, usize] =>
+            |this: &New| println!("Dropped New: {}", this.0)
+    );
 
     let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
         let v = vec![Old(0), Old(1), Old(2), Old(3), Old(4)];
         let _ = v.into_iter().map(|x| New(x.0)).take(2).collect::<Vec<_>>();
     }));
 
-    assert_eq!(unsafe { DROP_COUNTER_OLD[0] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_OLD[1] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_OLD[2] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_OLD[3] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_OLD[4] }, 1);
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&0), Some(&1)));
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&1), Some(&1)));
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&2), Some(&1)));
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&3), Some(&1)));
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&4), Some(&1)));
 
-    assert_eq!(unsafe { DROP_COUNTER_NEW[0] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_NEW[1] }, 1);
+    DROP_COUNTER_NEW.with_borrow(|c| assert_eq!(c.get(&0), Some(&1)));
+    DROP_COUNTER_NEW.with_borrow(|c| assert_eq!(c.get(&1), Some(&1)));
 }
 
 // regression test for issue #85322. Peekable previously implemented InPlaceIterable,
diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs
index b77ea3a..a82906d 100644
--- a/library/alloctests/tests/vec_deque.rs
+++ b/library/alloctests/tests/vec_deque.rs
@@ -1,6 +1,3 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
 use core::num::NonZero;
 use std::assert_matches::assert_matches;
 use std::collections::TryReserveErrorKind::*;
@@ -14,6 +11,7 @@
 use Taggypar::*;
 
 use crate::hash;
+use crate::testing::macros::struct_with_counted_drop;
 
 #[test]
 fn test_simple() {
@@ -719,15 +717,7 @@ fn test_show() {
 
 #[test]
 fn test_drop() {
-    static mut DROPS: i32 = 0;
-    struct Elem;
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem, DROPS);
 
     let mut ring = VecDeque::new();
     ring.push_back(Elem);
@@ -736,20 +726,12 @@ fn drop(&mut self) {
     ring.push_front(Elem);
     drop(ring);
 
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
 }
 
 #[test]
 fn test_drop_with_pop() {
-    static mut DROPS: i32 = 0;
-    struct Elem;
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem, DROPS);
 
     let mut ring = VecDeque::new();
     ring.push_back(Elem);
@@ -759,23 +741,15 @@ fn drop(&mut self) {
 
     drop(ring.pop_back());
     drop(ring.pop_front());
-    assert_eq!(unsafe { DROPS }, 2);
+    assert_eq!(DROPS.get(), 2);
 
     drop(ring);
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
 }
 
 #[test]
 fn test_drop_clear() {
-    static mut DROPS: i32 = 0;
-    struct Elem;
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem, DROPS);
 
     let mut ring = VecDeque::new();
     ring.push_back(Elem);
@@ -783,30 +757,16 @@ fn drop(&mut self) {
     ring.push_back(Elem);
     ring.push_front(Elem);
     ring.clear();
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
 
     drop(ring);
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
 }
 
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn test_drop_panic() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
 
     let mut q = VecDeque::new();
     q.push_back(D(false));
@@ -820,7 +780,7 @@ fn drop(&mut self) {
 
     catch_unwind(move || drop(q)).ok();
 
-    assert_eq!(unsafe { DROPS }, 8);
+    assert_eq!(DROPS.get(), 8);
 }
 
 #[test]
@@ -1655,21 +1615,7 @@ fn test_try_rfold_moves_iter() {
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn truncate_leak() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
 
     let mut q = VecDeque::new();
     q.push_back(D(false));
@@ -1683,27 +1629,13 @@ fn drop(&mut self) {
 
     catch_unwind(AssertUnwindSafe(|| q.truncate(1))).ok();
 
-    assert_eq!(unsafe { DROPS }, 7);
+    assert_eq!(DROPS.get(), 7);
 }
 
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn truncate_front_leak() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
 
     let mut q = VecDeque::new();
     q.push_back(D(false));
@@ -1717,28 +1649,13 @@ fn drop(&mut self) {
 
     catch_unwind(AssertUnwindSafe(|| q.truncate_front(1))).ok();
 
-    assert_eq!(unsafe { DROPS }, 7);
+    assert_eq!(DROPS.get(), 7);
 }
 
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn test_drain_leak() {
-    static mut DROPS: i32 = 0;
-
-    #[derive(Debug, PartialEq)]
-    struct D(u32, bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.1 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(u32, bool), DROPS => |this: &D| if this.1 { panic!("panic in `drop`"); } );
 
     let mut v = VecDeque::new();
     v.push_back(D(4, false));
@@ -1754,10 +1671,10 @@ fn drop(&mut self) {
     }))
     .ok();
 
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
     assert_eq!(v.len(), 3);
     drop(v);
-    assert_eq!(unsafe { DROPS }, 7);
+    assert_eq!(DROPS.get(), 7);
 }
 
 #[test]
diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs
index ce878f2..13cd7f7 100644
--- a/library/core/src/fmt/num.rs
+++ b/library/core/src/fmt/num.rs
@@ -565,123 +565,134 @@ mod imp {
 }
 impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
 
+const U128_MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for u128 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt_u128(*self, true, f)
+        let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
+
+        f.pad_integral(true, "", self._fmt(&mut buf))
     }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for i128 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt_u128(self.unsigned_abs(), *self >= 0, f)
+        // This is not a typo, we use the maximum number of digits of `u128`, hence why we use
+        // `U128_MAX_DEC_N`.
+        let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
+
+        let is_nonnegative = *self >= 0;
+        f.pad_integral(is_nonnegative, "", self.unsigned_abs()._fmt(&mut buf))
     }
 }
 
-/// Format optimized for u128. Computation of 128 bits is limited by proccessing
-/// in batches of 16 decimals at a time.
-fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    // Optimize common-case zero, which would also need special treatment due to
-    // its "leading" zero.
-    if n == 0 {
-        return f.pad_integral(true, "", "0");
-    }
-
-    // U128::MAX has 39 significant-decimals.
-    const MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
-    // Buffer decimals with right alignment.
-    let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
-
-    // Take the 16 least-significant decimals.
-    let (quot_1e16, mod_1e16) = div_rem_1e16(n);
-    let (mut remain, mut offset) = if quot_1e16 == 0 {
-        (mod_1e16, MAX_DEC_N)
-    } else {
-        // Write digits at buf[23..39].
-        enc_16lsd::<{ MAX_DEC_N - 16 }>(&mut buf, mod_1e16);
-
-        // Take another 16 decimals.
-        let (quot2, mod2) = div_rem_1e16(quot_1e16);
-        if quot2 == 0 {
-            (mod2, MAX_DEC_N - 16)
-        } else {
-            // Write digits at buf[7..23].
-            enc_16lsd::<{ MAX_DEC_N - 32 }>(&mut buf, mod2);
-            // Quot2 has at most 7 decimals remaining after two 1e16 divisions.
-            (quot2 as u64, MAX_DEC_N - 32)
+impl u128 {
+    /// Format optimized for u128. Computation of 128 bits is limited by proccessing
+    /// in batches of 16 decimals at a time.
+    #[doc(hidden)]
+    #[unstable(
+        feature = "fmt_internals",
+        reason = "specialized method meant to only be used by `SpecToString` implementation",
+        issue = "none"
+    )]
+    pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit<u8>]) -> &'a str {
+        // Optimize common-case zero, which would also need special treatment due to
+        // its "leading" zero.
+        if self == 0 {
+            return "0";
         }
-    };
 
-    // Format per four digits from the lookup table.
-    while remain > 999 {
-        // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
-        // and the while condition ensures at least 4 more decimals.
-        unsafe { core::hint::assert_unchecked(offset >= 4) }
-        // SAFETY: The offset counts down from its initial buf.len()
-        // without underflow due to the previous precondition.
-        unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
-        offset -= 4;
+        // Take the 16 least-significant decimals.
+        let (quot_1e16, mod_1e16) = div_rem_1e16(self);
+        let (mut remain, mut offset) = if quot_1e16 == 0 {
+            (mod_1e16, U128_MAX_DEC_N)
+        } else {
+            // Write digits at buf[23..39].
+            enc_16lsd::<{ U128_MAX_DEC_N - 16 }>(buf, mod_1e16);
 
-        // pull two pairs
-        let quad = remain % 1_00_00;
-        remain /= 1_00_00;
-        let pair1 = (quad / 100) as usize;
-        let pair2 = (quad % 100) as usize;
-        buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
-        buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
-        buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
-        buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
+            // Take another 16 decimals.
+            let (quot2, mod2) = div_rem_1e16(quot_1e16);
+            if quot2 == 0 {
+                (mod2, U128_MAX_DEC_N - 16)
+            } else {
+                // Write digits at buf[7..23].
+                enc_16lsd::<{ U128_MAX_DEC_N - 32 }>(buf, mod2);
+                // Quot2 has at most 7 decimals remaining after two 1e16 divisions.
+                (quot2 as u64, U128_MAX_DEC_N - 32)
+            }
+        };
+
+        // Format per four digits from the lookup table.
+        while remain > 999 {
+            // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
+            // and the while condition ensures at least 4 more decimals.
+            unsafe { core::hint::assert_unchecked(offset >= 4) }
+            // SAFETY: The offset counts down from its initial buf.len()
+            // without underflow due to the previous precondition.
+            unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
+            offset -= 4;
+
+            // pull two pairs
+            let quad = remain % 1_00_00;
+            remain /= 1_00_00;
+            let pair1 = (quad / 100) as usize;
+            let pair2 = (quad % 100) as usize;
+            buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
+            buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
+            buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
+            buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
+        }
+
+        // Format per two digits from the lookup table.
+        if remain > 9 {
+            // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
+            // and the if condition ensures at least 2 more decimals.
+            unsafe { core::hint::assert_unchecked(offset >= 2) }
+            // SAFETY: The offset counts down from its initial buf.len()
+            // without underflow due to the previous precondition.
+            unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
+            offset -= 2;
+
+            let pair = (remain % 100) as usize;
+            remain /= 100;
+            buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
+            buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
+        }
+
+        // Format the last remaining digit, if any.
+        if remain != 0 {
+            // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
+            // and the if condition ensures (at least) 1 more decimals.
+            unsafe { core::hint::assert_unchecked(offset >= 1) }
+            // SAFETY: The offset counts down from its initial buf.len()
+            // without underflow due to the previous precondition.
+            unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
+            offset -= 1;
+
+            // Either the compiler sees that remain < 10, or it prevents
+            // a boundary check up next.
+            let last = (remain & 15) as usize;
+            buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
+            // not used: remain = 0;
+        }
+
+        // SAFETY: All buf content since offset is set.
+        let written = unsafe { buf.get_unchecked(offset..) };
+        // SAFETY: Writes use ASCII from the lookup table exclusively.
+        unsafe {
+            str::from_utf8_unchecked(slice::from_raw_parts(
+                MaybeUninit::slice_as_ptr(written),
+                written.len(),
+            ))
+        }
     }
-
-    // Format per two digits from the lookup table.
-    if remain > 9 {
-        // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
-        // and the if condition ensures at least 2 more decimals.
-        unsafe { core::hint::assert_unchecked(offset >= 2) }
-        // SAFETY: The offset counts down from its initial buf.len()
-        // without underflow due to the previous precondition.
-        unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
-        offset -= 2;
-
-        let pair = (remain % 100) as usize;
-        remain /= 100;
-        buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
-        buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
-    }
-
-    // Format the last remaining digit, if any.
-    if remain != 0 {
-        // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
-        // and the if condition ensures (at least) 1 more decimals.
-        unsafe { core::hint::assert_unchecked(offset >= 1) }
-        // SAFETY: The offset counts down from its initial buf.len()
-        // without underflow due to the previous precondition.
-        unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
-        offset -= 1;
-
-        // Either the compiler sees that remain < 10, or it prevents
-        // a boundary check up next.
-        let last = (remain & 15) as usize;
-        buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
-        // not used: remain = 0;
-    }
-
-    // SAFETY: All buf content since offset is set.
-    let written = unsafe { buf.get_unchecked(offset..) };
-    // SAFETY: Writes use ASCII from the lookup table exclusively.
-    let as_str = unsafe {
-        str::from_utf8_unchecked(slice::from_raw_parts(
-            MaybeUninit::slice_as_ptr(written),
-            written.len(),
-        ))
-    };
-    f.pad_integral(is_nonnegative, "", as_str)
 }
 
 /// Encodes the 16 least-significant decimals of n into `buf[OFFSET .. OFFSET +
 /// 16 ]`.
-fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>; 39], n: u64) {
+fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], n: u64) {
     // Consume the least-significant decimals from a working copy.
     let mut remain = n;
 
diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs
index 7fd8d3e..fb858a0 100644
--- a/library/core/src/fmt/rt.rs
+++ b/library/core/src/fmt/rt.rs
@@ -183,38 +183,6 @@ pub(super) const fn as_u16(&self) -> Option<u16> {
             ArgumentType::Placeholder { .. } => None,
         }
     }
-
-    /// Used by `format_args` when all arguments are gone after inlining,
-    /// when using `&[]` would incorrectly allow for a bigger lifetime.
-    ///
-    /// This fails without format argument inlining, and that shouldn't be different
-    /// when the argument is inlined:
-    ///
-    /// ```compile_fail,E0716
-    /// let f = format_args!("{}", "a");
-    /// println!("{f}");
-    /// ```
-    #[inline]
-    pub const fn none() -> [Self; 0] {
-        []
-    }
-}
-
-/// This struct represents the unsafety of constructing an `Arguments`.
-/// It exists, rather than an unsafe function, in order to simplify the expansion
-/// of `format_args!(..)` and reduce the scope of the `unsafe` block.
-#[lang = "format_unsafe_arg"]
-pub struct UnsafeArg {
-    _private: (),
-}
-
-impl UnsafeArg {
-    /// See documentation where `UnsafeArg` is required to know when it is safe to
-    /// create and use `UnsafeArg`.
-    #[inline]
-    pub const unsafe fn new() -> Self {
-        Self { _private: () }
-    }
 }
 
 /// Used by the format_args!() macro to create a fmt::Arguments object.
@@ -248,8 +216,7 @@ pub fn new_v1<const P: usize, const A: usize>(
 
     /// Specifies nonstandard formatting parameters.
     ///
-    /// An `rt::UnsafeArg` is required because the following invariants must be held
-    /// in order for this function to be safe:
+    /// SAFETY: the following invariants must be held:
     /// 1. The `pieces` slice must be at least as long as `fmt`.
     /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
     /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
@@ -261,11 +228,10 @@ pub fn new_v1<const P: usize, const A: usize>(
     /// const _: () = if false { panic!("a {:1}", "a") };
     /// ```
     #[inline]
-    pub fn new_v1_formatted(
+    pub unsafe fn new_v1_formatted(
         pieces: &'a [&'static str],
         args: &'a [rt::Argument<'a>],
         fmt: &'a [rt::Placeholder],
-        _unsafe_arg: rt::UnsafeArg,
     ) -> Arguments<'a> {
         Arguments { pieces, fmt: Some(fmt), args }
     }
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index c26bbad..3a3f44c 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -2764,6 +2764,89 @@ pub fn strip_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> Op
         None
     }
 
+    /// Returns a subslice with the optional prefix removed.
+    ///
+    /// If the slice starts with `prefix`, returns the subslice after the prefix.  If `prefix`
+    /// is empty or the slice does not start with `prefix`, simply returns the original slice.
+    /// If `prefix` is equal to the original slice, returns an empty slice.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(trim_prefix_suffix)]
+    ///
+    /// let v = &[10, 40, 30];
+    ///
+    /// // Prefix present - removes it
+    /// assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]);
+    /// assert_eq!(v.trim_prefix(&[10, 40]), &[30][..]);
+    /// assert_eq!(v.trim_prefix(&[10, 40, 30]), &[][..]);
+    ///
+    /// // Prefix absent - returns original slice
+    /// assert_eq!(v.trim_prefix(&[50]), &[10, 40, 30][..]);
+    /// assert_eq!(v.trim_prefix(&[10, 50]), &[10, 40, 30][..]);
+    ///
+    /// let prefix : &str = "he";
+    /// assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref());
+    /// ```
+    #[must_use = "returns the subslice without modifying the original"]
+    #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+    pub fn trim_prefix<P: SlicePattern<Item = T> + ?Sized>(&self, prefix: &P) -> &[T]
+    where
+        T: PartialEq,
+    {
+        // This function will need rewriting if and when SlicePattern becomes more sophisticated.
+        let prefix = prefix.as_slice();
+        let n = prefix.len();
+        if n <= self.len() {
+            let (head, tail) = self.split_at(n);
+            if head == prefix {
+                return tail;
+            }
+        }
+        self
+    }
+
+    /// Returns a subslice with the optional suffix removed.
+    ///
+    /// If the slice ends with `suffix`, returns the subslice before the suffix.  If `suffix`
+    /// is empty or the slice does not end with `suffix`, simply returns the original slice.
+    /// If `suffix` is equal to the original slice, returns an empty slice.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(trim_prefix_suffix)]
+    ///
+    /// let v = &[10, 40, 30];
+    ///
+    /// // Suffix present - removes it
+    /// assert_eq!(v.trim_suffix(&[30]), &[10, 40][..]);
+    /// assert_eq!(v.trim_suffix(&[40, 30]), &[10][..]);
+    /// assert_eq!(v.trim_suffix(&[10, 40, 30]), &[][..]);
+    ///
+    /// // Suffix absent - returns original slice
+    /// assert_eq!(v.trim_suffix(&[50]), &[10, 40, 30][..]);
+    /// assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]);
+    /// ```
+    #[must_use = "returns the subslice without modifying the original"]
+    #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+    pub fn trim_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> &[T]
+    where
+        T: PartialEq,
+    {
+        // This function will need rewriting if and when SlicePattern becomes more sophisticated.
+        let suffix = suffix.as_slice();
+        let (len, n) = (self.len(), suffix.len());
+        if n <= len {
+            let (head, tail) = self.split_at(len - n);
+            if tail == suffix {
+                return head;
+            }
+        }
+        self
+    }
+
     /// Binary searches this slice for a given element.
     /// If the slice is not sorted, the returned result is unspecified and
     /// meaningless.
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index 4183479..5051b22 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -2426,6 +2426,83 @@ pub fn strip_suffix<P: Pattern>(&self, suffix: P) -> Option<&str>
         suffix.strip_suffix_of(self)
     }
 
+    /// Returns a string slice with the optional prefix removed.
+    ///
+    /// If the string starts with the pattern `prefix`, returns the substring after the prefix.
+    /// Unlike [`strip_prefix`], this method always returns `&str` for easy method chaining,
+    /// instead of returning [`Option<&str>`].
+    ///
+    /// If the string does not start with `prefix`, returns the original string unchanged.
+    ///
+    /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
+    /// function or closure that determines if a character matches.
+    ///
+    /// [`char`]: prim@char
+    /// [pattern]: self::pattern
+    /// [`strip_prefix`]: Self::strip_prefix
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(trim_prefix_suffix)]
+    ///
+    /// // Prefix present - removes it
+    /// assert_eq!("foo:bar".trim_prefix("foo:"), "bar");
+    /// assert_eq!("foofoo".trim_prefix("foo"), "foo");
+    ///
+    /// // Prefix absent - returns original string
+    /// assert_eq!("foo:bar".trim_prefix("bar"), "foo:bar");
+    ///
+    /// // Method chaining example
+    /// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/");
+    /// ```
+    #[must_use = "this returns the remaining substring as a new slice, \
+                  without modifying the original"]
+    #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+    pub fn trim_prefix<P: Pattern>(&self, prefix: P) -> &str {
+        prefix.strip_prefix_of(self).unwrap_or(self)
+    }
+
+    /// Returns a string slice with the optional suffix removed.
+    ///
+    /// If the string ends with the pattern `suffix`, returns the substring before the suffix.
+    /// Unlike [`strip_suffix`], this method always returns `&str` for easy method chaining,
+    /// instead of returning [`Option<&str>`].
+    ///
+    /// If the string does not end with `suffix`, returns the original string unchanged.
+    ///
+    /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
+    /// function or closure that determines if a character matches.
+    ///
+    /// [`char`]: prim@char
+    /// [pattern]: self::pattern
+    /// [`strip_suffix`]: Self::strip_suffix
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(trim_prefix_suffix)]
+    ///
+    /// // Suffix present - removes it
+    /// assert_eq!("bar:foo".trim_suffix(":foo"), "bar");
+    /// assert_eq!("foofoo".trim_suffix("foo"), "foo");
+    ///
+    /// // Suffix absent - returns original string
+    /// assert_eq!("bar:foo".trim_suffix("bar"), "bar:foo");
+    ///
+    /// // Method chaining example
+    /// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/");
+    /// ```
+    #[must_use = "this returns the remaining substring as a new slice, \
+                  without modifying the original"]
+    #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+    pub fn trim_suffix<P: Pattern>(&self, suffix: P) -> &str
+    where
+        for<'a> P::Searcher<'a>: ReverseSearcher<'a>,
+    {
+        suffix.strip_suffix_of(self).unwrap_or(self)
+    }
+
     /// Returns a string slice with all suffixes that match a pattern
     /// repeatedly removed.
     ///
diff --git a/library/coretests/tests/atomic.rs b/library/coretests/tests/atomic.rs
index e0c0fe4..b1ab443 100644
--- a/library/coretests/tests/atomic.rs
+++ b/library/coretests/tests/atomic.rs
@@ -228,24 +228,20 @@ fn static_init() {
 }
 
 #[test]
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#[allow(static_mut_refs)]
 fn atomic_access_bool() {
-    static mut ATOMIC: AtomicBool = AtomicBool::new(false);
+    let mut atom = AtomicBool::new(false);
 
-    unsafe {
-        assert_eq!(*ATOMIC.get_mut(), false);
-        ATOMIC.store(true, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), true);
-        ATOMIC.fetch_or(false, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), true);
-        ATOMIC.fetch_and(false, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), false);
-        ATOMIC.fetch_nand(true, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), true);
-        ATOMIC.fetch_xor(true, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), false);
-    }
+    assert_eq!(*atom.get_mut(), false);
+    atom.store(true, SeqCst);
+    assert_eq!(*atom.get_mut(), true);
+    atom.fetch_or(false, SeqCst);
+    assert_eq!(*atom.get_mut(), true);
+    atom.fetch_and(false, SeqCst);
+    assert_eq!(*atom.get_mut(), false);
+    atom.fetch_nand(true, SeqCst);
+    assert_eq!(*atom.get_mut(), true);
+    atom.fetch_xor(true, SeqCst);
+    assert_eq!(*atom.get_mut(), false);
 }
 
 #[test]
diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs
index d9060fe..16f116d 100644
--- a/library/coretests/tests/fmt/mod.rs
+++ b/library/coretests/tests/fmt/mod.rs
@@ -3,6 +3,21 @@
 mod num;
 
 #[test]
+fn test_lifetime() {
+    // Trigger all different forms of expansion,
+    // and check that each of them can be stored as a variable.
+    let a = format_args!("hello");
+    let a = format_args!("hello {a}");
+    let a = format_args!("hello {a:1}");
+    let a = format_args!("hello {a} {a:?}");
+    assert_eq!(a.to_string(), "hello hello hello hello hello hello hello");
+
+    // Without arguments, it should also work in consts.
+    const A: std::fmt::Arguments<'static> = format_args!("hello");
+    assert_eq!(A.to_string(), "hello");
+}
+
+#[test]
 fn test_format_flags() {
     // No residual flags left by pointer formatting
     let p = "".as_ptr();
diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs
index 5802b65..78c9572 100644
--- a/library/std/src/os/unix/mod.rs
+++ b/library/std/src/os/unix/mod.rs
@@ -116,6 +116,9 @@ pub mod prelude {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
     #[doc(no_inline)]
+    #[unstable(feature = "unix_send_signal", issue = "141975")]
+    pub use super::process::ChildExt;
+    #[doc(no_inline)]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use super::process::{CommandExt, ExitStatusExt};
     #[doc(no_inline)]
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index 57ce3c5..3f4fe2e 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -378,6 +378,41 @@ fn into_raw(self) -> i32 {
     }
 }
 
+#[unstable(feature = "unix_send_signal", issue = "141975")]
+pub trait ChildExt: Sealed {
+    /// Sends a signal to a child process.
+    ///
+    /// # Errors
+    ///
+    /// This function will return an error if the signal is invalid. The integer values associated
+    /// with signals are implemenation-specific, so it's encouraged to use a crate that provides
+    /// posix bindings.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(unix_send_signal)]
+    ///
+    /// use std::{io, os::unix::process::ChildExt, process::{Command, Stdio}};
+    ///
+    /// use libc::SIGTERM;
+    ///
+    /// fn main() -> io::Result<()> {
+    ///     let child = Command::new("cat").stdin(Stdio::piped()).spawn()?;
+    ///     child.send_signal(SIGTERM)?;
+    ///     Ok(())
+    /// }
+    /// ```
+    fn send_signal(&self, signal: i32) -> io::Result<()>;
+}
+
+#[unstable(feature = "unix_send_signal", issue = "141975")]
+impl ChildExt for process::Child {
+    fn send_signal(&self, signal: i32) -> io::Result<()> {
+        self.handle.send_signal(signal)
+    }
+}
+
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl FromRawFd for process::Stdio {
     #[inline]
diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs
index 2d949ec..47e9a61 100644
--- a/library/std/src/sys/pal/unix/linux/pidfd.rs
+++ b/library/std/src/sys/pal/unix/linux/pidfd.rs
@@ -13,11 +13,15 @@
 
 impl PidFd {
     pub fn kill(&self) -> io::Result<()> {
+        self.send_signal(libc::SIGKILL)
+    }
+
+    pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
         cvt(unsafe {
             libc::syscall(
                 libc::SYS_pidfd_send_signal,
                 self.0.as_raw_fd(),
-                libc::SIGKILL,
+                signal,
                 crate::ptr::null::<()>(),
                 0,
             )
diff --git a/library/std/src/sys/process/unix/fuchsia.rs b/library/std/src/sys/process/unix/fuchsia.rs
index 017ab91..d71be51 100644
--- a/library/std/src/sys/process/unix/fuchsia.rs
+++ b/library/std/src/sys/process/unix/fuchsia.rs
@@ -152,6 +152,11 @@ pub fn kill(&mut self) -> io::Result<()> {
         Ok(())
     }
 
+    pub fn send_signal(&self, _signal: i32) -> io::Result<()> {
+        // Fuchsia doesn't have a direct equivalent for signals
+        unimplemented!()
+    }
+
     pub fn wait(&mut self) -> io::Result<ExitStatus> {
         let mut proc_info: zx_info_process_t = Default::default();
         let mut actual: size_t = 0;
diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs
index 4f595ac..1fe80c1 100644
--- a/library/std/src/sys/process/unix/unix.rs
+++ b/library/std/src/sys/process/unix/unix.rs
@@ -963,9 +963,13 @@ pub fn id(&self) -> u32 {
         self.pid as u32
     }
 
-    pub fn kill(&mut self) -> io::Result<()> {
+    pub fn kill(&self) -> io::Result<()> {
+        self.send_signal(libc::SIGKILL)
+    }
+
+    pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
         // If we've already waited on this process then the pid can be recycled
-        // and used for another process, and we probably shouldn't be killing
+        // and used for another process, and we probably shouldn't be signaling
         // random processes, so return Ok because the process has exited already.
         if self.status.is_some() {
             return Ok(());
@@ -973,9 +977,9 @@ pub fn kill(&mut self) -> io::Result<()> {
         #[cfg(target_os = "linux")]
         if let Some(pid_fd) = self.pidfd.as_ref() {
             // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too
-            return pid_fd.kill();
+            return pid_fd.send_signal(signal);
         }
-        cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+        cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
     }
 
     pub fn wait(&mut self) -> io::Result<ExitStatus> {
diff --git a/library/std/src/sys/process/unix/unsupported.rs b/library/std/src/sys/process/unix/unsupported.rs
index e86561a..87403cd 100644
--- a/library/std/src/sys/process/unix/unsupported.rs
+++ b/library/std/src/sys/process/unix/unsupported.rs
@@ -40,7 +40,11 @@ pub fn id(&self) -> u32 {
         0
     }
 
-    pub fn kill(&mut self) -> io::Result<()> {
+    pub fn kill(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn send_signal(&self, _signal: i32) -> io::Result<()> {
         unsupported()
     }
 
diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs
index f33b4a3..51ae8c5 100644
--- a/library/std/src/sys/process/unix/vxworks.rs
+++ b/library/std/src/sys/process/unix/vxworks.rs
@@ -146,14 +146,18 @@ pub fn id(&self) -> u32 {
         self.pid as u32
     }
 
-    pub fn kill(&mut self) -> io::Result<()> {
+    pub fn kill(&self) -> io::Result<()> {
+        self.send_signal(libc::SIGKILL)
+    }
+
+    pub fn send_signal(&self, signal: i32) -> io::Result<()> {
         // If we've already waited on this process then the pid can be recycled
         // and used for another process, and we probably shouldn't be killing
         // random processes, so return Ok because the process has exited already.
         if self.status.is_some() {
             Ok(())
         } else {
-            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+            cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
         }
     }
 
diff --git a/src/bootstrap/build.rs b/src/bootstrap/build.rs
index e0e32d3..d9810e8 100644
--- a/src/bootstrap/build.rs
+++ b/src/bootstrap/build.rs
@@ -1,6 +1,7 @@
 use std::env;
 
 fn main() {
+    // this is needed because `HOST` is only available to build scripts.
     let host = env::var("HOST").unwrap();
     println!("cargo:rerun-if-changed=build.rs");
     println!("cargo:rustc-env=BUILD_TRIPLE={host}");
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 0088e85..53a5c6b 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -1129,6 +1129,7 @@ macro_rules! tool_extended {
             tool_name: $tool_name:expr,
             stable: $stable:expr
             $( , add_bins_to_sysroot: $add_bins_to_sysroot:expr )?
+            $( , add_features: $add_features:expr )?
             $( , )?
         }
     ) => {
@@ -1168,6 +1169,7 @@ fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
                     $tool_name,
                     $path,
                     None $( .or(Some(&$add_bins_to_sysroot)) )?,
+                    None $( .or(Some($add_features)) )?,
                 )
             }
         }
@@ -1205,7 +1207,13 @@ fn run_tool_build_step(
     tool_name: &'static str,
     path: &'static str,
     add_bins_to_sysroot: Option<&[&str]>,
+    add_features: Option<fn(&Builder<'_>, TargetSelection, &mut Vec<String>)>,
 ) -> ToolBuildResult {
+    let mut extra_features = Vec::new();
+    if let Some(func) = add_features {
+        func(builder, target, &mut extra_features);
+    }
+
     let ToolBuildResult { tool_path, build_compiler, target_compiler } =
         builder.ensure(ToolBuild {
             compiler,
@@ -1213,7 +1221,7 @@ fn run_tool_build_step(
             tool: tool_name,
             mode: Mode::ToolRustc,
             path,
-            extra_features: vec![],
+            extra_features,
             source_type: SourceType::InTree,
             allow_features: "",
             cargo_args: vec![],
@@ -1256,7 +1264,12 @@ fn run_tool_build_step(
     path: "src/tools/clippy",
     tool_name: "clippy-driver",
     stable: true,
-    add_bins_to_sysroot: ["clippy-driver"]
+    add_bins_to_sysroot: ["clippy-driver"],
+    add_features: |builder, target, features| {
+        if builder.config.jemalloc(target) {
+            features.push("jemalloc".to_string());
+        }
+    }
 });
 tool_extended!(Miri {
     path: "src/tools/miri",
diff --git a/src/doc/book b/src/doc/book
index 4433c9f..8a6d44e 160000
--- a/src/doc/book
+++ b/src/doc/book
@@ -1 +1 @@
-Subproject commit 4433c9f0cad8460bee05ede040587f8a1fa3f1de
+Subproject commit 8a6d44e45b7b564eeb6bae30507e1fbac439d72d
diff --git a/src/doc/reference b/src/doc/reference
index d4c66b3..50fc162 160000
--- a/src/doc/reference
+++ b/src/doc/reference
@@ -1 +1 @@
-Subproject commit d4c66b346f4b72d29e70390a3fa3ea7d4e064db1
+Subproject commit 50fc1628f36563958399123829c73755fa7a8421
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
index 9baa9e8..05c7d8b 160000
--- a/src/doc/rust-by-example
+++ b/src/doc/rust-by-example
@@ -1 +1 @@
-Subproject commit 9baa9e863116cb9524a177d5a5c475baac18928a
+Subproject commit 05c7d8bae65f23a1837430c5a19be129d414f5ec
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 204f8de..cf3c4ac 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -149,15 +149,12 @@ pub(crate) fn new_dcx(
     diagnostic_width: Option<usize>,
     unstable_opts: &UnstableOptions,
 ) -> rustc_errors::DiagCtxt {
-    let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-        rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-        false,
-    );
+    let translator = rustc_driver::default_translator();
     let emitter: Box<DynEmitter> = match error_format {
         ErrorOutputType::HumanReadable { kind, color_config } => {
             let short = kind.short();
             Box::new(
-                HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
+                HumanEmitter::new(stderr_destination(color_config), translator)
                     .sm(source_map.map(|sm| sm as _))
                     .short_message(short)
                     .diagnostic_width(diagnostic_width)
@@ -178,7 +175,7 @@ pub(crate) fn new_dcx(
                 JsonEmitter::new(
                     Box::new(io::BufWriter::new(io::stderr())),
                     Some(source_map),
-                    fallback_bundle,
+                    translator,
                     pretty,
                     json_rendered,
                     color_config,
@@ -387,8 +384,6 @@ pub(crate) fn run_global_ctxt(
         ctxt.external_traits.insert(sized_trait_did, sized_trait);
     }
 
-    debug!("crate: {:?}", tcx.hir_crate(()));
-
     let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt));
 
     if krate.module.doc_value().is_empty() {
diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs
index 3ff6828..f229f77 100644
--- a/src/librustdoc/doctest/make.rs
+++ b/src/librustdoc/doctest/make.rs
@@ -456,16 +456,13 @@ fn parse_source(
     let filename = FileName::anon_source_code(&wrapped_source);
 
     let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
-    let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-        rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-        false,
-    );
+    let translator = rustc_driver::default_translator();
     info.supports_color =
-        HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
+        HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator.clone())
             .supports_color();
     // Any errors in parsing should also appear when the doctest is compiled for real, so just
     // send all the errors that the parser emits directly into a `Sink` instead of stderr.
-    let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
+    let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
 
     // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
     let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index b611a3e..a2c4870 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -5394,43 +5394,6 @@
     search(true);
 }
 
-// @ts-expect-error
-function initSearch(searchIndx) {
-    rawSearchIndex = searchIndx;
-    if (typeof window !== "undefined") {
-        // @ts-expect-error
-        docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
-        registerSearchEvents();
-        // If there's a search term in the URL, execute the search now.
-        if (window.searchState.getQueryStringParams().search) {
-            search();
-        }
-    } else if (typeof exports !== "undefined") {
-        // @ts-expect-error
-        docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
-        exports.docSearch = docSearch;
-        exports.parseQuery = DocSearch.parseQuery;
-    }
-}
-
-if (typeof exports !== "undefined") {
-    exports.initSearch = initSearch;
-}
-
-if (typeof window !== "undefined") {
-    // @ts-expect-error
-    window.initSearch = initSearch;
-    // @ts-expect-error
-    if (window.searchIndex !== undefined) {
-        // @ts-expect-error
-        initSearch(window.searchIndex);
-    }
-} else {
-    // Running in Node, not a browser. Run initSearch just to produce the
-    // exports.
-    initSearch(new Map());
-}
-
 // Parts of this code are based on Lucene, which is licensed under the
 // Apache/2.0 license.
 // More information found here:
@@ -5909,3 +5872,44 @@
 Lev1TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new Int32Array([
     0xa0fc0000,0x5555ba08,0x55555555,
 ]);
+
+// ====================
+// WARNING: Nothing should be added below this comment: we need the `initSearch` function to
+// be called ONLY when the whole file has been parsed and loaded.
+
+// @ts-expect-error
+function initSearch(searchIndx) {
+    rawSearchIndex = searchIndx;
+    if (typeof window !== "undefined") {
+        // @ts-expect-error
+        docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
+        registerSearchEvents();
+        // If there's a search term in the URL, execute the search now.
+        if (window.searchState.getQueryStringParams().search) {
+            search();
+        }
+    } else if (typeof exports !== "undefined") {
+        // @ts-expect-error
+        docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
+        exports.docSearch = docSearch;
+        exports.parseQuery = DocSearch.parseQuery;
+    }
+}
+
+if (typeof exports !== "undefined") {
+    exports.initSearch = initSearch;
+}
+
+if (typeof window !== "undefined") {
+    // @ts-expect-error
+    window.initSearch = initSearch;
+    // @ts-expect-error
+    if (window.searchIndex !== undefined) {
+        // @ts-expect-error
+        initSearch(window.searchIndex);
+    }
+} else {
+    // Running in Node, not a browser. Run initSearch just to produce the
+    // exports.
+    initSearch(new Map());
+}
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 0f62c8b..61493c1 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -18,7 +18,6 @@
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
-use rustc_session::features::StabilityExt;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustdoc_json_types as types;
 // It's important to use the FxHashMap from rustdoc_json_types here, instead of
@@ -148,7 +147,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
             .copied()
             .filter(|(_, stability, _)| {
                 // Describe only target features which the user can toggle
-                stability.is_toggle_permitted(sess).is_ok()
+                stability.toggle_allowed().is_ok()
             })
             .map(|(name, stability, implied_features)| {
                 types::TargetFeature {
@@ -164,7 +163,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
                             // Imply only target features which the user can toggle
                             feature_stability
                                 .get(name)
-                                .map(|stability| stability.is_toggle_permitted(sess).is_ok())
+                                .map(|stability| stability.toggle_allowed().is_ok())
                                 .unwrap_or(false)
                         })
                         .map(String::from)
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 9662dd8..91cddbe 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -6,8 +6,8 @@
 use rustc_data_structures::sync::Lock;
 use rustc_errors::emitter::Emitter;
 use rustc_errors::registry::Registry;
-use rustc_errors::translation::{Translate, to_fluent_args};
-use rustc_errors::{Applicability, DiagCtxt, DiagInner, LazyFallbackBundle};
+use rustc_errors::translation::{Translator, to_fluent_args};
+use rustc_errors::{Applicability, DiagCtxt, DiagInner};
 use rustc_parse::{source_str_to_stream, unwrap_or_emit_fatal};
 use rustc_resolve::rustdoc::source_span_for_markdown_range;
 use rustc_session::parse::ParseSess;
@@ -36,11 +36,8 @@ fn check_rust_syntax(
     code_block: RustCodeBlock,
 ) {
     let buffer = Arc::new(Lock::new(Buffer::default()));
-    let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-        rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-        false,
-    );
-    let emitter = BufferEmitter { buffer: Arc::clone(&buffer), fallback_bundle };
+    let translator = rustc_driver::default_translator();
+    let emitter = BufferEmitter { buffer: Arc::clone(&buffer), translator };
 
     let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
     let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
@@ -149,17 +146,7 @@ struct Buffer {
 
 struct BufferEmitter {
     buffer: Arc<Lock<Buffer>>,
-    fallback_bundle: LazyFallbackBundle,
-}
-
-impl Translate for BufferEmitter {
-    fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
-        None
-    }
-
-    fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
-        &self.fallback_bundle
-    }
+    translator: Translator,
 }
 
 impl Emitter for BufferEmitter {
@@ -168,6 +155,7 @@ fn emit_diagnostic(&mut self, diag: DiagInner, _registry: &Registry) {
 
         let fluent_args = to_fluent_args(diag.args.iter());
         let translated_main_message = self
+            .translator
             .translate_message(&diag.messages[0].0, &fluent_args)
             .unwrap_or_else(|e| panic!("{e}"));
 
@@ -180,4 +168,8 @@ fn emit_diagnostic(&mut self, diag: DiagInner, _registry: &Registry) {
     fn source_map(&self) -> Option<&SourceMap> {
         None
     }
+
+    fn translator(&self) -> &Translator {
+        &self.translator
+    }
 }
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 1f93895..4d25124 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -37,8 +37,8 @@
 // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line
 // are deliberately not in a doc comment, because they need not be in public docs.)
 //
-// Latest feature: Pretty printing of inline attributes changed
-pub const FORMAT_VERSION: u32 = 48;
+// Latest feature: Pretty printing of cold attributes changed
+pub const FORMAT_VERSION: u32 = 50;
 
 /// The root of the emitted JSON blob.
 ///
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 3a76c61..13cf82a 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -58,6 +58,7 @@
 [features]
 integration = ["dep:tempfile"]
 internal = ["dep:clippy_lints_internal", "dep:tempfile"]
+jemalloc = []
 
 [package.metadata.rust-analyzer]
 # This package uses #[feature(rustc_private)]
diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
index ec45380..7ba11c2 100644
--- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
@@ -42,9 +42,8 @@ fn check_code_sample(code: String, edition: Edition, ignore: bool) -> (bool, Vec
                 let mut test_attr_spans = vec![];
                 let filename = FileName::anon_source_code(&code);
 
-                let fallback_bundle =
-                    rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
-                let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
+                let translator = rustc_driver::default_translator();
+                let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
                 let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
                 #[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx
                 let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 37adb14..426ba87 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -13,6 +13,11 @@
 extern crate rustc_session;
 extern crate rustc_span;
 
+// See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
+// about jemalloc.
+#[cfg(feature = "jemalloc")]
+extern crate tikv_jemalloc_sys as jemalloc_sys;
+
 use clippy_utils::sym;
 use rustc_interface::interface;
 use rustc_session::EarlyDiagCtxt;
@@ -181,6 +186,36 @@ fn display_help() {
 #[allow(clippy::too_many_lines)]
 #[allow(clippy::ignored_unit_patterns)]
 pub fn main() {
+    // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
+    // about jemalloc.
+    #[cfg(feature = "jemalloc")]
+    {
+        use std::os::raw::{c_int, c_void};
+
+        #[used]
+        static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc;
+        #[used]
+        static _F2: unsafe extern "C" fn(*mut *mut c_void, usize, usize) -> c_int = jemalloc_sys::posix_memalign;
+        #[used]
+        static _F3: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::aligned_alloc;
+        #[used]
+        static _F4: unsafe extern "C" fn(usize) -> *mut c_void = jemalloc_sys::malloc;
+        #[used]
+        static _F5: unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void = jemalloc_sys::realloc;
+        #[used]
+        static _F6: unsafe extern "C" fn(*mut c_void) = jemalloc_sys::free;
+
+        #[cfg(target_os = "macos")]
+        {
+            unsafe extern "C" {
+                fn _rjem_je_zone_register();
+            }
+
+            #[used]
+            static _F7: unsafe extern "C" fn() = _rjem_je_zone_register;
+        }
+    }
+
     let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
 
     rustc_driver::init_rustc_env_logger(&early_dcx);
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
index 5f8a4ce..49595e2 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
@@ -9,28 +9,35 @@
     && let ExprKind::Call(func, args) = e.kind
     && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
     && args.len() == 1
-    && let ExprKind::Call(func1, args1) = args[0].kind
-    && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
-    && args1.len() == 2
+    && let ExprKind::Block(block1, None) = args[0].kind
+    && block1.stmts.len() == 1
+    && let StmtKind::Let(local1) = block1.stmts[0].kind
+    && let Some(init1) = local1.init
+    && let ExprKind::Array(elements) = init1.kind
+    && elements.len() == 1
+    && let ExprKind::Call(func1, args1) = elements[0].kind
+    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
+    && args1.len() == 1
     && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
-    && let ExprKind::Array(elements) = inner.kind
-    && elements.len() == 2
-    && let ExprKind::Lit(ref lit) = elements[0].kind
+    && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind
+    && name.as_str() == "args"
+    && let Some(trailing_expr) = block1.expr
+    && let ExprKind::Call(func2, args2) = trailing_expr.kind
+    && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
+    && args2.len() == 2
+    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
+    && let ExprKind::Array(elements1) = inner1.kind
+    && elements1.len() == 2
+    && let ExprKind::Lit(ref lit) = elements1[0].kind
     && let LitKind::Str(s, _) = lit.node
     && s.as_str() == ""
-    && let ExprKind::Lit(ref lit1) = elements[1].kind
+    && let ExprKind::Lit(ref lit1) = elements1[1].kind
     && let LitKind::Str(s1, _) = lit1.node
     && s1.as_str() == "\n"
-    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
-    && let ExprKind::Array(elements1) = inner1.kind
-    && elements1.len() == 1
-    && let ExprKind::Call(func2, args2) = elements1[0].kind
-    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
-    && args2.len() == 1
-    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
     && block.expr.is_none()
-    && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
-    && name.as_str() == "print_text"
+    && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
+    && name1.as_str() == "print_text"
 {
     // report your lint here
 }
diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
index ecc2525..4fc7b49 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
@@ -19,25 +19,32 @@
     && let ExprKind::Call(func, args) = e1.kind
     && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
     && args.len() == 1
-    && let ExprKind::Call(func1, args1) = args[0].kind
-    && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
-    && args1.len() == 2
+    && let ExprKind::Block(block2, None) = args[0].kind
+    && block2.stmts.len() == 1
+    && let StmtKind::Let(local) = block2.stmts[0].kind
+    && let Some(init) = local.init
+    && let ExprKind::Array(elements) = init.kind
+    && elements.len() == 1
+    && let ExprKind::Call(func1, args1) = elements[0].kind
+    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
+    && args1.len() == 1
     && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
-    && let ExprKind::Array(elements) = inner.kind
-    && elements.len() == 2
-    && let ExprKind::Lit(ref lit2) = elements[0].kind
+    && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
+    && name1.as_str() == "args"
+    && let Some(trailing_expr) = block2.expr
+    && let ExprKind::Call(func2, args2) = trailing_expr.kind
+    && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
+    && args2.len() == 2
+    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
+    && let ExprKind::Array(elements1) = inner1.kind
+    && elements1.len() == 2
+    && let ExprKind::Lit(ref lit2) = elements1[0].kind
     && let LitKind::Str(s, _) = lit2.node
     && s.as_str() == ""
-    && let ExprKind::Lit(ref lit3) = elements[1].kind
+    && let ExprKind::Lit(ref lit3) = elements1[1].kind
     && let LitKind::Str(s1, _) = lit3.node
     && s1.as_str() == "\n"
-    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
-    && let ExprKind::Array(elements1) = inner1.kind
-    && elements1.len() == 1
-    && let ExprKind::Call(func2, args2) = elements1[0].kind
-    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
-    && args2.len() == 1
-    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
     && block1.expr.is_none()
     && block.expr.is_none()
 {
diff --git a/src/tools/clippy/tests/ui/manual_inspect.fixed b/src/tools/clippy/tests/ui/manual_inspect.fixed
index 9b768db..00a1915 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.fixed
+++ b/src/tools/clippy/tests/ui/manual_inspect.fixed
@@ -154,7 +154,6 @@
     });
 
     let _ = [0]
-        //~^ suspicious_map
         .into_iter()
         .inspect(|&x| {
             //~^ manual_inspect
diff --git a/src/tools/clippy/tests/ui/manual_inspect.rs b/src/tools/clippy/tests/ui/manual_inspect.rs
index e679636..b3b1713 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.rs
+++ b/src/tools/clippy/tests/ui/manual_inspect.rs
@@ -165,7 +165,6 @@ macro_rules! maybe_ret {
     });
 
     let _ = [0]
-        //~^ suspicious_map
         .into_iter()
         .map(|x| {
             //~^ manual_inspect
diff --git a/src/tools/clippy/tests/ui/manual_inspect.stderr b/src/tools/clippy/tests/ui/manual_inspect.stderr
index 78b085f..70c00c1 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.stderr
+++ b/src/tools/clippy/tests/ui/manual_inspect.stderr
@@ -157,25 +157,8 @@
 LL ~         println!("{}", x);
    |
 
-error: this call to `map()` won't have an effect on the call to `count()`
-  --> tests/ui/manual_inspect.rs:167:13
-   |
-LL |       let _ = [0]
-   |  _____________^
-LL | |
-LL | |         .into_iter()
-LL | |         .map(|x| {
-...  |
-LL | |         })
-LL | |         .count();
-   | |________________^
-   |
-   = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect`
-   = note: `-D clippy::suspicious-map` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]`
-
 error: using `map` over `inspect`
-  --> tests/ui/manual_inspect.rs:170:10
+  --> tests/ui/manual_inspect.rs:169:10
    |
 LL |         .map(|x| {
    |          ^^^
@@ -188,7 +171,7 @@
    |
 
 error: using `map` over `inspect`
-  --> tests/ui/manual_inspect.rs:203:30
+  --> tests/ui/manual_inspect.rs:202:30
    |
 LL |     if let Some(x) = Some(1).map(|x| { println!("{x}");
    |                              ^^^
@@ -200,5 +183,5 @@
 LL ~          }) {
    |
 
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index afd847f..10e2809 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -5,7 +5,7 @@
 use rustc_data_structures::sync::IntoDynSyncSend;
 use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
 use rustc_errors::registry::Registry;
-use rustc_errors::translation::Translate;
+use rustc_errors::translation::Translator;
 use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
 use rustc_session::parse::ParseSess as RawParseSess;
 use rustc_span::{
@@ -47,16 +47,6 @@ fn handle_non_ignoreable_error(&mut self, diag: DiagInner, registry: &Registry)
     }
 }
 
-impl Translate for SilentOnIgnoredFilesEmitter {
-    fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
-        self.emitter.fluent_bundle()
-    }
-
-    fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
-        self.emitter.fallback_fluent_bundle()
-    }
-}
-
 impl Emitter for SilentOnIgnoredFilesEmitter {
     fn source_map(&self) -> Option<&SourceMap> {
         None
@@ -84,6 +74,10 @@ fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry) {
         }
         self.handle_non_ignoreable_error(diag, registry);
     }
+
+    fn translator(&self) -> &Translator {
+        self.emitter.translator()
+    }
 }
 
 impl From<Color> for ColorConfig {
@@ -110,23 +104,15 @@ fn default_dcx(
         ColorConfig::Never
     };
 
-    let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-        rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-        false,
-    );
-    let emitter = Box::new(
-        HumanEmitter::new(stderr_destination(emit_color), fallback_bundle)
-            .sm(Some(source_map.clone())),
-    );
+    let translator = rustc_driver::default_translator();
 
-    let emitter: Box<DynEmitter> = if !show_parse_errors {
-        Box::new(SilentEmitter {
-            fatal_emitter: emitter,
-            fatal_note: None,
-            emit_fatal_diagnostic: false,
-        })
+    let emitter: Box<DynEmitter> = if show_parse_errors {
+        Box::new(
+            HumanEmitter::new(stderr_destination(emit_color), translator)
+                .sm(Some(source_map.clone())),
+        )
     } else {
-        emitter
+        Box::new(SilentEmitter { translator })
     };
     DiagCtxt::new(Box::new(SilentOnIgnoredFilesEmitter {
         has_non_ignorable_parser_errors: false,
@@ -205,7 +191,7 @@ pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
     }
 
     pub(crate) fn set_silent_emitter(&mut self) {
-        self.raw_psess.dcx().make_silent(None, false);
+        self.raw_psess.dcx().make_silent();
     }
 
     pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
@@ -335,16 +321,6 @@ struct TestEmitter {
             num_emitted_errors: Arc<AtomicU32>,
         }
 
-        impl Translate for TestEmitter {
-            fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
-                None
-            }
-
-            fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
-                panic!("test emitter attempted to translate a diagnostic");
-            }
-        }
-
         impl Emitter for TestEmitter {
             fn source_map(&self) -> Option<&SourceMap> {
                 None
@@ -353,6 +329,10 @@ fn source_map(&self) -> Option<&SourceMap> {
             fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {
                 self.num_emitted_errors.fetch_add(1, Ordering::Release);
             }
+
+            fn translator(&self) -> &Translator {
+                panic!("test emitter attempted to translate a diagnostic");
+            }
         }
 
         fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> DiagInner {
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 045f2f06..b3517b2 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -305,7 +305,6 @@
 ui/borrowck/issue-10876.rs
 ui/borrowck/issue-109271-pass-self-into-closure.rs
 ui/borrowck/issue-111554.rs
-ui/borrowck/issue-114374-invalid-help-fmt-args.rs
 ui/borrowck/issue-11493.rs
 ui/borrowck/issue-115259-suggest-iter-mut.rs
 ui/borrowck/issue-119915-bad-clone-suggestion.rs
diff --git a/tests/assembly/s390x-backchain-toggle.rs b/tests/assembly/s390x-backchain-toggle.rs
index 83c7b82..9bae15b 100644
--- a/tests/assembly/s390x-backchain-toggle.rs
+++ b/tests/assembly/s390x-backchain-toggle.rs
@@ -1,5 +1,5 @@
 //@ add-core-stubs
-//@ revisions: enable-backchain disable-backchain
+//@ revisions: enable-backchain disable-backchain default-backchain
 //@ assembly-output: emit-asm
 //@ compile-flags: -Copt-level=3 --crate-type=lib --target=s390x-unknown-linux-gnu
 //@ needs-llvm-components: systemz
@@ -26,6 +26,8 @@ extern "C" fn test_backchain() -> i32 {
     // enable-backchain: stg [[REG1]], 0(%r15)
     // disable-backchain: aghi %r15, -160
     // disable-backchain-NOT: stg %r{{.*}}, 0(%r15)
+    // default-backchain: aghi %r15, -160
+    // default-backchain-NOT: stg %r{{.*}}, 0(%r15)
     unsafe {
         extern_func();
     }
@@ -35,6 +37,7 @@ extern "C" fn test_backchain() -> i32 {
     // Make sure that the expected return value is written into %r2 (return register):
     // enable-backchain-NEXT: lghi %r2, 1
     // disable-backchain: lghi %r2, 0
+    // default-backchain: lghi %r2, 0
     #[cfg(target_feature = "backchain")]
     {
         1
diff --git a/tests/codegen/target-feature-negative-implication.rs b/tests/codegen/target-feature-negative-implication.rs
new file mode 100644
index 0000000..36cd82d
--- /dev/null
+++ b/tests/codegen/target-feature-negative-implication.rs
@@ -0,0 +1,20 @@
+//@ add-core-stubs
+//@ needs-llvm-components: x86
+//@ compile-flags: --target=x86_64-unknown-linux-gnu
+//@ compile-flags: -Ctarget-feature=-avx2
+
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+#[no_mangle]
+pub unsafe fn banana() {
+    // CHECK-LABEL: @banana()
+    // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] {
+}
+
+// CHECK: attributes [[BANANAATTRS]]
+// CHECK-SAME: -avx512
diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs
index 0fc1e01..eb19b0d 100644
--- a/tests/codegen/target-feature-overrides.rs
+++ b/tests/codegen/target-feature-overrides.rs
@@ -1,3 +1,4 @@
+// ignore-tidy-linelength
 //@ add-core-stubs
 //@ revisions: COMPAT INCOMPAT
 //@ needs-llvm-components: x86
@@ -39,7 +40,7 @@ pub unsafe fn banana() -> u32 {
 
 // CHECK: attributes [[APPLEATTRS]]
 // COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}"
-// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}"
+// INCOMPAT-SAME: "target-features"="{{(-[^,]+,)*}}-avx2{{(,-[^,]+)*}},-avx{{(,-[^,]+)*}},+avx{{(,\+[^,]+)*}}"
 // CHECK: attributes [[BANANAATTRS]]
 // COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}"
-// INCOMPAT-SAME: "target-features"="-avx2,-avx"
+// INCOMPAT-SAME: "target-features"="{{(-[^,]+,)*}}-avx2{{(,-[^,]+)*}},-avx{{(,-[^,]+)*}}"
diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs
index 6be0e21..81499c0 100644
--- a/tests/codegen/tied-features-strength.rs
+++ b/tests/codegen/tied-features-strength.rs
@@ -4,14 +4,23 @@
 //@ compile-flags: --crate-type=rlib --target=aarch64-unknown-linux-gnu
 //@ needs-llvm-components: aarch64
 
+// Rust made SVE require neon.
 //@ [ENABLE_SVE] compile-flags: -C target-feature=+sve -Copt-level=0
-// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" }
+// ENABLE_SVE: attributes #0
+// ENABLE_SVE-SAME: +neon
+// ENABLE_SVE-SAME: +sve
 
+// However, disabling SVE does not disable neon.
 //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0
-// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(-sve,?)|(\+neon,?))*}}" }
+// DISABLE_SVE: attributes #0
+// DISABLE_SVE-NOT: -neon
+// DISABLE_SVE-SAME: -sve
 
+// OTOH, neon fn `fp-armv8` are fully tied; toggling neon must toggle `fp-armv8` the same way.
 //@ [DISABLE_NEON] compile-flags: -C target-feature=-neon -Copt-level=0
-// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(-fp-armv8,?)|(-neon,?))*}}" }
+// DISABLE_NEON: attributes #0
+// DISABLE_NEON-SAME: -neon
+// DISABLE_NEON-SAME: -fp-armv8
 
 //@ [ENABLE_NEON] compile-flags: -C target-feature=+neon -Copt-level=0
 // ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fp-armv8,?)|(\+neon,?))*}}" }
diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
index 33f1ad9..e2d3c6c 100644
--- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
@@ -11,28 +11,29 @@
       let _9: ();
       let _10: ();
       let mut _11: std::fmt::Arguments<'_>;
-      let mut _12: &[&str; 3];
-      let _13: &[&str; 3];
-      let _14: [&str; 3];
-      let mut _15: &[core::fmt::rt::Argument<'_>; 2];
-      let _16: &[core::fmt::rt::Argument<'_>; 2];
-      let _17: [core::fmt::rt::Argument<'_>; 2];
+      let mut _13: &std::boxed::Box<dyn std::fmt::Display>;
+      let mut _14: &u32;
+      let mut _16: core::fmt::rt::Argument<'_>;
+      let mut _17: &std::boxed::Box<dyn std::fmt::Display>;
       let mut _18: core::fmt::rt::Argument<'_>;
-      let mut _19: &std::boxed::Box<dyn std::fmt::Display>;
-      let _20: &std::boxed::Box<dyn std::fmt::Display>;
-      let mut _21: core::fmt::rt::Argument<'_>;
-      let mut _22: &u32;
-      let _23: &u32;
-      let mut _25: bool;
-      let mut _26: isize;
-      let mut _27: isize;
-      let mut _28: isize;
-+     let _29: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
-+     let _30: u32;
+      let mut _19: &u32;
+      let mut _20: &[&str; 3];
+      let _21: &[&str; 3];
+      let _22: [&str; 3];
+      let mut _23: &[core::fmt::rt::Argument<'_>; 2];
+      let _24: &[core::fmt::rt::Argument<'_>; 2];
+      let mut _26: &std::boxed::Box<dyn std::fmt::Display>;
+      let mut _27: &u32;
+      let mut _28: bool;
+      let mut _29: isize;
+      let mut _30: isize;
+      let mut _31: isize;
++     let _32: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
++     let _33: u32;
       scope 1 {
 -         debug foo => _1;
-+         debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _29;
-+         debug ((foo: Foo<T>).1: u32) => _30;
++         debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _32;
++         debug ((foo: Foo<T>).1: u32) => _33;
           let _5: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
           scope 2 {
               debug x => _5;
@@ -42,17 +43,29 @@
                   scope 4 {
                       debug x => _8;
                       let _8: std::boxed::Box<dyn std::fmt::Display>;
-                      let mut _24: &[&str; 3];
+                      let _12: (&std::boxed::Box<dyn std::fmt::Display>, &u32);
++                     let _34: &std::boxed::Box<dyn std::fmt::Display>;
++                     let _35: &u32;
+                      scope 5 {
+-                         debug args => _12;
++                         debug ((args: (&Box<dyn std::fmt::Display>, &u32)).0: &std::boxed::Box<dyn std::fmt::Display>) => _34;
++                         debug ((args: (&Box<dyn std::fmt::Display>, &u32)).1: &u32) => _35;
+                          let _15: [core::fmt::rt::Argument<'_>; 2];
+                          scope 6 {
+                              debug args => _15;
+                              let mut _25: &[&str; 3];
+                          }
+                      }
                   }
               }
           }
       }
   
       bb0: {
-          _25 = const false;
+          _28 = const false;
 -         StorageLive(_1);
-+         StorageLive(_29);
-+         StorageLive(_30);
++         StorageLive(_32);
++         StorageLive(_33);
 +         nop;
           StorageLive(_2);
           StorageLive(_3);
@@ -66,77 +79,93 @@
           _2 = Result::<Box<dyn std::fmt::Display>, <T as Err>::Err>::Ok(move _3);
           StorageDead(_3);
 -         _1 = Foo::<T> { x: move _2, y: const 7_u32 };
-+         _29 = move _2;
-+         _30 = const 7_u32;
++         _32 = move _2;
++         _33 = const 7_u32;
 +         nop;
           StorageDead(_2);
           StorageLive(_5);
-          _25 = const true;
+          _28 = const true;
 -         _5 = move (_1.0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>);
-+         _5 = move _29;
++         _5 = move _32;
           StorageLive(_6);
 -         _6 = copy (_1.1: u32);
-+         _6 = copy _30;
++         _6 = copy _33;
           _7 = discriminant(_5);
           switchInt(move _7) -> [0: bb2, otherwise: bb7];
       }
   
       bb2: {
           StorageLive(_8);
-          _25 = const false;
+          _28 = const false;
           _8 = move ((_5 as Ok).0: std::boxed::Box<dyn std::fmt::Display>);
           StorageLive(_9);
           StorageLive(_10);
           StorageLive(_11);
-          StorageLive(_12);
+-         StorageLive(_12);
++         StorageLive(_34);
++         StorageLive(_35);
++         nop;
           StorageLive(_13);
-          _24 = const foo::<T>::promoted[0];
-          _13 = &(*_24);
-          _12 = &(*_13);
+          _13 = &_8;
+          StorageLive(_14);
+          _14 = &_6;
+-         _12 = (move _13, move _14);
++         _34 = move _13;
++         _35 = move _14;
++         nop;
+          StorageDead(_14);
+          StorageDead(_13);
           StorageLive(_15);
           StorageLive(_16);
           StorageLive(_17);
-          StorageLive(_18);
-          StorageLive(_19);
-          StorageLive(_20);
-          _20 = &_8;
-          _19 = &(*_20);
-          _18 = core::fmt::rt::Argument::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _19) -> [return: bb3, unwind unreachable];
+-         _26 = deref_copy (_12.0: &std::boxed::Box<dyn std::fmt::Display>);
++         _26 = deref_copy _34;
+          _17 = &(*_26);
+          _16 = core::fmt::rt::Argument::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _17) -> [return: bb3, unwind unreachable];
       }
   
       bb3: {
-          StorageDead(_19);
-          StorageLive(_21);
-          StorageLive(_22);
-          StorageLive(_23);
-          _23 = &_6;
-          _22 = &(*_23);
-          _21 = core::fmt::rt::Argument::<'_>::new_display::<u32>(move _22) -> [return: bb4, unwind unreachable];
+          StorageDead(_17);
+          StorageLive(_18);
+          StorageLive(_19);
+-         _27 = deref_copy (_12.1: &u32);
++         _27 = deref_copy _35;
+          _19 = &(*_27);
+          _18 = core::fmt::rt::Argument::<'_>::new_display::<u32>(move _19) -> [return: bb4, unwind unreachable];
       }
   
       bb4: {
-          StorageDead(_22);
-          _17 = [move _18, move _21];
-          StorageDead(_21);
+          StorageDead(_19);
+          _15 = [move _16, move _18];
           StorageDead(_18);
-          _16 = &_17;
-          _15 = &(*_16);
-          _11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _12, move _15) -> [return: bb5, unwind unreachable];
+          StorageDead(_16);
+          StorageLive(_20);
+          StorageLive(_21);
+          _25 = const foo::<T>::promoted[0];
+          _21 = &(*_25);
+          _20 = &(*_21);
+          StorageLive(_23);
+          StorageLive(_24);
+          _24 = &_15;
+          _23 = &(*_24);
+          _11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _20, move _23) -> [return: bb5, unwind unreachable];
       }
   
       bb5: {
-          StorageDead(_15);
-          StorageDead(_12);
+          StorageDead(_24);
+          StorageDead(_23);
+          StorageDead(_21);
+          StorageDead(_20);
           _10 = _eprint(move _11) -> [return: bb6, unwind unreachable];
       }
   
       bb6: {
           StorageDead(_11);
-          StorageDead(_23);
-          StorageDead(_20);
-          StorageDead(_17);
-          StorageDead(_16);
-          StorageDead(_13);
+          StorageDead(_15);
+-         StorageDead(_12);
++         StorageDead(_34);
++         StorageDead(_35);
++         nop;
           StorageDead(_10);
           _9 = const ();
           StorageDead(_9);
@@ -156,22 +185,22 @@
   
       bb9: {
           StorageDead(_6);
-          _26 = discriminant(_5);
-          switchInt(move _26) -> [0: bb11, otherwise: bb13];
+          _29 = discriminant(_5);
+          switchInt(move _29) -> [0: bb11, otherwise: bb13];
       }
   
       bb10: {
-          _25 = const false;
+          _28 = const false;
           StorageDead(_5);
 -         StorageDead(_1);
-+         StorageDead(_29);
-+         StorageDead(_30);
++         StorageDead(_32);
++         StorageDead(_33);
 +         nop;
           return;
       }
   
       bb11: {
-          switchInt(copy _25) -> [0: bb10, otherwise: bb12];
+          switchInt(copy _28) -> [0: bb10, otherwise: bb12];
       }
   
       bb12: {
diff --git a/tests/ui/attributes/expected-word.rs b/tests/ui/attributes/expected-word.rs
new file mode 100644
index 0000000..246aa78
--- /dev/null
+++ b/tests/ui/attributes/expected-word.rs
@@ -0,0 +1,3 @@
+#[cold = true]
+//~^ ERROR malformed `cold` attribute input [E0565]
+fn main() {}
diff --git a/tests/ui/attributes/expected-word.stderr b/tests/ui/attributes/expected-word.stderr
new file mode 100644
index 0000000..dcb10e7
--- /dev/null
+++ b/tests/ui/attributes/expected-word.stderr
@@ -0,0 +1,12 @@
+error[E0565]: malformed `cold` attribute input
+  --> $DIR/expected-word.rs:1:1
+   |
+LL | #[cold = true]
+   | ^^^^^^^------^
+   | |      |
+   | |      didn't expect any arguments here
+   | help: must be of the form: `#[cold]`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0565`.
diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr
index 911c136..72580e7 100644
--- a/tests/ui/borrowck/clone-on-ref.stderr
+++ b/tests/ui/borrowck/clone-on-ref.stderr
@@ -30,7 +30,7 @@
    |          ^ move out of `x` occurs here
 LL |
 LL |     println!("{b}");
-   |               --- borrow later used here
+   |                - borrow later used here
    |
 help: if `T` implemented `Clone`, you could clone the value
   --> $DIR/clone-on-ref.rs:11:8
@@ -57,7 +57,7 @@
    |          ^ move out of `x` occurs here
 LL |
 LL |     println!("{b:?}");
-   |               ----- borrow later used here
+   |                - borrow later used here
    |
 note: if `A` implemented `Clone`, you could clone the value
   --> $DIR/clone-on-ref.rs:19:1
diff --git a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs
deleted file mode 100644
index 4a6c2f9..0000000
--- a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#![allow(dead_code)]
-
-fn bar<'a>(_: std::fmt::Arguments<'a>) {}
-fn main() {
-    let x = format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3);
-    //~^ ERROR temporary value dropped while borrowed
-
-    bar(x);
-
-    let foo = format_args!("{}", "hi");
-    //~^ ERROR temporary value dropped while borrowed
-    bar(foo);
-
-    let foo = format_args!("hi"); // no placeholder in arguments, so no error
-    bar(foo);
-}
diff --git a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr
deleted file mode 100644
index 30f292f..0000000
--- a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr
+++ /dev/null
@@ -1,31 +0,0 @@
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/issue-114374-invalid-help-fmt-args.rs:5:13
-   |
-LL |     let x = format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
-   |             |
-   |             creates a temporary value which is freed while still in use
-...
-LL |     bar(x);
-   |         - borrow later used here
-   |
-   = note: the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used
-   = note: to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>
-
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/issue-114374-invalid-help-fmt-args.rs:10:15
-   |
-LL |     let foo = format_args!("{}", "hi");
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
-   |               |
-   |               creates a temporary value which is freed while still in use
-LL |
-LL |     bar(foo);
-   |         --- borrow later used here
-   |
-   = note: the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used
-   = note: to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr
index f29a41d..f422919 100644
--- a/tests/ui/check-cfg/target_feature.stderr
+++ b/tests/ui/check-cfg/target_feature.stderr
@@ -211,10 +211,6 @@
 `reference-types`
 `relax`
 `relaxed-simd`
-`reserve-x18`
-`retpoline-external-thunk`
-`retpoline-indirect-branches`
-`retpoline-indirect-calls`
 `rtm`
 `sb`
 `scq`
diff --git a/tests/ui/consts/recursive-const-in-impl.stderr b/tests/ui/consts/recursive-const-in-impl.stderr
index 6175112..035d9c2 100644
--- a/tests/ui/consts/recursive-const-in-impl.stderr
+++ b/tests/ui/consts/recursive-const-in-impl.stderr
@@ -1,11 +1,12 @@
 error: queries overflow the depth limit!
-  --> $DIR/recursive-const-in-impl.rs:11:14
+  --> $DIR/recursive-const-in-impl.rs:11:20
    |
 LL |     println!("{}", Thing::<i32>::X);
-   |              ^^^^
+   |                    ^^^^^^^^^^^^^^^
    |
    = help: consider increasing the recursion limit by adding a `#![recursion_limit = "14"]` attribute to your crate (`recursive_const_in_impl`)
    = note: query depth increased by 9 when simplifying constant for the type system `main::promoted[1]`
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
index 77cc307..ed5a112 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
@@ -11,5 +11,5 @@ fn none() {}
 
 #[optimize(banana)]
 //~^ ERROR the `#[optimize]` attribute is an experimental feature
-//~| ERROR E0722
+//~| ERROR malformed `optimize` attribute input [E0539]
 fn not_known() {}
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
index 4e6e4ac..e7e62b4 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
@@ -38,13 +38,16 @@
    = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0722]: invalid argument
-  --> $DIR/feature-gate-optimize_attribute.rs:12:12
+error[E0539]: malformed `optimize` attribute input
+  --> $DIR/feature-gate-optimize_attribute.rs:12:1
    |
 LL | #[optimize(banana)]
-   |            ^^^^^^
+   | ^^^^^^^^^^^------^^
+   | |          |
+   | |          valid arguments are `size`, `speed` or `none`
+   | help: must be of the form: `#[optimize(size|speed|none)]`
 
 error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0658, E0722.
-For more information about an error, try `rustc --explain E0658`.
+Some errors have detailed explanations: E0539, E0658.
+For more information about an error, try `rustc --explain E0539`.
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
index 1c6868d..9280dfd 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
@@ -379,14 +379,6 @@
 LL | #![proc_macro_derive()]
    | ^^^^^^^^^^^^^^^^^^^^^^^
 
-warning: attribute should be applied to a function definition
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1
-   |
-LL | #![cold]
-   | ^^^^^^^^ cannot be applied to crates
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-
 warning: attribute should be applied to an `extern` block with non-Rust ABI
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:64:1
    |
@@ -417,6 +409,14 @@
 LL | #![must_use]
    | ^^^^^^^^^^^^
 
+warning: attribute should be applied to a function definition
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1
+   |
+LL | #![cold]
+   | ^^^^^^^^ cannot be applied to crates
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
 warning: `#[macro_use]` only has an effect on `extern crate` and modules
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:176:5
    |
diff --git a/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr b/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
index cd9ed0f..1dcd800 100644
--- a/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
+++ b/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
@@ -8,7 +8,7 @@
    |     ^^^^^^^^^ mutable borrow occurs here
 ...
 LL |     println!("{h}");
-   |               --- immutable borrow later used here
+   |                - immutable borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/foreign-2021.rs:7:13
diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
index 676b6c1..aa0f640 100644
--- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr
+++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
@@ -23,7 +23,7 @@
    |     ^^^^^^^^^ mutable borrow occurs here
 ...
 LL |     println!("{a}");
-   |               --- immutable borrow later used here
+   |                - immutable borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:16:13
@@ -99,7 +99,7 @@
    |     ^ second mutable borrow occurs here
 ...
 LL |     println!("{a}");
-   |               --- first borrow later used here
+   |                - first borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:63:13
@@ -175,7 +175,7 @@
    |     ^^^^^^^ `s.f` is assigned to here but it was already borrowed
 ...
 LL |     println!("{a}");
-   |               --- borrow later used here
+   |                - borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:112:13
@@ -197,7 +197,7 @@
    |     ^^^^^^^ `s.f` is assigned to here but it was already borrowed
 ...
 LL |     println!("{a}");
-   |               --- borrow later used here
+   |                - borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:128:13
@@ -219,7 +219,7 @@
    |     ^^^ use of borrowed `s.f`
 ...
 LL |     println!("{a}");
-   |               --- borrow later used here
+   |                - borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:140:13
diff --git a/tests/ui/issues/issue-43988.stderr b/tests/ui/issues/issue-43988.stderr
index bd4eb8b..fe61e13 100644
--- a/tests/ui/issues/issue-43988.stderr
+++ b/tests/ui/issues/issue-43988.stderr
@@ -38,7 +38,7 @@
    |     ^^^^^^^
    |     |
    |     expected this to be a list
-   |     help: must be of the form: `#[repr(C)]`
+   |     help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error[E0539]: malformed `inline` attribute input
   --> $DIR/issue-43988.rs:30:5
@@ -64,7 +64,7 @@
    |              ^^^^^^^
    |              |
    |              expected this to be a list
-   |              help: must be of the form: `#[repr(C)]`
+   |              help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error[E0518]: attribute should be applied to function or closure
   --> $DIR/issue-43988.rs:5:5
diff --git a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
index 66f9140..b0d64b5 100644
--- a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
+++ b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
@@ -6,7 +6,7 @@
    |                   |
    |                   creates a temporary value which is freed while still in use
 LL |     println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?}");
-   |                                                   ----- borrow later used here
+   |                                                    - borrow later used here
    |
 help: consider using a `let` binding to create a longer lived value
    |
diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr
index e1c45e8..03ce975 100644
--- a/tests/ui/lint/unused/unused-attr-duplicate.stderr
+++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr
@@ -103,18 +103,6 @@
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:77:1
-   |
-LL | #[cold]
-   | ^^^^^^^ help: remove this attribute
-   |
-note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:76:1
-   |
-LL | #[cold]
-   | ^^^^^^^
-
-error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:79:1
    |
 LL | #[track_caller]
@@ -289,5 +277,17 @@
    | ^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
+error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:77:1
+   |
+LL | #[cold]
+   | ^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:76:1
+   |
+LL | #[cold]
+   | ^^^^^^^
+
 error: aborting due to 23 previous errors
 
diff --git a/tests/ui/macros/format-args-temporaries-in-write.stderr b/tests/ui/macros/format-args-temporaries-in-write.stderr
index e58a433..4899878 100644
--- a/tests/ui/macros/format-args-temporaries-in-write.stderr
+++ b/tests/ui/macros/format-args-temporaries-in-write.stderr
@@ -13,11 +13,6 @@
    |     -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
    |     |
    |     `mutex` dropped here while still borrowed
-   |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
-   |
-LL |         write!(Out, "{}", mutex.lock()); /* no semicolon */
-   |                                        +
 
 error[E0597]: `mutex` does not live long enough
   --> $DIR/format-args-temporaries-in-write.rs:47:29
@@ -34,11 +29,6 @@
    |     -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
    |     |
    |     `mutex` dropped here while still borrowed
-   |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
-   |
-LL |         writeln!(Out, "{}", mutex.lock()); /* no semicolon */
-   |                                          +
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/repr/repr.stderr b/tests/ui/repr/repr.stderr
index f3b1139..9e58133 100644
--- a/tests/ui/repr/repr.stderr
+++ b/tests/ui/repr/repr.stderr
@@ -5,7 +5,7 @@
    | ^^^^^^^
    | |
    | expected this to be a list
-   | help: must be of the form: `#[repr(C)]`
+   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error[E0539]: malformed `repr` attribute input
   --> $DIR/repr.rs:4:1
@@ -14,7 +14,7 @@
    | ^^^^^^^^^^^^^
    | |
    | expected this to be a list
-   | help: must be of the form: `#[repr(C)]`
+   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error[E0539]: malformed `repr` attribute input
   --> $DIR/repr.rs:7:1
@@ -23,7 +23,7 @@
    | ^^^^^^^^^^^^^
    | |
    | expected this to be a list
-   | help: must be of the form: `#[repr(C)]`
+   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/sized/unsized-binding.rs b/tests/ui/sized/unsized-binding.rs
index 3b99b0f..ce6c152 100644
--- a/tests/ui/sized/unsized-binding.rs
+++ b/tests/ui/sized/unsized-binding.rs
@@ -1,5 +1,5 @@
 fn main() {
     let x = *""; //~ ERROR E0277
-    println!("{}", x);
-    println!("{}", x);
+    drop(x);
+    drop(x);
 }
diff --git a/tests/ui/statics/read_before_init.rs b/tests/ui/statics/read_before_init.rs
new file mode 100644
index 0000000..d779ef6
--- /dev/null
+++ b/tests/ui/statics/read_before_init.rs
@@ -0,0 +1,22 @@
+//! This test checks the one code path that does not go through
+//! the regular CTFE memory access (as an optimization). We forgot
+//! to duplicate the static item self-initialization check, allowing
+//! reading from the uninitialized static memory before it was
+//! initialized at the end of the static initializer.
+//!
+//! https://github.com/rust-lang/rust/issues/142532
+
+use std::mem::MaybeUninit;
+
+pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
+//~^ ERROR: encountered static that tried to initialize itself with itself
+
+const fn foo(x: &i32) -> MaybeUninit<i32> {
+    let mut temp = MaybeUninit::<i32>::uninit();
+    unsafe {
+        std::ptr::copy(x, temp.as_mut_ptr(), 1);
+    }
+    temp
+}
+
+fn main() {}
diff --git a/tests/ui/statics/read_before_init.stderr b/tests/ui/statics/read_before_init.stderr
new file mode 100644
index 0000000..aeebcf7
--- /dev/null
+++ b/tests/ui/statics/read_before_init.stderr
@@ -0,0 +1,17 @@
+error[E0080]: encountered static that tried to initialize itself with itself
+  --> $DIR/read_before_init.rs:11:45
+   |
+LL | pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
+   |                                             ^^^^^^^^^ evaluation of `X` failed inside this call
+   |
+note: inside `foo`
+  --> $DIR/read_before_init.rs:17:9
+   |
+LL |         std::ptr::copy(x, temp.as_mut_ptr(), 1);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `std::ptr::copy::<i32>`
+  --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/suggestions/issue-97760.stderr b/tests/ui/suggestions/issue-97760.stderr
index c3cf7e1..ddd143b 100644
--- a/tests/ui/suggestions/issue-97760.stderr
+++ b/tests/ui/suggestions/issue-97760.stderr
@@ -1,8 +1,8 @@
 error[E0277]: `<impl IntoIterator as IntoIterator>::Item` doesn't implement `std::fmt::Display`
-  --> $DIR/issue-97760.rs:4:19
+  --> $DIR/issue-97760.rs:4:20
    |
 LL |         println!("{x}");
-   |                   ^^^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
+   |                    ^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
    |
    = help: the trait `std::fmt::Display` is not implemented for `<impl IntoIterator as IntoIterator>::Item`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr
index 2dca0c2..0b2d71f 100644
--- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr
+++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr
@@ -7,13 +7,5 @@
    |
    = note: this feature is not stably supported; its behavior can change in the future
 
-warning: unstable feature specified for `-Ctarget-feature`: `f`
-   |
-   = note: this feature is not stably supported; its behavior can change in the future
-
-warning: unstable feature specified for `-Ctarget-feature`: `zicsr`
-   |
-   = note: this feature is not stably supported; its behavior can change in the future
-
-warning: 4 warnings emitted
+warning: 2 warnings emitted
 
diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs
index 302ccec..1006b07 100644
--- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs
+++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs
@@ -24,5 +24,3 @@ pub trait Freeze {}
 
 //~? WARN must be disabled to ensure that the ABI of the current target can be implemented correctly
 //~? WARN unstable feature specified for `-Ctarget-feature`
-//[riscv]~? WARN unstable feature specified for `-Ctarget-feature`
-//[riscv]~? WARN unstable feature specified for `-Ctarget-feature`
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
index 2a0f5f0..79e8982 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
@@ -1,4 +1,4 @@
-warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
+warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline-external-thunk` compiler flag instead
    |
    = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
index f7b6cb1..f5ff15d 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
@@ -1,4 +1,4 @@
-warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline` compiler flag instead
    |
    = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
index 4f2cd1d..158cca0 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
@@ -1,4 +1,4 @@
-warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline` compiler flag instead
    |
    = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.rs b/tests/ui/target-feature/retpoline-target-feature-flag.rs
index de3c44c..05c8586 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.rs
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.rs
@@ -16,6 +16,6 @@
 #![no_core]
 extern crate minicore;
 
-//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
-//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
-//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`
+//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`
+//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`
diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout
index c20f123..5d6e390 100644
--- a/tests/ui/unpretty/exhaustive.hir.stdout
+++ b/tests/ui/unpretty/exhaustive.hir.stdout
@@ -405,8 +405,10 @@
     fn expr_format_args() {
         let expr;
         format_arguments::new_const(&[]);
-        format_arguments::new_v1(&[""],
-            &[format_argument::new_display(&expr)]);
+        {
+            super let args = [format_argument::new_display(&expr)];
+            format_arguments::new_v1(&[""], &args)
+        };
     }
 }
 mod items {
diff --git a/tests/ui/unpretty/flattened-format-args.stdout b/tests/ui/unpretty/flattened-format-args.stdout
index a5d9432..4af8292 100644
--- a/tests/ui/unpretty/flattened-format-args.stdout
+++ b/tests/ui/unpretty/flattened-format-args.stdout
@@ -10,7 +10,9 @@
     let x = 1;
     // Should flatten to println!("a 123 b {x} xyz\n"):
     {
-        ::std::io::_print(format_arguments::new_v1(&["a 123 b ", " xyz\n"],
-                &[format_argument::new_display(&x)]));
+        ::std::io::_print({
+                super let args = [format_argument::new_display(&x)];
+                format_arguments::new_v1(&["a 123 b ", " xyz\n"], &args)
+            });
     };
 }