about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-12 17:48:31 +0000
committerbors <bors@rust-lang.org>2022-10-12 17:48:31 +0000
commitc0983a9aac889d16722a12602ac678051e62c3fb (patch)
tree69917ea3c0bba48597bde38e8736912d184be91a /compiler
parent538f118da1409759ba198acc0ff62070bc6d2dce (diff)
parentf2c48105ce6f0889c18648160f8f0f03b69bc4a4 (diff)
downloadrust-c0983a9aac889d16722a12602ac678051e62c3fb.tar.gz
rust-c0983a9aac889d16722a12602ac678051e62c3fb.zip
Auto merge of #102975 - Dylan-DPC:rollup-vzuwsh2, r=Dylan-DPC
Rollup of 7 pull requests

Successful merges:

 - #102623 (translation: eager translation)
 - #102719 (Enforce alphabetical sorting with tidy)
 - #102830 (Unify `tcx.constness` query and param env constness checks)
 - #102883 (Fix stabilization of `feature(half_open_range_patterns)`)
 - #102927 (Fix `let` keyword removal suggestion in structs)
 - #102936 (rustdoc: remove unused CSS `nav.sum`)
 - #102940 (Update books)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs5
-rw-r--r--compiler/rustc_ast/src/token.rs3
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs15
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs7
-rw-r--r--compiler/rustc_const_eval/src/const_eval/fn_queries.rs72
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs3
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs5
-rw-r--r--compiler/rustc_error_messages/locales/en-US/query_system.ftl2
-rw-r--r--compiler/rustc_error_messages/src/lib.rs31
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs4
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs57
-rw-r--r--compiler/rustc_errors/src/emitter.rs4
-rw-r--r--compiler/rustc_errors/src/json.rs4
-rw-r--r--compiler/rustc_errors/src/lib.rs11
-rw-r--r--compiler/rustc_errors/src/translation.rs34
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs5
-rw-r--r--compiler/rustc_infer/src/errors/mod.rs33
-rw-r--r--compiler/rustc_infer/src/errors/note_and_explain.rs9
-rw-r--r--compiler/rustc_interface/src/tests.rs27
-rw-r--r--compiler/rustc_lint/src/errors.rs15
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs27
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs150
-rw-r--r--compiler/rustc_macros/src/diagnostics/mod.rs4
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs95
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs108
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs56
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs3
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs3
-rw-r--r--compiler/rustc_middle/src/thir.rs3
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs3
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs3
-rw-r--r--compiler/rustc_parse/src/parser/item.rs17
-rw-r--r--compiler/rustc_query_system/src/error.rs12
-rw-r--r--compiler/rustc_query_system/src/lib.rs2
-rw-r--r--compiler/rustc_session/src/options.rs106
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs3
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs69
40 files changed, 615 insertions, 412 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 340302766d2..60b7f2e4c22 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -3039,7 +3039,7 @@ pub type ForeignItem = Item<ForeignItemKind>;
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(AssocItem, 104);
     static_assert_size!(AssocItemKind, 32);
     static_assert_size!(Attribute, 32);
@@ -3060,11 +3060,12 @@ mod size_asserts {
     static_assert_size!(Local, 72);
     static_assert_size!(Param, 40);
     static_assert_size!(Pat, 120);
-    static_assert_size!(PatKind, 96);
     static_assert_size!(Path, 40);
     static_assert_size!(PathSegment, 24);
+    static_assert_size!(PatKind, 96);
     static_assert_size!(Stmt, 32);
     static_assert_size!(StmtKind, 16);
     static_assert_size!(Ty, 96);
     static_assert_size!(TyKind, 72);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 16224d71e45..83b10d906e2 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -889,10 +889,11 @@ where
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(Lit, 12);
     static_assert_size!(LitKind, 2);
     static_assert_size!(Nonterminal, 16);
     static_assert_size!(Token, 24);
     static_assert_size!(TokenKind, 16);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index 1d3c4fcca0a..015f5c1ee8a 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -646,10 +646,11 @@ impl DelimSpan {
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(AttrTokenStream, 8);
     static_assert_size!(AttrTokenTree, 32);
     static_assert_size!(LazyAttrTokenStream, 8);
     static_assert_size!(TokenStream, 8);
     static_assert_size!(TokenTree, 32);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 63ff64b00be..c6c85ffa84d 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -1,4 +1,7 @@
-use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay};
+use rustc_errors::{
+    fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay,
+    SubdiagnosticMessage,
+};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{symbol::Ident, Span, Symbol};
 
@@ -19,7 +22,10 @@ pub struct UseAngleBrackets {
 }
 
 impl AddToDiagnostic for UseAngleBrackets {
-    fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         diag.multipart_suggestion(
             fluent::ast_lowering::use_angle_brackets,
             vec![(self.open_param, String::from("<")), (self.close_param, String::from(">"))],
@@ -69,7 +75,10 @@ pub enum AssocTyParenthesesSub {
 }
 
 impl AddToDiagnostic for AssocTyParenthesesSub {
-    fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         match self {
             Self::Empty { parentheses_span } => diag.multipart_suggestion(
                 fluent::ast_lowering::remove_parentheses,
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 035f0ce1cbc..ba2ed24fc08 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -1,6 +1,6 @@
 //! Errors emitted by ast_passes.
 
-use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic};
+use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessage};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{Span, Symbol};
 
@@ -17,7 +17,10 @@ pub struct ForbiddenLet {
 }
 
 impl AddToDiagnostic for ForbiddenLetReason {
-    fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         match self {
             Self::GenericForbidden => {}
             Self::NotSupportedOr(span) => {
@@ -228,7 +231,10 @@ pub struct ExternBlockSuggestion {
 }
 
 impl AddToDiagnostic for ExternBlockSuggestion {
-    fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         let start_suggestion = if let Some(abi) = self.abi {
             format!("extern \"{}\" {{", abi)
         } else {
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 1f577e9f352..a292bfce31e 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -15,7 +15,10 @@ use rustc_data_structures::profiling::TimingGuard;
 use rustc_data_structures::profiling::VerboseTimingGuard;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::Emitter;
-use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level};
+use rustc_errors::{
+    translation::{to_fluent_args, Translate},
+    DiagnosticId, FatalError, Handler, Level,
+};
 use rustc_fs_util::link_or_copy;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_incremental::{
@@ -1740,7 +1743,7 @@ impl Translate for SharedEmitter {
 
 impl Emitter for SharedEmitter {
     fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
-        let fluent_args = self.to_fluent_args(diag.args());
+        let fluent_args = to_fluent_args(diag.args());
         drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
             msg: self.translate_messages(&diag.message, &fluent_args).to_string(),
             code: diag.code.clone(),
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
index f1674d04f8d..cdcebb61c2e 100644
--- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -25,12 +25,10 @@ pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
 /// report whether said intrinsic has a `rustc_const_{un,}stable` attribute. Otherwise, return
 /// `Constness::NotConst`.
 fn constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness {
-    let def_id = def_id.expect_local();
-    let node = tcx.hir().get_by_def_id(def_id);
-
-    match node {
+    let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+    match tcx.hir().get(hir_id) {
         hir::Node::Ctor(_) => hir::Constness::Const,
-        hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness,
+
         hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => {
             // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other
             // foreign items cannot be evaluated at compile-time.
@@ -41,20 +39,62 @@ fn constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness {
             };
             if is_const { hir::Constness::Const } else { hir::Constness::NotConst }
         }
-        _ => {
-            if let Some(fn_kind) = node.fn_kind() {
-                if fn_kind.constness() == hir::Constness::Const {
-                    return hir::Constness::Const;
-                }
 
-                // If the function itself is not annotated with `const`, it may still be a `const fn`
-                // if it resides in a const trait impl.
-                let is_const = is_parent_const_impl_raw(tcx, def_id);
-                if is_const { hir::Constness::Const } else { hir::Constness::NotConst }
-            } else {
-                hir::Constness::NotConst
+        hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
+            if tcx.is_const_default_method(def_id) =>
+        {
+            hir::Constness::Const
+        }
+
+        hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(..), .. })
+        | hir::Node::Item(hir::Item { kind: hir::ItemKind::Static(..), .. })
+        | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Const(..), .. })
+        | hir::Node::AnonConst(_)
+        | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. })
+        | hir::Node::ImplItem(hir::ImplItem {
+            kind:
+                hir::ImplItemKind::Fn(
+                    hir::FnSig {
+                        header: hir::FnHeader { constness: hir::Constness::Const, .. },
+                        ..
+                    },
+                    ..,
+                ),
+            ..
+        }) => hir::Constness::Const,
+
+        hir::Node::ImplItem(hir::ImplItem {
+            kind: hir::ImplItemKind::Type(..) | hir::ImplItemKind::Fn(..),
+            ..
+        }) => {
+            let parent_hir_id = tcx.hir().get_parent_node(hir_id);
+            match tcx.hir().get(parent_hir_id) {
+                hir::Node::Item(hir::Item {
+                    kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
+                    ..
+                }) => *constness,
+                _ => span_bug!(
+                    tcx.def_span(parent_hir_id.owner),
+                    "impl item's parent node is not an impl",
+                ),
             }
         }
+
+        hir::Node::Item(hir::Item {
+            kind: hir::ItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..),
+            ..
+        })
+        | hir::Node::TraitItem(hir::TraitItem {
+            kind:
+                hir::TraitItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..),
+            ..
+        })
+        | hir::Node::Item(hir::Item {
+            kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
+            ..
+        }) => *constness,
+
+        _ => hir::Constness::NotConst,
     }
 }
 
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 510adde6296..719588a936c 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -788,9 +788,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(Immediate, 48);
     static_assert_size!(ImmTy<'_>, 64);
     static_assert_size!(Operand, 56);
     static_assert_size!(OpTy<'_>, 80);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index eeeb7d6d3e5..b0625b5f412 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -892,10 +892,11 @@ where
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
-    // These are in alphabetical order, which is easy to maintain.
-    static_assert_size!(MemPlaceMeta, 24);
+    // tidy-alphabetical-start
     static_assert_size!(MemPlace, 40);
+    static_assert_size!(MemPlaceMeta, 24);
     static_assert_size!(MPlaceTy<'_>, 64);
     static_assert_size!(Place, 40);
     static_assert_size!(PlaceTy<'_>, 64);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_error_messages/locales/en-US/query_system.ftl b/compiler/rustc_error_messages/locales/en-US/query_system.ftl
index b914ba52a73..870e824039c 100644
--- a/compiler/rustc_error_messages/locales/en-US/query_system.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/query_system.ftl
@@ -12,6 +12,8 @@ query_system_cycle_usage = cycle used when {$usage}
 
 query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again
 
+query_system_cycle_stack_middle = ...which requires {$desc}...
+
 query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle
 
 query_system_cycle_recursive_ty_alias = type aliases cannot be recursive
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index a2d507328b3..a6024044ad8 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -35,6 +35,7 @@ pub use unic_langid::{langid, LanguageIdentifier};
 
 // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module.
 fluent_messages! {
+    // tidy-alphabetical-start
     ast_lowering => "../locales/en-US/ast_lowering.ftl",
     ast_passes => "../locales/en-US/ast_passes.ftl",
     attr => "../locales/en-US/attr.ftl",
@@ -64,6 +65,7 @@ fluent_messages! {
     symbol_mangling => "../locales/en-US/symbol_mangling.ftl",
     trait_selection => "../locales/en-US/trait_selection.ftl",
     ty_utils => "../locales/en-US/ty_utils.ftl",
+    // tidy-alphabetical-end
 }
 
 pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES};
@@ -277,6 +279,18 @@ pub enum SubdiagnosticMessage {
     /// Non-translatable diagnostic message.
     // FIXME(davidtwco): can a `Cow<'static, str>` be used here?
     Str(String),
+    /// Translatable message which has already been translated eagerly.
+    ///
+    /// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
+    /// be instantiated multiple times with different values. As translation normally happens
+    /// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run,
+    /// the setting of diagnostic arguments in the derived code will overwrite previous variable
+    /// values and only the final value will be set when translation occurs - resulting in
+    /// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
+    /// happening immediately after the subdiagnostic derive's logic has been run. This variant
+    /// stores messages which have been translated eagerly.
+    // FIXME(#100717): can a `Cow<'static, str>` be used here?
+    Eager(String),
     /// Identifier of a Fluent message. Instances of this variant are generated by the
     /// `Subdiagnostic` derive.
     FluentIdentifier(FluentId),
@@ -304,8 +318,20 @@ impl<S: Into<String>> From<S> for SubdiagnosticMessage {
 #[rustc_diagnostic_item = "DiagnosticMessage"]
 pub enum DiagnosticMessage {
     /// Non-translatable diagnostic message.
-    // FIXME(davidtwco): can a `Cow<'static, str>` be used here?
+    // FIXME(#100717): can a `Cow<'static, str>` be used here?
     Str(String),
+    /// Translatable message which has already been translated eagerly.
+    ///
+    /// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
+    /// be instantiated multiple times with different values. As translation normally happens
+    /// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run,
+    /// the setting of diagnostic arguments in the derived code will overwrite previous variable
+    /// values and only the final value will be set when translation occurs - resulting in
+    /// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
+    /// happening immediately after the subdiagnostic derive's logic has been run. This variant
+    /// stores messages which have been translated eagerly.
+    // FIXME(#100717): can a `Cow<'static, str>` be used here?
+    Eager(String),
     /// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic
     /// message.
     ///
@@ -324,6 +350,7 @@ impl DiagnosticMessage {
     pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self {
         let attr = match sub {
             SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s),
+            SubdiagnosticMessage::Eager(s) => return DiagnosticMessage::Eager(s),
             SubdiagnosticMessage::FluentIdentifier(id) => {
                 return DiagnosticMessage::FluentIdentifier(id, None);
             }
@@ -332,6 +359,7 @@ impl DiagnosticMessage {
 
         match self {
             DiagnosticMessage::Str(s) => DiagnosticMessage::Str(s.clone()),
+            DiagnosticMessage::Eager(s) => DiagnosticMessage::Eager(s.clone()),
             DiagnosticMessage::FluentIdentifier(id, _) => {
                 DiagnosticMessage::FluentIdentifier(id.clone(), Some(attr))
             }
@@ -367,6 +395,7 @@ impl Into<SubdiagnosticMessage> for DiagnosticMessage {
     fn into(self) -> SubdiagnosticMessage {
         match self {
             DiagnosticMessage::Str(s) => SubdiagnosticMessage::Str(s),
+            DiagnosticMessage::Eager(s) => SubdiagnosticMessage::Eager(s),
             DiagnosticMessage::FluentIdentifier(id, None) => {
                 SubdiagnosticMessage::FluentIdentifier(id)
             }
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index b32fc3c719b..f14b8ee3254 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -7,7 +7,7 @@
 
 use crate::emitter::FileWithAnnotatedLines;
 use crate::snippet::Line;
-use crate::translation::Translate;
+use crate::translation::{to_fluent_args, Translate};
 use crate::{
     CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle,
     LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic,
@@ -46,7 +46,7 @@ impl Translate for AnnotateSnippetEmitterWriter {
 impl Emitter for AnnotateSnippetEmitterWriter {
     /// The entry point for the diagnostics generation
     fn emit_diagnostic(&mut self, diag: &Diagnostic) {
-        let fluent_args = self.to_fluent_args(diag.args());
+        let fluent_args = to_fluent_args(diag.args());
 
         let mut children = diag.children.clone();
         let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 31e410aaaf0..3e0840caaa6 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -27,7 +27,11 @@ pub struct SuggestionsDisabled;
 /// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of
 /// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of
 /// diagnostic emission.
-pub type DiagnosticArg<'source> = (Cow<'source, str>, DiagnosticArgValue<'source>);
+pub type DiagnosticArg<'iter, 'source> =
+    (&'iter DiagnosticArgName<'source>, &'iter DiagnosticArgValue<'source>);
+
+/// Name of a diagnostic argument.
+pub type DiagnosticArgName<'source> = Cow<'source, str>;
 
 /// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted
 /// to a `FluentValue` by the emitter to be used in diagnostic translation.
@@ -199,9 +203,20 @@ impl IntoDiagnosticArg for ast::token::TokenKind {
 /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
 #[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")]
 #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "AddToDiagnostic")]
-pub trait AddToDiagnostic {
+pub trait AddToDiagnostic
+where
+    Self: Sized,
+{
     /// Add a subdiagnostic to an existing diagnostic.
-    fn add_to_diagnostic(self, diag: &mut Diagnostic);
+    fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+        self.add_to_diagnostic_with(diag, |_, m| m);
+    }
+
+    /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used
+    /// (to optionally perform eager translation).
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage;
 }
 
 /// Trait implemented by lint types. This should not be implemented manually. Instead, use
@@ -229,7 +244,7 @@ pub struct Diagnostic {
     pub span: MultiSpan,
     pub children: Vec<SubDiagnostic>,
     pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
-    args: Vec<DiagnosticArg<'static>>,
+    args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>,
 
     /// This is not used for highlighting or rendering any error message.  Rather, it can be used
     /// as a sort key to sort a buffer of diagnostics.  By default, it is the primary span of
@@ -321,7 +336,7 @@ impl Diagnostic {
             span: MultiSpan::new(),
             children: vec![],
             suggestions: Ok(vec![]),
-            args: vec![],
+            args: Default::default(),
             sort_span: DUMMY_SP,
             is_lint: false,
         }
@@ -917,13 +932,30 @@ impl Diagnostic {
         self
     }
 
-    /// Add a subdiagnostic from a type that implements `Subdiagnostic` - see
-    /// [rustc_macros::Subdiagnostic].
+    /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
+    /// [rustc_macros::Subdiagnostic]).
     pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self {
         subdiagnostic.add_to_diagnostic(self);
         self
     }
 
+    /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
+    /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages
+    /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of
+    /// interpolated variables).
+    pub fn eager_subdiagnostic(
+        &mut self,
+        handler: &crate::Handler,
+        subdiagnostic: impl AddToDiagnostic,
+    ) -> &mut Self {
+        subdiagnostic.add_to_diagnostic_with(self, |diag, msg| {
+            let args = diag.args();
+            let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
+            handler.eagerly_translate(msg, args)
+        });
+        self
+    }
+
     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
         self.span = sp.into();
         if let Some(span) = self.span.primary_span() {
@@ -956,8 +988,11 @@ impl Diagnostic {
         self
     }
 
-    pub fn args(&self) -> &[DiagnosticArg<'static>] {
-        &self.args
+    // Exact iteration order of diagnostic arguments shouldn't make a difference to output because
+    // they're only used in interpolation.
+    #[allow(rustc::potential_query_instability)]
+    pub fn args<'a>(&'a self) -> impl Iterator<Item = DiagnosticArg<'a, 'static>> {
+        self.args.iter()
     }
 
     pub fn set_arg(
@@ -965,7 +1000,7 @@ impl Diagnostic {
         name: impl Into<Cow<'static, str>>,
         arg: impl IntoDiagnosticArg,
     ) -> &mut Self {
-        self.args.push((name.into(), arg.into_diagnostic_arg()));
+        self.args.insert(name.into(), arg.into_diagnostic_arg());
         self
     }
 
@@ -976,7 +1011,7 @@ impl Diagnostic {
     /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
     /// combining it with the primary message of the diagnostic (if translatable, otherwise it just
     /// passes the user's string along).
-    fn subdiagnostic_message_to_diagnostic_message(
+    pub(crate) fn subdiagnostic_message_to_diagnostic_message(
         &self,
         attr: impl Into<SubdiagnosticMessage>,
     ) -> DiagnosticMessage {
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 66fbb8f1213..cd6413bc3ec 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -14,7 +14,7 @@ use rustc_span::{FileLines, SourceFile, Span};
 
 use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
 use crate::styled_buffer::StyledBuffer;
-use crate::translation::Translate;
+use crate::translation::{to_fluent_args, Translate};
 use crate::{
     CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler,
     LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
@@ -535,7 +535,7 @@ impl Emitter for EmitterWriter {
     }
 
     fn emit_diagnostic(&mut self, diag: &Diagnostic) {
-        let fluent_args = self.to_fluent_args(diag.args());
+        let fluent_args = to_fluent_args(diag.args());
 
         let mut children = diag.children.clone();
         let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 1680c6accd7..4cc7be47fc2 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -13,7 +13,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap};
 
 use crate::emitter::{Emitter, HumanReadableErrorType};
 use crate::registry::Registry;
-use crate::translation::Translate;
+use crate::translation::{to_fluent_args, Translate};
 use crate::DiagnosticId;
 use crate::{
     CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic,
@@ -312,7 +312,7 @@ struct UnusedExterns<'a, 'b, 'c> {
 
 impl Diagnostic {
     fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
-        let args = je.to_fluent_args(diag.args());
+        let args = to_fluent_args(diag.args());
         let sugg = diag.suggestions.iter().flatten().map(|sugg| {
             let translated_message = je.translate_message(&sugg.msg, &args);
             Diagnostic {
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index c8ccdc539af..b16c54e0aac 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -598,6 +598,17 @@ impl Handler {
         }
     }
 
+    /// Translate `message` eagerly with `args`.
+    pub fn eagerly_translate<'a>(
+        &self,
+        message: DiagnosticMessage,
+        args: impl Iterator<Item = DiagnosticArg<'a, 'static>>,
+    ) -> SubdiagnosticMessage {
+        let inner = self.inner.borrow();
+        let args = crate::translation::to_fluent_args(args);
+        SubdiagnosticMessage::Eager(inner.emitter.translate_message(&message, &args).to_string())
+    }
+
     // This is here to not allow mutation of flags;
     // as of this writing it's only used in tests in librustc_middle.
     pub fn can_emit_warnings(&self) -> bool {
diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs
index 4f407badb3f..a7737b467b7 100644
--- a/compiler/rustc_errors/src/translation.rs
+++ b/compiler/rustc_errors/src/translation.rs
@@ -4,6 +4,27 @@ use rustc_data_structures::sync::Lrc;
 use rustc_error_messages::FluentArgs;
 use std::borrow::Cow;
 
+/// Convert diagnostic arguments (a rustc internal type that exists to implement
+/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
+///
+/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
+/// passed around as a reference thereafter.
+pub fn to_fluent_args<'iter, 'arg: 'iter>(
+    iter: impl Iterator<Item = DiagnosticArg<'iter, 'arg>>,
+) -> FluentArgs<'arg> {
+    let mut args = if let Some(size) = iter.size_hint().1 {
+        FluentArgs::with_capacity(size)
+    } else {
+        FluentArgs::new()
+    };
+
+    for (k, v) in iter {
+        args.set(k.clone(), v.clone());
+    }
+
+    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`
@@ -15,15 +36,6 @@ pub trait Translate {
     /// unavailable for the requested locale.
     fn fallback_fluent_bundle(&self) -> &FluentBundle;
 
-    /// Convert diagnostic arguments (a rustc internal type that exists to implement
-    /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
-    ///
-    /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
-    /// passed around as a reference thereafter.
-    fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
-        FromIterator::from_iter(args.iter().cloned())
-    }
-
     /// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
     fn translate_messages(
         &self,
@@ -43,7 +55,9 @@ pub trait Translate {
     ) -> Cow<'_, str> {
         trace!(?message, ?args);
         let (identifier, attr) = match message {
-            DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg),
+            DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
+                return Cow::Borrowed(&msg);
+            }
             DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
         };
 
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 48c40eae662..5ea433e6b3d 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -412,8 +412,6 @@ declare_features! (
     (incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None),
     /// Allows non-trivial generic constants which have to have wfness manually propagated to callers
     (incomplete, generic_const_exprs, "1.56.0", Some(76560), None),
-    /// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern.
-    (active, half_open_range_patterns, "1.41.0", Some(67264), None),
     /// Allows using `..=X` as a patterns in slices.
     (active, half_open_range_patterns_in_slices, "CURRENT_RUSTC_VERSION", Some(67264), None),
     /// Allows `if let` guard in match arms.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 098f9d51549..bc149e48d89 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3514,7 +3514,7 @@ impl<'hir> Node<'hir> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(Block<'_>, 48);
     static_assert_size!(Body<'_>, 32);
     static_assert_size!(Expr<'_>, 64);
@@ -3533,9 +3533,9 @@ mod size_asserts {
     static_assert_size!(Local<'_>, 64);
     static_assert_size!(Param<'_>, 32);
     static_assert_size!(Pat<'_>, 72);
-    static_assert_size!(PatKind<'_>, 48);
     static_assert_size!(Path<'_>, 40);
     static_assert_size!(PathSegment<'_>, 48);
+    static_assert_size!(PatKind<'_>, 48);
     static_assert_size!(QPath<'_>, 24);
     static_assert_size!(Res, 12);
     static_assert_size!(Stmt<'_>, 32);
@@ -3544,4 +3544,5 @@ mod size_asserts {
     static_assert_size!(TraitItemKind<'_>, 48);
     static_assert_size!(Ty<'_>, 48);
     static_assert_size!(TyKind<'_>, 32);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs
index 85b877652c6..500900d3d4a 100644
--- a/compiler/rustc_infer/src/errors/mod.rs
+++ b/compiler/rustc_infer/src/errors/mod.rs
@@ -1,6 +1,7 @@
 use hir::GenericParamKind;
 use rustc_errors::{
-    fluent, AddToDiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString, MultiSpan,
+    fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString,
+    MultiSpan, SubdiagnosticMessage,
 };
 use rustc_hir as hir;
 use rustc_hir::{FnRetTy, Ty};
@@ -229,7 +230,10 @@ pub enum RegionOriginNote<'a> {
 }
 
 impl AddToDiagnostic for RegionOriginNote<'_> {
-    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         let mut label_or_note = |span, msg: DiagnosticMessage| {
             let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count();
             let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count();
@@ -290,7 +294,10 @@ pub enum LifetimeMismatchLabels {
 }
 
 impl AddToDiagnostic for LifetimeMismatchLabels {
-    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         match self {
             LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => {
                 diag.span_label(param_span, fluent::infer::declared_different);
@@ -340,7 +347,10 @@ pub struct AddLifetimeParamsSuggestion<'a> {
 }
 
 impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> {
-    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         let mut mk_suggestion = || {
             let (
                 hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
@@ -439,7 +449,10 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq {
 }
 
 impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq {
-    fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) {
+    fn add_to_diagnostic_with<F>(mut self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         self.unmet_requirements
             .push_span_label(self.binding_span, fluent::infer::msl_introduces_static);
         diag.span_note(self.unmet_requirements, fluent::infer::msl_unmet_req);
@@ -451,7 +464,10 @@ pub struct ImplNote {
 }
 
 impl AddToDiagnostic for ImplNote {
-    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         match self.impl_span {
             Some(span) => diag.span_note(span, fluent::infer::msl_impl_note),
             None => diag.note(fluent::infer::msl_impl_note),
@@ -466,7 +482,10 @@ pub enum TraitSubdiag {
 
 // FIXME(#100717) used in `Vec<TraitSubdiag>` so requires eager translation/list support
 impl AddToDiagnostic for TraitSubdiag {
-    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         match self {
             TraitSubdiag::Note { span } => {
                 diag.span_note(span, "this has an implicit `'static` lifetime requirement");
diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs
index 7f54918f736..201a3c7100c 100644
--- a/compiler/rustc_infer/src/errors/note_and_explain.rs
+++ b/compiler/rustc_infer/src/errors/note_and_explain.rs
@@ -1,5 +1,7 @@
 use crate::infer::error_reporting::nice_region_error::find_anon_type;
-use rustc_errors::{self, fluent, AddToDiagnostic, IntoDiagnosticArg};
+use rustc_errors::{
+    self, fluent, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessage,
+};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::{symbol::kw, Span};
 
@@ -159,7 +161,10 @@ impl RegionExplanation<'_> {
 }
 
 impl AddToDiagnostic for RegionExplanation<'_> {
-    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         if let Some(span) = self.desc.span {
             diag.span_note(span, fluent::infer::region_explanation);
         } else {
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 98eeaad976f..d64cdcdbaa9 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -540,7 +540,7 @@ fn test_codegen_options_tracking_hash() {
     }
 
     // Make sure that changing an [UNTRACKED] option leaves the hash unchanged.
-    // This list is in alphabetical order.
+    // tidy-alphabetical-start
     untracked!(ar, String::from("abc"));
     untracked!(codegen_units, Some(42));
     untracked!(default_linker_libraries, true);
@@ -556,6 +556,7 @@ fn test_codegen_options_tracking_hash() {
     untracked!(rpath, true);
     untracked!(save_temps, true);
     untracked!(strip, Strip::Debuginfo);
+    // tidy-alphabetical-end
 
     macro_rules! tracked {
         ($name: ident, $non_default_value: expr) => {
@@ -567,7 +568,7 @@ fn test_codegen_options_tracking_hash() {
     }
 
     // Make sure that changing a [TRACKED] option changes the hash.
-    // This list is in alphabetical order.
+    // tidy-alphabetical-start
     tracked!(code_model, Some(CodeModel::Large));
     tracked!(control_flow_guard, CFGuard::Checks);
     tracked!(debug_assertions, Some(true));
@@ -577,8 +578,8 @@ fn test_codegen_options_tracking_hash() {
     tracked!(force_unwind_tables, Some(true));
     tracked!(inline_threshold, Some(0xf007ba11));
     tracked!(instrument_coverage, Some(InstrumentCoverage::All));
-    tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
     tracked!(link_dead_code, Some(true));
+    tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
     tracked!(llvm_args, vec![String::from("1"), String::from("2")]);
     tracked!(lto, LtoCli::Fat);
     tracked!(metadata, vec![String::from("A"), String::from("B")]);
@@ -599,6 +600,7 @@ fn test_codegen_options_tracking_hash() {
     tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));
     tracked!(target_cpu, Some(String::from("abc")));
     tracked!(target_feature, String::from("all the features, all of them"));
+    // tidy-alphabetical-end
 }
 
 #[test]
@@ -619,12 +621,13 @@ fn test_top_level_options_tracked_no_crate() {
     }
 
     // Make sure that changing a [TRACKED_NO_CRATE_HASH] option leaves the crate hash unchanged but changes the incremental hash.
-    // This list is in alphabetical order.
-    tracked!(remap_path_prefix, vec![("/home/bors/rust".into(), "src".into())]);
+    // tidy-alphabetical-start
     tracked!(
         real_rust_source_base_dir,
         Some("/home/bors/rust/.rustup/toolchains/nightly/lib/rustlib/src/rust".into())
     );
+    tracked!(remap_path_prefix, vec![("/home/bors/rust".into(), "src".into())]);
+    // tidy-alphabetical-end
 }
 
 #[test]
@@ -641,7 +644,7 @@ fn test_unstable_options_tracking_hash() {
     }
 
     // Make sure that changing an [UNTRACKED] option leaves the hash unchanged.
-    // This list is in alphabetical order.
+    // tidy-alphabetical-start
     untracked!(assert_incr_state, Some(String::from("loaded")));
     untracked!(deduplicate_diagnostics, false);
     untracked!(dep_tasks, true);
@@ -678,12 +681,12 @@ fn test_unstable_options_tracking_hash() {
     untracked!(perf_stats, true);
     // `pre_link_arg` is omitted because it just forwards to `pre_link_args`.
     untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]);
-    untracked!(profile_closures, true);
     untracked!(print_llvm_passes, true);
     untracked!(print_mono_items, Some(String::from("abc")));
     untracked!(print_type_sizes, true);
     untracked!(proc_macro_backtrace, true);
     untracked!(proc_macro_execution_strategy, ProcMacroExecutionStrategy::CrossThread);
+    untracked!(profile_closures, true);
     untracked!(query_dep_graph, true);
     untracked!(save_analysis, true);
     untracked!(self_profile, SwitchWithOptPath::Enabled(None));
@@ -701,6 +704,7 @@ fn test_unstable_options_tracking_hash() {
     untracked!(unstable_options, true);
     untracked!(validate_mir, true);
     untracked!(verbose, true);
+    // tidy-alphabetical-end
 
     macro_rules! tracked {
         ($name: ident, $non_default_value: expr) => {
@@ -712,7 +716,7 @@ fn test_unstable_options_tracking_hash() {
     }
 
     // Make sure that changing a [TRACKED] option changes the hash.
-    // This list is in alphabetical order.
+    // tidy-alphabetical-start
     tracked!(allow_features, Some(vec![String::from("lang_items")]));
     tracked!(always_encode_mir, true);
     tracked!(asm_comments, true);
@@ -733,10 +737,10 @@ fn test_unstable_options_tracking_hash() {
     tracked!(debug_macros, true);
     tracked!(dep_info_omit_d_target, true);
     tracked!(drop_tracking, true);
-    tracked!(export_executable_symbols, true);
     tracked!(dual_proc_macros, true);
     tracked!(dwarf_version, Some(5));
     tracked!(emit_thin_lto, false);
+    tracked!(export_executable_symbols, true);
     tracked!(fewer_names, Some(true));
     tracked!(force_unstable_if_unmarked, true);
     tracked!(fuel, Some(("abc".to_string(), 99)));
@@ -759,8 +763,8 @@ fn test_unstable_options_tracking_hash() {
     tracked!(mutable_noalias, Some(true));
     tracked!(no_generate_arange_section, true);
     tracked!(no_link, true);
-    tracked!(no_unique_section_names, true);
     tracked!(no_profiler_runtime, true);
+    tracked!(no_unique_section_names, true);
     tracked!(oom, OomStrategy::Panic);
     tracked!(osx_rpath_install_name, true);
     tracked!(packed_bundled_libs, true);
@@ -773,8 +777,8 @@ fn test_unstable_options_tracking_hash() {
     tracked!(print_fuel, Some("abc".to_string()));
     tracked!(profile, true);
     tracked!(profile_emit, Some(PathBuf::from("abc")));
-    tracked!(profiler_runtime, "abc".to_string());
     tracked!(profile_sample_use, Some(PathBuf::from("abc")));
+    tracked!(profiler_runtime, "abc".to_string());
     tracked!(relax_elf_relocations, Some(true));
     tracked!(relro_level, Some(RelroLevel::Full));
     tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
@@ -803,6 +807,7 @@ fn test_unstable_options_tracking_hash() {
     tracked!(verify_llvm_ir, true);
     tracked!(virtual_function_elimination, true);
     tracked!(wasi_exec_model, Some(WasiExecModel::Reactor));
+    // tidy-alphabetical-end
 
     macro_rules! tracked_no_crate_hash {
         ($name: ident, $non_default_value: expr) => {
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index 880f3fbd00e..97d012fb611 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -1,4 +1,7 @@
-use rustc_errors::{fluent, AddToDiagnostic, ErrorGuaranteed, Handler, IntoDiagnostic};
+use rustc_errors::{
+    fluent, AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic,
+    SubdiagnosticMessage,
+};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::lint::Level;
 use rustc_span::{Span, Symbol};
@@ -23,7 +26,10 @@ pub enum OverruledAttributeSub {
 }
 
 impl AddToDiagnostic for OverruledAttributeSub {
-    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         match self {
             OverruledAttributeSub::DefaultSource { id } => {
                 diag.note(fluent::lint::default_source);
@@ -88,7 +94,10 @@ pub struct RequestedLevel {
 }
 
 impl AddToDiagnostic for RequestedLevel {
-    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
         diag.note(fluent::lint::requested_level);
         diag.set_arg(
             "level",
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index 83040a652b1..8cf307df5a5 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -10,27 +10,31 @@ use synstructure::Structure;
 /// The central struct for constructing the `into_diagnostic` method from an annotated struct.
 pub(crate) struct DiagnosticDerive<'a> {
     structure: Structure<'a>,
-    handler: syn::Ident,
     builder: DiagnosticDeriveBuilder,
 }
 
 impl<'a> DiagnosticDerive<'a> {
     pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
         Self {
-            builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic },
-            handler,
+            builder: DiagnosticDeriveBuilder {
+                diag,
+                kind: DiagnosticDeriveKind::Diagnostic { handler },
+            },
             structure,
         }
     }
 
     pub(crate) fn into_tokens(self) -> TokenStream {
-        let DiagnosticDerive { mut structure, handler, mut builder } = self;
+        let DiagnosticDerive { mut structure, mut builder } = self;
 
         let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
             let preamble = builder.preamble(&variant);
             let body = builder.body(&variant);
 
             let diag = &builder.parent.diag;
+            let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
+                unreachable!()
+            };
             let init = match builder.slug.value_ref() {
                 None => {
                     span_err(builder.span, "diagnostic slug not specified")
@@ -48,14 +52,17 @@ impl<'a> DiagnosticDerive<'a> {
                 }
             };
 
+            let formatting_init = &builder.formatting_init;
             quote! {
                 #init
+                #formatting_init
                 #preamble
                 #body
                 #diag
             }
         });
 
+        let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() };
         structure.gen_impl(quote! {
             gen impl<'__diagnostic_handler_sess, G>
                     rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
@@ -96,17 +103,18 @@ impl<'a> LintDiagnosticDerive<'a> {
             let body = builder.body(&variant);
 
             let diag = &builder.parent.diag;
-
+            let formatting_init = &builder.formatting_init;
             quote! {
                 #preamble
+                #formatting_init
                 #body
                 #diag
             }
         });
 
         let msg = builder.each_variant(&mut structure, |mut builder, variant| {
-            // HACK(wafflelapkin): initialize slug (???)
-            let _preamble = builder.preamble(&variant);
+            // Collect the slug by generating the preamble.
+            let _ = builder.preamble(&variant);
 
             match builder.slug.value_ref() {
                 None => {
@@ -125,7 +133,10 @@ impl<'a> LintDiagnosticDerive<'a> {
         let diag = &builder.diag;
         structure.gen_impl(quote! {
             gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self {
-                fn decorate_lint<'__b>(self, #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> {
+                fn decorate_lint<'__b>(
+                    self,
+                    #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>
+                ) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> {
                     use rustc_errors::IntoDiagnosticArg;
                     #implementation
                 }
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 9e88dc7a913..dcbe89251cb 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -5,9 +5,9 @@ use crate::diagnostics::error::{
     DiagnosticDeriveError,
 };
 use crate::diagnostics::utils::{
-    bind_style_of_field, build_field_mapping, report_error_if_not_applied_to_span,
-    report_type_error, should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo,
-    FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
+    build_field_mapping, report_error_if_not_applied_to_span, report_type_error,
+    should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, FieldInnerTy, FieldMap,
+    HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
 };
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote};
@@ -17,9 +17,9 @@ use syn::{
 use synstructure::{BindingInfo, Structure, VariantInfo};
 
 /// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq)]
 pub(crate) enum DiagnosticDeriveKind {
-    Diagnostic,
+    Diagnostic { handler: syn::Ident },
     LintDiagnostic,
 }
 
@@ -40,6 +40,9 @@ pub(crate) struct DiagnosticDeriveVariantBuilder<'parent> {
     /// The parent builder for the entire type.
     pub parent: &'parent DiagnosticDeriveBuilder,
 
+    /// Initialization of format strings for code suggestions.
+    pub formatting_init: TokenStream,
+
     /// Span of the struct or the enum variant.
     pub span: proc_macro::Span,
 
@@ -88,19 +91,7 @@ impl DiagnosticDeriveBuilder {
             }
         }
 
-        for variant in structure.variants_mut() {
-            // First, change the binding style of each field based on the code that will be
-            // generated for the field - e.g. `set_arg` calls needs by-move bindings, whereas
-            // `set_primary_span` only needs by-ref.
-            variant.bind_with(|bi| bind_style_of_field(bi.ast()).0);
-
-            // Then, perform a stable sort on bindings which generates code for by-ref bindings
-            // before code generated for by-move bindings. Any code generated for the by-ref
-            // bindings which creates a reference to the by-move fields will happen before the
-            // by-move bindings move those fields and make them inaccessible.
-            variant.bindings_mut().sort_by_cached_key(|bi| bind_style_of_field(bi.ast()));
-        }
-
+        structure.bind_with(|_| synstructure::BindStyle::Move);
         let variants = structure.each_variant(|variant| {
             let span = match structure.ast().data {
                 syn::Data::Struct(..) => span,
@@ -112,6 +103,7 @@ impl DiagnosticDeriveBuilder {
                 parent: &self,
                 span,
                 field_map: build_field_mapping(variant),
+                formatting_init: TokenStream::new(),
                 slug: None,
                 code: None,
             };
@@ -143,16 +135,14 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
 
     /// Generates calls to `span_label` and similar functions based on the attributes on fields or
     /// calls to `set_arg` when no attributes are present.
-    ///
-    /// Expects use of `Self::each_variant` which will have sorted bindings so that by-ref bindings
-    /// (which may create references to by-move bindings) have their code generated first -
-    /// necessary as code for suggestions uses formatting machinery and the value of other fields
-    /// (any given field can be referenced multiple times, so must be accessed through a borrow);
-    /// and when passing fields to `add_subdiagnostic` or `set_arg` for Fluent, fields must be
-    /// accessed by-move.
     pub fn body<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream {
         let mut body = quote! {};
-        for binding in variant.bindings() {
+        // Generate `set_arg` calls first..
+        for binding in variant.bindings().iter().filter(|bi| should_generate_set_arg(bi.ast())) {
+            body.extend(self.generate_field_code(binding));
+        }
+        // ..and then subdiagnostic additions.
+        for binding in variant.bindings().iter().filter(|bi| !should_generate_set_arg(bi.ast())) {
             body.extend(self.generate_field_attrs_code(binding));
         }
         body
@@ -274,24 +264,27 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
         }
     }
 
-    fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
+    fn generate_field_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
+        let diag = &self.parent.diag;
+
         let field = binding_info.ast();
         let field_binding = &binding_info.binding;
 
-        if should_generate_set_arg(&field) {
-            let diag = &self.parent.diag;
-            let ident = field.ident.as_ref().unwrap();
-            // strip `r#` prefix, if present
-            let ident = format_ident!("{}", ident);
-            return quote! {
-                #diag.set_arg(
-                    stringify!(#ident),
-                    #field_binding
-                );
-            };
+        let ident = field.ident.as_ref().unwrap();
+        let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
+
+        quote! {
+            #diag.set_arg(
+                stringify!(#ident),
+                #field_binding
+            );
         }
+    }
+
+    fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
+        let field = binding_info.ast();
+        let field_binding = &binding_info.binding;
 
-        let needs_move = bind_style_of_field(&field).is_move();
         let inner_ty = FieldInnerTy::from_type(&field.ty);
 
         field
@@ -304,10 +297,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
                 let (binding, needs_destructure) = if needs_clone {
                     // `primary_span` can accept a `Vec<Span>` so don't destructure that.
                     (quote! { #field_binding.clone() }, false)
-                } else if needs_move {
-                    (quote! { #field_binding }, true)
                 } else {
-                    (quote! { *#field_binding }, true)
+                    (quote! { #field_binding }, true)
                 };
 
                 let generated_code = self
@@ -340,18 +331,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
         let diag = &self.parent.diag;
         let meta = attr.parse_meta()?;
 
-        if let Meta::Path(_) = meta {
-            let ident = &attr.path.segments.last().unwrap().ident;
-            let name = ident.to_string();
-            let name = name.as_str();
-            match name {
-                "skip_arg" => {
-                    // Don't need to do anything - by virtue of the attribute existing, the
-                    // `set_arg` call will not be generated.
-                    return Ok(quote! {});
-                }
-                "primary_span" => match self.parent.kind {
-                    DiagnosticDeriveKind::Diagnostic => {
+        let ident = &attr.path.segments.last().unwrap().ident;
+        let name = ident.to_string();
+        match (&meta, name.as_str()) {
+            // Don't need to do anything - by virtue of the attribute existing, the
+            // `set_arg` call will not be generated.
+            (Meta::Path(_), "skip_arg") => return Ok(quote! {}),
+            (Meta::Path(_), "primary_span") => {
+                match self.parent.kind {
+                    DiagnosticDeriveKind::Diagnostic { .. } => {
                         report_error_if_not_applied_to_span(attr, &info)?;
 
                         return Ok(quote! {
@@ -363,10 +351,50 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
                             diag.help("the `primary_span` field attribute is not valid for lint diagnostics")
                         })
                     }
-                },
-                "subdiagnostic" => return Ok(quote! { #diag.subdiagnostic(#binding); }),
-                _ => {}
+                }
+            }
+            (Meta::Path(_), "subdiagnostic") => {
+                return Ok(quote! { #diag.subdiagnostic(#binding); });
+            }
+            (Meta::NameValue(_), "subdiagnostic") => {
+                throw_invalid_attr!(attr, &meta, |diag| {
+                    diag.help("`eager` is the only supported nested attribute for `subdiagnostic`")
+                })
+            }
+            (Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => {
+                if nested.len() != 1 {
+                    throw_invalid_attr!(attr, &meta, |diag| {
+                        diag.help(
+                            "`eager` is the only supported nested attribute for `subdiagnostic`",
+                        )
+                    })
+                }
+
+                let handler = match &self.parent.kind {
+                    DiagnosticDeriveKind::Diagnostic { handler } => handler,
+                    DiagnosticDeriveKind::LintDiagnostic => {
+                        throw_invalid_attr!(attr, &meta, |diag| {
+                            diag.help("eager subdiagnostics are not supported on lints")
+                        })
+                    }
+                };
+
+                let nested_attr = nested.first().expect("pop failed for single element list");
+                match nested_attr {
+                    NestedMeta::Meta(meta @ Meta::Path(_))
+                        if meta.path().segments.last().unwrap().ident.to_string().as_str()
+                            == "eager" =>
+                    {
+                        return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); });
+                    }
+                    _ => {
+                        throw_invalid_nested_attr!(attr, nested_attr, |diag| {
+                            diag.help("`eager` is the only supported nested attribute for `subdiagnostic`")
+                        })
+                    }
+                }
             }
+            _ => (),
         }
 
         let (subdiag, slug) = self.parse_subdiag_attribute(attr)?;
@@ -389,7 +417,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
             SubdiagnosticKind::Suggestion {
                 suggestion_kind,
                 applicability: static_applicability,
-                code,
+                code_field,
+                code_init,
             } => {
                 let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?;
 
@@ -402,11 +431,12 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
                     .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified });
                 let style = suggestion_kind.to_suggestion_style();
 
+                self.formatting_init.extend(code_init);
                 Ok(quote! {
                     #diag.span_suggestion_with_style(
                         #span_field,
                         rustc_errors::fluent::#slug,
-                        #code,
+                        #code_field,
                         #applicability,
                         #style
                     );
@@ -451,7 +481,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
             // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
             ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
                 let binding = &info.binding.binding;
-                Ok((quote!(*#binding), None))
+                Ok((quote!(#binding), None))
             }
             // If `ty` is `(Span, Applicability)` then return tokens accessing those.
             Type::Tuple(tup) => {
diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs
index 4166816b5e3..f98cc66e9e9 100644
--- a/compiler/rustc_macros/src/diagnostics/mod.rs
+++ b/compiler/rustc_macros/src/diagnostics/mod.rs
@@ -9,7 +9,7 @@ use diagnostic::{DiagnosticDerive, LintDiagnosticDerive};
 pub(crate) use fluent::fluent_messages;
 use proc_macro2::TokenStream;
 use quote::format_ident;
-use subdiagnostic::SubdiagnosticDerive;
+use subdiagnostic::SubdiagnosticDeriveBuilder;
 use synstructure::Structure;
 
 /// Implements `#[derive(Diagnostic)]`, which allows for errors to be specified as a struct,
@@ -155,5 +155,5 @@ pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream {
 /// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident });
 /// ```
 pub fn session_subdiagnostic_derive(s: Structure<'_>) -> TokenStream {
-    SubdiagnosticDerive::new(s).into_tokens()
+    SubdiagnosticDeriveBuilder::new().into_tokens(s)
 }
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 9a2588513f0..3d4c3ab9fd7 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -5,7 +5,7 @@ use crate::diagnostics::error::{
     DiagnosticDeriveError,
 };
 use crate::diagnostics::utils::{
-    build_field_mapping, report_error_if_not_applied_to_applicability,
+    build_field_mapping, new_code_ident, report_error_if_not_applied_to_applicability,
     report_error_if_not_applied_to_span, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce,
     SpannedOption, SubdiagnosticKind,
 };
@@ -15,19 +15,19 @@ use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta
 use synstructure::{BindingInfo, Structure, VariantInfo};
 
 /// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
-pub(crate) struct SubdiagnosticDerive<'a> {
-    structure: Structure<'a>,
+pub(crate) struct SubdiagnosticDeriveBuilder {
     diag: syn::Ident,
+    f: syn::Ident,
 }
 
-impl<'a> SubdiagnosticDerive<'a> {
-    pub(crate) fn new(structure: Structure<'a>) -> Self {
+impl SubdiagnosticDeriveBuilder {
+    pub(crate) fn new() -> Self {
         let diag = format_ident!("diag");
-        Self { structure, diag }
+        let f = format_ident!("f");
+        Self { diag, f }
     }
 
-    pub(crate) fn into_tokens(self) -> TokenStream {
-        let SubdiagnosticDerive { mut structure, diag } = self;
+    pub(crate) fn into_tokens<'a>(self, mut structure: Structure<'a>) -> TokenStream {
         let implementation = {
             let ast = structure.ast();
             let span = ast.span().unwrap();
@@ -53,10 +53,11 @@ impl<'a> SubdiagnosticDerive<'a> {
 
             structure.bind_with(|_| synstructure::BindStyle::Move);
             let variants_ = structure.each_variant(|variant| {
-                let mut builder = SubdiagnosticDeriveBuilder {
-                    diag: &diag,
+                let mut builder = SubdiagnosticDeriveVariantBuilder {
+                    parent: &self,
                     variant,
                     span,
+                    formatting_init: TokenStream::new(),
                     fields: build_field_mapping(variant),
                     span_field: None,
                     applicability: None,
@@ -72,9 +73,17 @@ impl<'a> SubdiagnosticDerive<'a> {
             }
         };
 
+        let diag = &self.diag;
+        let f = &self.f;
         let ret = structure.gen_impl(quote! {
             gen impl rustc_errors::AddToDiagnostic for @Self {
-                fn add_to_diagnostic(self, #diag: &mut rustc_errors::Diagnostic) {
+                fn add_to_diagnostic_with<__F>(self, #diag: &mut rustc_errors::Diagnostic, #f: __F)
+                where
+                    __F: Fn(
+                        &mut rustc_errors::Diagnostic,
+                        rustc_errors::SubdiagnosticMessage
+                    ) -> rustc_errors::SubdiagnosticMessage,
+                {
                     use rustc_errors::{Applicability, IntoDiagnosticArg};
                     #implementation
                 }
@@ -88,15 +97,18 @@ impl<'a> SubdiagnosticDerive<'a> {
 /// for the final generated method. This is a separate struct to `SubdiagnosticDerive`
 /// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
 /// double mut borrow later on.
-struct SubdiagnosticDeriveBuilder<'a> {
+struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
     /// The identifier to use for the generated `DiagnosticBuilder` instance.
-    diag: &'a syn::Ident,
+    parent: &'parent SubdiagnosticDeriveBuilder,
 
     /// Info for the current variant (or the type if not an enum).
     variant: &'a VariantInfo<'a>,
     /// Span for the entire type.
     span: proc_macro::Span,
 
+    /// Initialization of format strings for code suggestions.
+    formatting_init: TokenStream,
+
     /// Store a map of field name to its corresponding field. This is built on construction of the
     /// derive builder.
     fields: FieldMap,
@@ -112,7 +124,7 @@ struct SubdiagnosticDeriveBuilder<'a> {
     has_suggestion_parts: bool,
 }
 
-impl<'a> HasFieldMap for SubdiagnosticDeriveBuilder<'a> {
+impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
     fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
         self.fields.get(field)
     }
@@ -156,7 +168,7 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
     }
 }
 
-impl<'a> SubdiagnosticDeriveBuilder<'a> {
+impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
     fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
         let mut kind_slugs = vec![];
 
@@ -187,7 +199,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
         let ast = binding.ast();
         assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
 
-        let diag = &self.diag;
+        let diag = &self.parent.diag;
         let ident = ast.ident.as_ref().unwrap();
         // strip `r#` prefix, if present
         let ident = format_ident!("{}", ident);
@@ -222,7 +234,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
                 };
 
                 let generated = self
-                    .generate_field_code_inner(kind_stats, attr, info)
+                    .generate_field_code_inner(kind_stats, attr, info, inner_ty.will_iterate())
                     .unwrap_or_else(|v| v.to_compile_error());
 
                 inner_ty.with(binding, generated)
@@ -235,13 +247,18 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
         kind_stats: KindsStatistics,
         attr: &Attribute,
         info: FieldInfo<'_>,
+        clone_suggestion_code: bool,
     ) -> Result<TokenStream, DiagnosticDeriveError> {
         let meta = attr.parse_meta()?;
         match meta {
             Meta::Path(path) => self.generate_field_code_inner_path(kind_stats, attr, info, path),
-            Meta::List(list @ MetaList { .. }) => {
-                self.generate_field_code_inner_list(kind_stats, attr, info, list)
-            }
+            Meta::List(list @ MetaList { .. }) => self.generate_field_code_inner_list(
+                kind_stats,
+                attr,
+                info,
+                list,
+                clone_suggestion_code,
+            ),
             _ => throw_invalid_attr!(attr, &meta),
         }
     }
@@ -345,6 +362,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
         attr: &Attribute,
         info: FieldInfo<'_>,
         list: MetaList,
+        clone_suggestion_code: bool,
     ) -> Result<TokenStream, DiagnosticDeriveError> {
         let span = attr.span().unwrap();
         let ident = &list.path.segments.last().unwrap().ident;
@@ -382,7 +400,8 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
                     match nested_name {
                         "code" => {
                             let formatted_str = self.build_format(&value.value(), value.span());
-                            code.set_once(formatted_str, span);
+                            let code_field = new_code_ident();
+                            code.set_once((code_field, formatted_str), span);
                         }
                         _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
                             diag.help("`code` is the only valid nested attribute")
@@ -390,14 +409,20 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
                     }
                 }
 
-                let Some((code, _)) = code else {
+                let Some((code_field, formatted_str)) = code.value() else {
                     span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
                         .emit();
                     return Ok(quote! {});
                 };
                 let binding = info.binding;
 
-                Ok(quote! { suggestions.push((#binding, #code)); })
+                self.formatting_init.extend(quote! { let #code_field = #formatted_str; });
+                let code_field = if clone_suggestion_code {
+                    quote! { #code_field.clone() }
+                } else {
+                    quote! { #code_field }
+                };
+                Ok(quote! { suggestions.push((#binding, #code_field)); })
             }
             _ => throw_invalid_attr!(attr, &Meta::List(list), |diag| {
                 let mut span_attrs = vec![];
@@ -442,13 +467,23 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
 
         let span_field = self.span_field.value_ref();
 
-        let diag = &self.diag;
+        let diag = &self.parent.diag;
+        let f = &self.parent.f;
         let mut calls = TokenStream::new();
         for (kind, slug) in kind_slugs {
+            let message = format_ident!("__message");
+            calls.extend(quote! { let #message = #f(#diag, rustc_errors::fluent::#slug.into()); });
+
             let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
-            let message = quote! { rustc_errors::fluent::#slug };
             let call = match kind {
-                SubdiagnosticKind::Suggestion { suggestion_kind, applicability, code } => {
+                SubdiagnosticKind::Suggestion {
+                    suggestion_kind,
+                    applicability,
+                    code_init,
+                    code_field,
+                } => {
+                    self.formatting_init.extend(code_init);
+
                     let applicability = applicability
                         .value()
                         .map(|a| quote! { #a })
@@ -457,8 +492,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
 
                     if let Some(span) = span_field {
                         let style = suggestion_kind.to_suggestion_style();
-
-                        quote! { #diag.#name(#span, #message, #code, #applicability, #style); }
+                        quote! { #diag.#name(#span, #message, #code_field, #applicability, #style); }
                     } else {
                         span_err(self.span, "suggestion without `#[primary_span]` field").emit();
                         quote! { unreachable!(); }
@@ -499,6 +533,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
                     }
                 }
             };
+
             calls.extend(call);
         }
 
@@ -510,11 +545,13 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
             .map(|binding| self.generate_field_set_arg(binding))
             .collect();
 
+        let formatting_init = &self.formatting_init;
         Ok(quote! {
             #init
+            #formatting_init
             #attr_args
-            #calls
             #plain_args
+            #calls
         })
     }
 }
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 162699c2868..4fd4adc5112 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -4,16 +4,29 @@ use crate::diagnostics::error::{
 use proc_macro::Span;
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote, ToTokens};
-use std::cmp::Ordering;
+use std::cell::RefCell;
 use std::collections::{BTreeSet, HashMap};
 use std::fmt;
 use std::str::FromStr;
 use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple};
 use syn::{MetaList, MetaNameValue, NestedMeta, Path};
-use synstructure::{BindStyle, BindingInfo, VariantInfo};
+use synstructure::{BindingInfo, VariantInfo};
 
 use super::error::invalid_nested_attr;
 
+thread_local! {
+    pub static CODE_IDENT_COUNT: RefCell<u32> = RefCell::new(0);
+}
+
+/// Returns an ident of the form `__code_N` where `N` is incremented once with every call.
+pub(crate) fn new_code_ident() -> syn::Ident {
+    CODE_IDENT_COUNT.with(|count| {
+        let ident = format_ident!("__code_{}", *count.borrow());
+        *count.borrow_mut() += 1;
+        ident
+    })
+}
+
 /// Checks whether the type name of `ty` matches `name`.
 ///
 /// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or
@@ -142,6 +155,15 @@ impl<'ty> FieldInnerTy<'ty> {
         unreachable!();
     }
 
+    /// Returns `true` if `FieldInnerTy::with` will result in iteration for this inner type (i.e.
+    /// that cloning might be required for values moved in the loop body).
+    pub(crate) fn will_iterate(&self) -> bool {
+        match self {
+            FieldInnerTy::Vec(..) => true,
+            FieldInnerTy::Option(..) | FieldInnerTy::None => false,
+        }
+    }
+
     /// Returns `Option` containing inner type if there is one.
     pub(crate) fn inner_type(&self) -> Option<&'ty Type> {
         match self {
@@ -434,7 +456,12 @@ pub(super) enum SubdiagnosticKind {
     Suggestion {
         suggestion_kind: SuggestionKind,
         applicability: SpannedOption<Applicability>,
-        code: TokenStream,
+        /// Identifier for variable used for formatted code, e.g. `___code_0`. Enables separation
+        /// of formatting and diagnostic emission so that `set_arg` calls can happen in-between..
+        code_field: syn::Ident,
+        /// Initialization logic for `code_field`'s variable, e.g.
+        /// `let __formatted_code = /* whatever */;`
+        code_init: TokenStream,
     },
     /// `#[multipart_suggestion{,_short,_hidden,_verbose}]`
     MultipartSuggestion {
@@ -469,7 +496,8 @@ impl SubdiagnosticKind {
                     SubdiagnosticKind::Suggestion {
                         suggestion_kind,
                         applicability: None,
-                        code: TokenStream::new(),
+                        code_field: new_code_ident(),
+                        code_init: TokenStream::new(),
                     }
                 } else if let Some(suggestion_kind) =
                     name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok())
@@ -548,9 +576,10 @@ impl SubdiagnosticKind {
             };
 
             match (nested_name, &mut kind) {
-                ("code", SubdiagnosticKind::Suggestion { .. }) => {
+                ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => {
                     let formatted_str = fields.build_format(&value.value(), value.span());
-                    code.set_once(formatted_str, span);
+                    let code_init = quote! { let #code_field = #formatted_str; };
+                    code.set_once(code_init, span);
                 }
                 (
                     "applicability",
@@ -582,13 +611,13 @@ impl SubdiagnosticKind {
         }
 
         match kind {
-            SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => {
-                *code_field = if let Some((code, _)) = code {
-                    code
+            SubdiagnosticKind::Suggestion { ref code_field, ref mut code_init, .. } => {
+                *code_init = if let Some(init) = code.value() {
+                    init
                 } else {
                     span_err(span, "suggestion without `code = \"...\"`").emit();
-                    quote! { "" }
-                }
+                    quote! { let #code_field: String = unreachable!(); }
+                };
             }
             SubdiagnosticKind::Label
             | SubdiagnosticKind::Note
@@ -620,65 +649,8 @@ impl quote::IdentFragment for SubdiagnosticKind {
     }
 }
 
-/// Wrapper around `synstructure::BindStyle` which implements `Ord`.
-#[derive(PartialEq, Eq)]
-pub(super) struct OrderedBindStyle(pub(super) BindStyle);
-
-impl OrderedBindStyle {
-    /// Is `BindStyle::Move` or `BindStyle::MoveMut`?
-    pub(super) fn is_move(&self) -> bool {
-        matches!(self.0, BindStyle::Move | BindStyle::MoveMut)
-    }
-}
-
-impl Ord for OrderedBindStyle {
-    fn cmp(&self, other: &Self) -> Ordering {
-        match (self.is_move(), other.is_move()) {
-            // If both `self` and `other` are the same, then ordering is equal.
-            (true, true) | (false, false) => Ordering::Equal,
-            // If `self` is not a move then it should be considered less than `other` (so that
-            // references are sorted first).
-            (false, _) => Ordering::Less,
-            // If `self` is a move then it must be greater than `other` (again, so that references
-            // are sorted first).
-            (true, _) => Ordering::Greater,
-        }
-    }
-}
-
-impl PartialOrd for OrderedBindStyle {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
 /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
 /// call (like `span_label`).
 pub(super) fn should_generate_set_arg(field: &Field) -> bool {
     field.attrs.is_empty()
 }
-
-/// Returns `true` if `field` needs to have code generated in the by-move branch of the
-/// generated derive rather than the by-ref branch.
-pub(super) fn bind_style_of_field(field: &Field) -> OrderedBindStyle {
-    let generates_set_arg = should_generate_set_arg(field);
-    let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]);
-    // FIXME(davidtwco): better support for one field needing to be in the by-move and
-    // by-ref branches.
-    let is_subdiagnostic = field
-        .attrs
-        .iter()
-        .map(|attr| attr.path.segments.last().unwrap().ident.to_string())
-        .any(|attr| attr == "subdiagnostic");
-
-    // `set_arg` calls take their argument by-move..
-    let needs_move = generates_set_arg
-        // If this is a `MultiSpan` field then it needs to be moved to be used by any
-        // attribute..
-        || is_multispan
-        // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is
-        // unlikely to be `Copy`..
-        || is_subdiagnostic;
-
-    OrderedBindStyle(if needs_move { BindStyle::Move } else { BindStyle::Ref })
-}
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 3fc10197b91..68119598285 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1059,6 +1059,43 @@ fn should_encode_const(def_kind: DefKind) -> bool {
     }
 }
 
+fn should_encode_constness(def_kind: DefKind) -> bool {
+    match def_kind {
+        DefKind::Struct
+        | DefKind::Union
+        | DefKind::Enum
+        | DefKind::Trait
+        | DefKind::AssocTy
+        | DefKind::Fn
+        | DefKind::Const
+        | DefKind::Static(..)
+        | DefKind::Ctor(..)
+        | DefKind::AssocFn
+        | DefKind::AssocConst
+        | DefKind::AnonConst
+        | DefKind::InlineConst
+        | DefKind::OpaqueTy
+        | DefKind::ImplTraitPlaceholder
+        | DefKind::Impl
+        | DefKind::Closure
+        | DefKind::Generator
+        | DefKind::TyAlias => true,
+        DefKind::Variant
+        | DefKind::TraitAlias
+        | DefKind::ForeignTy
+        | DefKind::Field
+        | DefKind::TyParam
+        | DefKind::Mod
+        | DefKind::ForeignMod
+        | DefKind::ConstParam
+        | DefKind::Macro(..)
+        | DefKind::Use
+        | DefKind::LifetimeParam
+        | DefKind::GlobalAsm
+        | DefKind::ExternCrate => false,
+    }
+}
+
 fn should_encode_trait_impl_trait_tys<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
     if tcx.def_kind(def_id) != DefKind::AssocFn {
         return false;
@@ -1165,6 +1202,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             {
                 record!(self.tables.trait_impl_trait_tys[def_id] <- table);
             }
+            if should_encode_constness(def_kind) {
+                self.tables.constness.set(def_id.index, tcx.constness(def_id));
+            }
         }
         let inherent_impls = tcx.crate_inherent_impls(());
         for (def_id, implementations) in inherent_impls.inherent_impls.iter() {
@@ -1192,7 +1232,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         };
 
         record!(self.tables.variant_data[def_id] <- data);
-        self.tables.constness.set(def_id.index, hir::Constness::Const);
         record_array!(self.tables.children[def_id] <- variant.fields.iter().map(|f| {
             assert!(f.did.is_local());
             f.did.index
@@ -1220,7 +1259,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         };
 
         record!(self.tables.variant_data[def_id] <- data);
-        self.tables.constness.set(def_id.index, hir::Constness::Const);
         if variant.ctor_kind == CtorKind::Fn {
             record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
         }
@@ -1284,7 +1322,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
         record!(self.tables.repr_options[def_id] <- adt_def.repr());
         record!(self.tables.variant_data[def_id] <- data);
-        self.tables.constness.set(def_id.index, hir::Constness::Const);
         if variant.ctor_kind == CtorKind::Fn {
             record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
         }
@@ -1320,7 +1357,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     }
                 };
                 self.tables.asyncness.set(def_id.index, m_sig.header.asyncness);
-                self.tables.constness.set(def_id.index, hir::Constness::NotConst);
             }
             ty::AssocKind::Type => {
                 self.encode_explicit_item_bounds(def_id);
@@ -1345,13 +1381,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind else { bug!() };
                 self.tables.asyncness.set(def_id.index, sig.header.asyncness);
                 record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
-                // Can be inside `impl const Trait`, so using sig.header.constness is not reliable
-                let constness = if self.tcx.is_const_fn_raw(def_id) {
-                    hir::Constness::Const
-                } else {
-                    hir::Constness::NotConst
-                };
-                self.tables.constness.set(def_id.index, constness);
             }
             ty::AssocKind::Const | ty::AssocKind::Type => {}
         }
@@ -1474,7 +1503,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             hir::ItemKind::Fn(ref sig, .., body) => {
                 self.tables.asyncness.set(def_id.index, sig.header.asyncness);
                 record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
-                self.tables.constness.set(def_id.index, sig.header.constness);
             }
             hir::ItemKind::Macro(ref macro_def, _) => {
                 if macro_def.macro_rules {
@@ -1495,7 +1523,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             hir::ItemKind::Struct(ref struct_def, _) => {
                 let adt_def = self.tcx.adt_def(def_id);
                 record!(self.tables.repr_options[def_id] <- adt_def.repr());
-                self.tables.constness.set(def_id.index, hir::Constness::Const);
 
                 // Encode def_ids for each field and method
                 // for methods, write all the stuff get_trait_method
@@ -1524,9 +1551,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     is_non_exhaustive: variant.is_field_list_non_exhaustive(),
                 });
             }
-            hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
+            hir::ItemKind::Impl(hir::Impl { defaultness, .. }) => {
                 self.tables.impl_defaultness.set(def_id.index, *defaultness);
-                self.tables.constness.set(def_id.index, *constness);
 
                 let trait_ref = self.tcx.impl_trait_ref(def_id);
                 if let Some(trait_ref) = trait_ref {
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index d3a98e43c53..d4258151ff3 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -2946,11 +2946,12 @@ impl Location {
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(BasicBlockData<'_>, 144);
     static_assert_size!(LocalDecl<'_>, 56);
     static_assert_size!(Statement<'_>, 32);
     static_assert_size!(StatementKind<'_>, 16);
     static_assert_size!(Terminator<'_>, 112);
     static_assert_size!(TerminatorKind<'_>, 96);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 9a22a12b93b..85ef51f129b 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1245,10 +1245,11 @@ pub enum BinOp {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(AggregateKind<'_>, 40);
     static_assert_size!(Operand<'_>, 24);
     static_assert_size!(Place<'_>, 16);
     static_assert_size!(PlaceElem<'_>, 24);
     static_assert_size!(Rvalue<'_>, 40);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index f46f0ea4cab..ea7a507d7a4 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -848,7 +848,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 mod size_asserts {
     use super::*;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(Block, 56);
     static_assert_size!(Expr<'_>, 64);
     static_assert_size!(ExprKind<'_>, 40);
@@ -856,4 +856,5 @@ mod size_asserts {
     static_assert_size!(PatKind<'_>, 56);
     static_assert_size!(Stmt<'_>, 48);
     static_assert_size!(StmtKind<'_>, 40);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 753d7bffe84..12d24d6751e 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -2668,8 +2668,9 @@ pub struct DestructuredConst<'tcx> {
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(PredicateS<'_>, 48);
     static_assert_size!(TyS<'_>, 40);
     static_assert_size!(WithStableHash<TyS<'_>>, 56);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 0dc05475ce9..81c051b8f35 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -459,7 +459,8 @@ fn make_token_stream(
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(AttrWrapper, 16);
     static_assert_size!(LazyAttrTokenStreamImpl, 144);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index e82044a89c4..ebcbc75ba32 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1789,20 +1789,25 @@ impl<'a> Parser<'a> {
                 }
             } else {
                 let mut err = self.expected_ident_found();
-                if let Some((ident, _)) = self.token.ident() && ident.as_str() == "let" {
-                    self.bump(); // `let`
-                    let span = self.prev_token.span.until(self.token.span);
+                if self.eat_keyword_noexpect(kw::Let)
+                    && let removal_span = self.prev_token.span.until(self.token.span)
+                    && let Ok(ident) = self.parse_ident_common(false)
+                        // Cancel this error, we don't need it.
+                        .map_err(|err| err.cancel())
+                    && self.token.kind == TokenKind::Colon
+                {
                     err.span_suggestion(
-                        span,
-                        "remove the let, the `let` keyword is not allowed in struct field definitions",
+                        removal_span,
+                        "remove this `let` keyword",
                         String::new(),
                         Applicability::MachineApplicable,
                     );
                     err.note("the `let` keyword is not allowed in `struct` fields");
                     err.note("see <https://doc.rust-lang.org/book/ch05-01-defining-structs.html> for more information");
                     err.emit();
-                    self.bump();
                     return Ok(ident);
+                } else {
+                    self.restore_snapshot(snapshot);
                 }
                 err
             };
diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs
index 8602a4cf5ae..1e74e0e2990 100644
--- a/compiler/rustc_query_system/src/error.rs
+++ b/compiler/rustc_query_system/src/error.rs
@@ -1,19 +1,15 @@
-use rustc_errors::AddToDiagnostic;
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::Limit;
 use rustc_span::{Span, Symbol};
 
+#[derive(Subdiagnostic)]
+#[note(query_system::cycle_stack_middle)]
 pub struct CycleStack {
+    #[primary_span]
     pub span: Span,
     pub desc: String,
 }
 
-impl AddToDiagnostic for CycleStack {
-    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
-        diag.span_note(self.span, &format!("...which requires {}...", self.desc));
-    }
-}
-
 #[derive(Copy, Clone)]
 pub enum HandleCycleError {
     Error,
@@ -53,7 +49,7 @@ pub struct Cycle {
     #[primary_span]
     pub span: Span,
     pub stack_bottom: String,
-    #[subdiagnostic]
+    #[subdiagnostic(eager)]
     pub cycle_stack: Vec<CycleStack>,
     #[subdiagnostic]
     pub stack_count: StackCount,
diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs
index 8f6da73d1f2..f47760e9ae6 100644
--- a/compiler/rustc_query_system/src/lib.rs
+++ b/compiler/rustc_query_system/src/lib.rs
@@ -4,7 +4,7 @@
 #![feature(min_specialization)]
 #![feature(extern_types)]
 #![allow(rustc::potential_query_instability)]
-// #![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 8d527c05122..102df3a4d7e 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1075,12 +1075,11 @@ mod parse {
 options! {
     CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen",
 
-    // This list is in alphabetical order.
-    //
     // If you add a new option, please update:
     // - compiler/rustc_interface/src/tests.rs
     // - src/doc/rustc/src/codegen-options/index.md
 
+    // tidy-alphabetical-start
     ar: String = (String::new(), parse_string, [UNTRACKED],
         "this option is deprecated and does nothing"),
     #[rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field")]
@@ -1195,9 +1194,8 @@ options! {
     target_feature: String = (String::new(), parse_target_feature, [TRACKED],
         "target specific attributes. (`rustc --print target-features` for details). \
         This feature is unsafe."),
+    // tidy-alphabetical-end
 
-    // This list is in alphabetical order.
-    //
     // If you add a new option, please update:
     // - compiler/rustc_interface/src/tests.rs
     // - src/doc/rustc/src/codegen-options/index.md
@@ -1206,24 +1204,23 @@ options! {
 options! {
     UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable",
 
-    // This list is in alphabetical order.
-    //
     // If you add a new option, please update:
     // - compiler/rustc_interface/src/tests.rs
     // - src/doc/unstable-book/src/compiler-flags
 
+    // tidy-alphabetical-start
     allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED],
         "only allow the listed language features to be enabled in code (space separated)"),
     always_encode_mir: bool = (false, parse_bool, [TRACKED],
         "encode MIR of all functions into the crate metadata (default: no)"),
-    assume_incomplete_release: bool = (false, parse_bool, [TRACKED],
-        "make cfg(version) treat the current version as incomplete (default: no)"),
     #[rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field")]
     asm_comments: bool = (false, parse_bool, [TRACKED],
         "generate comments into the assembly (may change behavior) (default: no)"),
     assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "assert that the incremental cache is in given state: \
          either `loaded` or `not-loaded`."),
+    assume_incomplete_release: bool = (false, parse_bool, [TRACKED],
+        "make cfg(version) treat the current version as incomplete (default: no)"),
     #[rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field")]
     binary_dep_depinfo: bool = (false, parse_bool, [TRACKED],
         "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \
@@ -1256,6 +1253,8 @@ options! {
     dep_tasks: bool = (false, parse_bool, [UNTRACKED],
         "print tasks that execute and the color their dep node gets (requires debug build) \
         (default: no)"),
+    diagnostic_width: Option<usize> = (None, parse_opt_number, [UNTRACKED],
+        "set the current output width for diagnostic truncation"),
     dlltool: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
         "import library generation tool (windows-gnu only)"),
     dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
@@ -1337,16 +1336,16 @@ options! {
         "hash spans relative to their parent item for incr. comp. (default: no)"),
     incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED],
         "verify incr. comp. hashes of green query instances (default: no)"),
+    inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED],
+        "control whether `#[inline]` functions are in all CGUs"),
     inline_llvm: bool = (true, parse_bool, [TRACKED],
         "enable LLVM inlining (default: yes)"),
     inline_mir: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "enable MIR inlining (default: no)"),
-    inline_mir_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
-        "a default MIR inlining threshold (default: 50)"),
     inline_mir_hint_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
         "inlining threshold for functions with inline hint (default: 100)"),
-    inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED],
-        "control whether `#[inline]` functions are in all CGUs"),
+    inline_mir_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
+        "a default MIR inlining threshold (default: 50)"),
     input_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather statistics about the input (default: no)"),
     #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")]
@@ -1363,6 +1362,8 @@ options! {
         "insert function instrument code for mcount-based tracing (default: no)"),
     keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
         "keep hygiene data after analysis (default: no)"),
+    layout_seed: Option<u64> = (None, parse_opt_number, [TRACKED],
+        "seed layout randomization"),
     link_native_libraries: bool = (true, parse_bool, [UNTRACKED],
         "link native libraries in the linker invocation (default: yes)"),
     link_only: bool = (false, parse_bool, [TRACKED],
@@ -1392,11 +1393,11 @@ options! {
         "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \
         enabled, overriding all other checks. Passes that are not specified are enabled or \
         disabled by other flags as usual."),
-    mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED],
-        "use line numbers relative to the function in mir pretty printing"),
     #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")]
     mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED],
         "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
+    mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED],
+        "use line numbers relative to the function in mir pretty printing"),
     move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED],
         "the size at which the `large_assignments` lint starts to be emitted"),
     mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED],
@@ -1419,18 +1420,16 @@ options! {
         "compile without linking"),
     no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED],
         "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"),
-    no_unique_section_names: bool = (false, parse_bool, [TRACKED],
-        "do not use unique names for text and data sections when -Z function-sections is used"),
     no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED],
         "prevent automatic injection of the profiler_builtins crate"),
+    no_unique_section_names: bool = (false, parse_bool, [TRACKED],
+        "do not use unique names for text and data sections when -Z function-sections is used"),
     normalize_docs: bool = (false, parse_bool, [TRACKED],
         "normalize associated items in rustdoc when generating documentation"),
     oom: OomStrategy = (OomStrategy::Abort, parse_oom_strategy, [TRACKED],
         "panic strategy for out-of-memory handling"),
     osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
         "pass `-install_name @rpath/...` to the macOS linker (default: no)"),
-    diagnostic_width: Option<usize> = (None, parse_opt_number, [UNTRACKED],
-        "set the current output width for diagnostic truncation"),
     packed_bundled_libs: bool = (false, parse_bool, [TRACKED],
         "change rlib format to store native libraries as archives"),
     panic_abort_tests: bool = (false, parse_bool, [TRACKED],
@@ -1480,25 +1479,20 @@ options! {
     profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
         "file path to emit profiling data at runtime when using 'profile' \
         (default based on relative source path)"),
-    profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED],
-        "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"),
     profile_sample_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
         "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"),
+    profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED],
+        "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"),
     query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
         "enable queries of the dependency graph for regression testing (default: no)"),
     randomize_layout: bool = (false, parse_bool, [TRACKED],
         "randomize the layout of types (default: no)"),
-    layout_seed: Option<u64> = (None, parse_opt_number, [TRACKED],
-        "seed layout randomization"),
     relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "whether ELF relocations can be relaxed"),
     relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],
         "choose which RELRO level to use"),
     remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
         "remap paths under the current working directory to this path prefix"),
-    simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
-        "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \
-        to rust's source base directory. only meant for testing purposes"),
     report_delayed_bugs: bool = (false, parse_bool, [TRACKED],
         "immediately print bugs registered with `delay_span_bug` (default: no)"),
     sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
@@ -1516,27 +1510,41 @@ options! {
     self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled,
         parse_switch_with_opt_path, [UNTRACKED],
         "run the self profiler and output the raw event data"),
-    /// keep this in sync with the event filter names in librustc_data_structures/profiling.rs
-    self_profile_events: Option<Vec<String>> = (None, parse_opt_comma_list, [UNTRACKED],
-        "specify the events recorded by the self profiler;
-        for example: `-Z self-profile-events=default,query-keys`
-        all options: none, all, default, generic-activity, query-provider, query-cache-hit
-                     query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"),
     self_profile_counter: String = ("wall-time".to_string(), parse_string, [UNTRACKED],
         "counter used by the self profiler (default: `wall-time`), one of:
         `wall-time` (monotonic clock, i.e. `std::time::Instant`)
         `instructions:u` (retired instructions, userspace-only)
         `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy)"
     ),
+    /// keep this in sync with the event filter names in librustc_data_structures/profiling.rs
+    self_profile_events: Option<Vec<String>> = (None, parse_opt_comma_list, [UNTRACKED],
+        "specify the events recorded by the self profiler;
+        for example: `-Z self-profile-events=default,query-keys`
+        all options: none, all, default, generic-activity, query-provider, query-cache-hit
+                     query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"),
     share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "make the current crate share its generic instantiations"),
     show_span: Option<String> = (None, parse_opt_string, [TRACKED],
         "show spans for compiler debugging (expr|pat|ty)"),
+    simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
+        "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \
+        to rust's source base directory. only meant for testing purposes"),
     span_debug: bool = (false, parse_bool, [UNTRACKED],
         "forward proc_macro::Span's `Debug` impl to `Span`"),
     /// o/w tests have closure@path
     span_free_formats: bool = (false, parse_bool, [UNTRACKED],
         "exclude spans when debug-printing compiler state (default: no)"),
+    split_dwarf_inlining: bool = (true, parse_bool, [TRACKED],
+        "provide minimal debug info in the object/executable to facilitate online \
+         symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"),
+    split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED],
+        "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform)
+        (default: `split`)
+
+        `split`: sections which do not require relocation are written into a DWARF object (`.dwo`)
+                 file which is ignored by the linker
+        `single`: sections which do not require relocation are written into object file but ignored
+                  by the linker"),
     src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
         "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
     #[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")]
@@ -1546,17 +1554,6 @@ options! {
         "control if mem::uninitialized and mem::zeroed panic on more UB"),
     strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
         "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
-    split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED],
-        "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform)
-        (default: `split`)
-
-        `split`: sections which do not require relocation are written into a DWARF object (`.dwo`)
-                 file which is ignored by the linker
-        `single`: sections which do not require relocation are written into object file but ignored
-                  by the linker"),
-    split_dwarf_inlining: bool = (true, parse_bool, [TRACKED],
-        "provide minimal debug info in the object/executable to facilitate online \
-         symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"),
     symbol_mangling_version: Option<SymbolManglingVersion> = (None,
         parse_symbol_mangling_version, [TRACKED],
         "which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
@@ -1565,17 +1562,6 @@ options! {
         "show extended diagnostic help (default: no)"),
     temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "the directory the intermediate files are written to"),
-    // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved
-    // alongside query results and changes to translation options can affect diagnostics - so
-    // translation options should be tracked.
-    translate_lang: Option<LanguageIdentifier> = (None, parse_opt_langid, [TRACKED],
-        "language identifier for diagnostic output"),
-    translate_additional_ftl: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
-        "additional fluent translation to preferentially use (for testing translation)"),
-    translate_directionality_markers: bool = (false, parse_bool, [TRACKED],
-        "emit directionality isolation markers in translated diagnostics"),
-    tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
-        "select processor to schedule for (`rustc --print target-cpus` for details)"),
     #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")]
     thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "enable ThinLTO when possible"),
@@ -1599,6 +1585,15 @@ options! {
         "choose the TLS model to use (`rustc --print tls-models` for details)"),
     trace_macros: bool = (false, parse_bool, [UNTRACKED],
         "for every macro invocation, print its name and arguments (default: no)"),
+    // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved
+    // alongside query results and changes to translation options can affect diagnostics - so
+    // translation options should be tracked.
+    translate_additional_ftl: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
+        "additional fluent translation to preferentially use (for testing translation)"),
+    translate_directionality_markers: bool = (false, parse_bool, [TRACKED],
+        "emit directionality isolation markers in translated diagnostics"),
+    translate_lang: Option<LanguageIdentifier> = (None, parse_opt_langid, [TRACKED],
+        "language identifier for diagnostic output"),
     translate_remapped_path_to_local_path: bool = (true, parse_bool, [TRACKED],
         "translate remapped paths into local paths when possible (default: yes)"),
     trap_unreachable: Option<bool> = (None, parse_opt_bool, [TRACKED],
@@ -1607,6 +1602,8 @@ options! {
         "treat error number `val` that occurs as bug"),
     trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED],
         "in diagnostics, use heuristics to shorten paths referring to items"),
+    tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
+        "select processor to schedule for (`rustc --print target-cpus` for details)"),
     ui_testing: bool = (false, parse_bool, [UNTRACKED],
         "emit compiler diagnostics in a form suitable for UI testing (default: no)"),
     uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED],
@@ -1647,9 +1644,8 @@ options! {
         Requires `-Clto[=[fat,yes]]`"),
     wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED],
         "whether to build a wasi command or reactor"),
+    // tidy-alphabetical-end
 
-    // This list is in alphabetical order.
-    //
     // If you add a new option, please update:
     // - compiler/rustc_interface/src/tests.rs
 }
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index d2fb8c32ffd..9fe7da3f29e 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -740,7 +740,8 @@ impl<'a, Ty> FnAbi<'a, Ty> {
 mod size_asserts {
     use super::*;
     use rustc_data_structures::static_assert_size;
-    // These are in alphabetical order, which is easy to maintain.
+    // tidy-alphabetical-start
     static_assert_size!(ArgAbi<'_, usize>, 56);
     static_assert_size!(FnAbi<'_, usize>, 80);
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index cd9d2296405..196d70614e7 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -137,77 +137,10 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     let local_did = def_id.as_local();
     let hir_id = local_did.map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id));
 
-    let constness = match hir_id {
-        Some(hir_id) => match tcx.hir().get(hir_id) {
-            hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
-                if tcx.is_const_default_method(def_id) =>
-            {
-                hir::Constness::Const
-            }
-
-            hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(..), .. })
-            | hir::Node::Item(hir::Item { kind: hir::ItemKind::Static(..), .. })
-            | hir::Node::TraitItem(hir::TraitItem {
-                kind: hir::TraitItemKind::Const(..), ..
-            })
-            | hir::Node::AnonConst(_)
-            | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. })
-            | hir::Node::ImplItem(hir::ImplItem {
-                kind:
-                    hir::ImplItemKind::Fn(
-                        hir::FnSig {
-                            header: hir::FnHeader { constness: hir::Constness::Const, .. },
-                            ..
-                        },
-                        ..,
-                    ),
-                ..
-            }) => hir::Constness::Const,
-
-            hir::Node::ImplItem(hir::ImplItem {
-                kind: hir::ImplItemKind::Type(..) | hir::ImplItemKind::Fn(..),
-                ..
-            }) => {
-                let parent_hir_id = tcx.hir().get_parent_node(hir_id);
-                match tcx.hir().get(parent_hir_id) {
-                    hir::Node::Item(hir::Item {
-                        kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
-                        ..
-                    }) => *constness,
-                    _ => span_bug!(
-                        tcx.def_span(parent_hir_id.owner),
-                        "impl item's parent node is not an impl",
-                    ),
-                }
-            }
-
-            hir::Node::Item(hir::Item {
-                kind:
-                    hir::ItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..),
-                ..
-            })
-            | hir::Node::TraitItem(hir::TraitItem {
-                kind:
-                    hir::TraitItemKind::Fn(
-                        hir::FnSig { header: hir::FnHeader { constness, .. }, .. },
-                        ..,
-                    ),
-                ..
-            })
-            | hir::Node::Item(hir::Item {
-                kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
-                ..
-            }) => *constness,
-
-            _ => hir::Constness::NotConst,
-        },
-        None => hir::Constness::NotConst,
-    };
-
     let unnormalized_env = ty::ParamEnv::new(
         tcx.intern_predicates(&predicates),
         traits::Reveal::UserFacing,
-        constness,
+        tcx.constness(def_id),
     );
 
     let body_id =