about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs285
-rw-r--r--compiler/rustc_ast/src/format.rs20
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs259
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs4
-rw-r--r--compiler/rustc_ast/src/visit.rs2235
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs19
-rw-r--r--compiler/rustc_attr_data_structures/src/encode_cross_crate.rs2
-rw-r--r--compiler/rustc_attr_data_structures/src/lib.rs4
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs115
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/must_use.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/stability.rs26
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs14
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs10
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs7
-rw-r--r--compiler/rustc_codegen_gcc/messages.ftl8
-rw-r--r--compiler/rustc_codegen_gcc/src/back/lto.rs88
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs13
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs12
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl10
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs114
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs28
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs17
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs17
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl10
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs134
-rw-r--r--compiler/rustc_codegen_ssa/src/back/lto.rs91
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs184
-rw-r--r--compiler/rustc_codegen_ssa/src/common.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs17
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/constant.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs9
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/write.rs7
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs9
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs8
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs19
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs22
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs5
-rw-r--r--compiler/rustc_const_eval/src/util/caller_location.rs2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0466.md4
-rw-r--r--compiler/rustc_expand/src/base.rs9
-rw-r--r--compiler/rustc_expand/src/lib.rs2
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs16
-rw-r--r--compiler/rustc_hir/src/hir.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs19
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs2
-rw-r--r--compiler/rustc_lint/messages.ftl2
-rw-r--r--compiler/rustc_lint/src/builtin.rs4
-rw-r--r--compiler/rustc_lint/src/if_let_rescope.rs2
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs2
-rw-r--r--compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs4
-rw-r--r--compiler/rustc_lint/src/shadowed_into_iter.rs4
-rw-r--r--compiler/rustc_lint/src/static_mut_refs.rs2
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs26
-rw-r--r--compiler/rustc_macros/src/lib.rs11
-rw-r--r--compiler/rustc_macros/src/visitable.rs82
-rw-r--r--compiler/rustc_metadata/messages.ftl3
-rw-r--r--compiler/rustc_metadata/src/errors.rs7
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs15
-rw-r--r--compiler/rustc_middle/src/hooks/mod.rs4
-rw-r--r--compiler/rustc_middle/src/mir/consts.rs63
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs2
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs6
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs18
-rw-r--r--compiler/rustc_middle/src/mir/query.rs2
-rw-r--r--compiler/rustc_middle/src/query/erase.rs6
-rw-r--r--compiler/rustc_middle/src/query/mod.rs5
-rw-r--r--compiler/rustc_middle/src/ty/context.rs29
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs11
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_constant.rs27
-rw-r--r--compiler/rustc_mir_build/src/builder/mod.rs6
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs38
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs2
-rw-r--r--compiler/rustc_mir_transform/src/impossible_predicates.rs25
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs24
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs15
-rw-r--r--compiler/rustc_parse/src/lexer/diagnostics.rs44
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs18
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs106
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs5
-rw-r--r--compiler/rustc_passes/src/check_attr.rs19
-rw-r--r--compiler/rustc_privacy/src/lib.rs122
-rw-r--r--compiler/rustc_public/src/alloc.rs8
-rw-r--r--compiler/rustc_public_bridge/src/alloc.rs3
-rw-r--r--compiler/rustc_public_bridge/src/context/impls.rs7
-rw-r--r--compiler/rustc_resolve/messages.ftl2
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs97
-rw-r--r--compiler/rustc_resolve/src/errors.rs7
-rw-r--r--compiler/rustc_resolve/src/imports.rs39
-rw-r--r--compiler/rustc_resolve/src/lib.rs36
-rw-r--r--compiler/rustc_resolve/src/macros.rs11
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs17
-rw-r--r--compiler/rustc_session/src/config.rs4
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--compiler/rustc_target/Cargo.toml3
-rw-r--r--compiler/rustc_target/src/json.rs15
-rw-r--r--compiler/rustc_target/src/spec/json.rs1025
-rw-r--r--compiler/rustc_target/src/spec/mod.rs388
-rw-r--r--compiler/rustc_target/src/tests.rs50
102 files changed, 2963 insertions, 3402 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 8c2b521c560..97e07095875 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -28,7 +28,7 @@ use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::tagged_ptr::Tag;
-use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+use rustc_macros::{Decodable, Encodable, HashStable_Generic, Walkable};
 pub use rustc_span::AttrId;
 use rustc_span::source_map::{Spanned, respan};
 use rustc_span::{ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
@@ -39,6 +39,7 @@ use crate::ptr::P;
 use crate::token::{self, CommentKind, Delimiter};
 use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
 use crate::util::parser::{ExprPrecedence, Fixity};
+use crate::visit::{AssocCtxt, BoundKind, LifetimeCtxt};
 
 /// A "Label" is an identifier of some point in sources,
 /// e.g. in the following code:
@@ -50,7 +51,7 @@ use crate::util::parser::{ExprPrecedence, Fixity};
 /// ```
 ///
 /// `'outer` is a label.
-#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic, Eq, PartialEq)]
+#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic, Eq, PartialEq, Walkable)]
 pub struct Label {
     pub ident: Ident,
 }
@@ -63,7 +64,7 @@ impl fmt::Debug for Label {
 
 /// A "Lifetime" is an annotation of the scope in which variable
 /// can be used, e.g. `'a` in `&'a i32`.
-#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq, Hash, Walkable)]
 pub struct Lifetime {
     pub id: NodeId,
     pub ident: Ident,
@@ -87,7 +88,7 @@ impl fmt::Display for Lifetime {
 /// along with a bunch of supporting information.
 ///
 /// E.g., `std::cmp::PartialEq`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Path {
     pub span: Span,
     /// The segments in the path: the things separated by `::`.
@@ -211,7 +212,7 @@ pub fn join_path_idents(path: impl IntoIterator<Item = impl Borrow<Ident>>) -> S
 /// A segment of a path: an identifier, an optional lifetime, and a set of types.
 ///
 /// E.g., `std`, `String` or `Box<T>`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct PathSegment {
     /// The identifier portion of this path segment.
     pub ident: Ident,
@@ -255,7 +256,7 @@ impl PathSegment {
 /// The generic arguments and associated item constraints of a path segment.
 ///
 /// E.g., `<A, B>` as in `Foo<A, B>` or `(A, B)` as in `Foo(A, B)`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum GenericArgs {
     /// The `<'a, A, B, C>` in `foo::bar::baz::<'a, A, B, C>`.
     AngleBracketed(AngleBracketedArgs),
@@ -280,10 +281,10 @@ impl GenericArgs {
 }
 
 /// Concrete argument in the sequence of generic args.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum GenericArg {
     /// `'a` in `Foo<'a>`.
-    Lifetime(Lifetime),
+    Lifetime(#[visitable(extra = LifetimeCtxt::GenericArg)] Lifetime),
     /// `Bar` in `Foo<Bar>`.
     Type(P<Ty>),
     /// `1` in `Foo<1>`.
@@ -301,7 +302,7 @@ impl GenericArg {
 }
 
 /// A path like `Foo<'a, T>`.
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct AngleBracketedArgs {
     /// The overall span.
     pub span: Span,
@@ -310,7 +311,7 @@ pub struct AngleBracketedArgs {
 }
 
 /// Either an argument for a generic parameter or a constraint on an associated item.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum AngleBracketedArg {
     /// A generic argument for a generic parameter.
     Arg(GenericArg),
@@ -340,7 +341,7 @@ impl From<ParenthesizedArgs> for P<GenericArgs> {
 }
 
 /// A path like `Foo(A, B) -> C`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct ParenthesizedArgs {
     /// ```text
     /// Foo(A, B) -> C
@@ -376,7 +377,7 @@ impl ParenthesizedArgs {
 pub use crate::node_id::{CRATE_NODE_ID, DUMMY_NODE_ID, NodeId};
 
 /// Modifiers on a trait bound like `[const]`, `?` and `!`.
-#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Walkable)]
 pub struct TraitBoundModifiers {
     pub constness: BoundConstness,
     pub asyncness: BoundAsyncness,
@@ -391,10 +392,10 @@ impl TraitBoundModifiers {
     };
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum GenericBound {
     Trait(PolyTraitRef),
-    Outlives(Lifetime),
+    Outlives(#[visitable(extra = LifetimeCtxt::Bound)] Lifetime),
     /// Precise capturing syntax: `impl Sized + use<'a>`
     Use(ThinVec<PreciseCapturingArg>, Span),
 }
@@ -429,7 +430,7 @@ impl fmt::Display for ParamKindOrd {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum GenericParamKind {
     /// A lifetime definition (e.g., `'a: 'b + 'c + 'd`).
     Lifetime,
@@ -445,11 +446,12 @@ pub enum GenericParamKind {
     },
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct GenericParam {
     pub id: NodeId,
     pub ident: Ident,
     pub attrs: AttrVec,
+    #[visitable(extra = BoundKind::Bound)]
     pub bounds: GenericBounds,
     pub is_placeholder: bool,
     pub kind: GenericParamKind,
@@ -470,7 +472,7 @@ impl GenericParam {
 
 /// Represents lifetime, type and const parameters attached to a declaration of
 /// a function, enum, trait, etc.
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct Generics {
     pub params: ThinVec<GenericParam>,
     pub where_clause: WhereClause,
@@ -478,7 +480,7 @@ pub struct Generics {
 }
 
 /// A where-clause in a definition.
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct WhereClause {
     /// `true` if we ate a `where` token.
     ///
@@ -496,7 +498,7 @@ impl WhereClause {
 }
 
 /// A single predicate in a where-clause.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct WherePredicate {
     pub attrs: AttrVec,
     pub kind: WherePredicateKind,
@@ -506,7 +508,7 @@ pub struct WherePredicate {
 }
 
 /// Predicate kind in where-clause.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum WherePredicateKind {
     /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
     BoundPredicate(WhereBoundPredicate),
@@ -519,42 +521,45 @@ pub enum WherePredicateKind {
 /// A type bound.
 ///
 /// E.g., `for<'c> Foo: Send + Clone + 'c`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct WhereBoundPredicate {
     /// Any generics from a `for` binding.
     pub bound_generic_params: ThinVec<GenericParam>,
     /// The type being bounded.
     pub bounded_ty: P<Ty>,
     /// Trait and lifetime bounds (`Clone + Send + 'static`).
+    #[visitable(extra = BoundKind::Bound)]
     pub bounds: GenericBounds,
 }
 
 /// A lifetime predicate.
 ///
 /// E.g., `'a: 'b + 'c`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct WhereRegionPredicate {
+    #[visitable(extra = LifetimeCtxt::Bound)]
     pub lifetime: Lifetime,
+    #[visitable(extra = BoundKind::Bound)]
     pub bounds: GenericBounds,
 }
 
 /// An equality predicate (unsupported).
 ///
 /// E.g., `T = int`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct WhereEqPredicate {
     pub lhs_ty: P<Ty>,
     pub rhs_ty: P<Ty>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Crate {
-    pub attrs: AttrVec,
-    pub items: ThinVec<P<Item>>,
-    pub spans: ModSpans,
     /// Must be equal to `CRATE_NODE_ID` after the crate root is expanded, but may hold
     /// expansion placeholders or an unassigned value (`DUMMY_NODE_ID`) before that.
     pub id: NodeId,
+    pub attrs: AttrVec,
+    pub items: ThinVec<P<Item>>,
+    pub spans: ModSpans,
     pub is_placeholder: bool,
 }
 
@@ -608,7 +613,7 @@ pub enum MetaItemInner {
 /// A block (`{ .. }`).
 ///
 /// E.g., `{ .. }` as in `fn foo() { .. }`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Block {
     /// The statements in the block.
     pub stmts: ThinVec<Stmt>,
@@ -622,7 +627,7 @@ pub struct Block {
 /// A match pattern.
 ///
 /// Patterns appear in match statements and some other contexts, such as `let` and `if let`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Pat {
     pub id: NodeId,
     pub kind: PatKind,
@@ -770,7 +775,7 @@ impl From<P<Pat>> for Pat {
 /// Patterns like the fields of `Foo { x, ref y, ref mut z }`
 /// are treated the same as `x: x, y: ref y, z: ref mut z`,
 /// except when `is_shorthand` is true.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct PatField {
     /// The identifier for the field.
     pub ident: Ident,
@@ -784,7 +789,7 @@ pub struct PatField {
 }
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[derive(Encodable, Decodable, HashStable_Generic)]
+#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum ByRef {
     Yes(Mutability),
     No,
@@ -806,7 +811,7 @@ impl ByRef {
 /// `.0` is the by-reference mode (`ref`, `ref mut`, or by value),
 /// `.1` is the mutability of the binding.
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[derive(Encodable, Decodable, HashStable_Generic)]
+#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
 pub struct BindingMode(pub ByRef, pub Mutability);
 
 impl BindingMode {
@@ -829,7 +834,7 @@ impl BindingMode {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum RangeEnd {
     /// `..=` or `...`
     Included(RangeSyntax),
@@ -837,7 +842,7 @@ pub enum RangeEnd {
     Excluded,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum RangeSyntax {
     /// `...`
     DotDotDot,
@@ -848,7 +853,7 @@ pub enum RangeSyntax {
 /// All the different flavors of pattern that Rust recognizes.
 //
 // Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum PatKind {
     /// A missing pattern, e.g. for an anonymous param in a bare fn like `fn f(u32)`.
     Missing,
@@ -930,7 +935,7 @@ pub enum PatKind {
 }
 
 /// Whether the `..` is present in a struct fields pattern.
-#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Walkable)]
 pub enum PatFieldsRest {
     /// `module::StructName { field, ..}`
     Rest,
@@ -943,7 +948,7 @@ pub enum PatFieldsRest {
 /// The kind of borrow in an `AddrOf` expression,
 /// e.g., `&place` or `&raw const place`.
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
-#[derive(Encodable, Decodable, HashStable_Generic)]
+#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum BorrowKind {
     /// A normal borrow, `&$expr` or `&mut $expr`.
     /// The resulting type is either `&'a T` or `&'a mut T`
@@ -959,7 +964,7 @@ pub enum BorrowKind {
     Pin,
 }
 
-#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum BinOpKind {
     /// The `+` operator (addition)
     Add,
@@ -1089,7 +1094,7 @@ impl From<AssignOpKind> for BinOpKind {
     }
 }
 
-#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum AssignOpKind {
     /// The `+=` operator (addition)
     AddAssign,
@@ -1141,7 +1146,7 @@ pub type AssignOp = Spanned<AssignOpKind>;
 /// Unary operator.
 ///
 /// Note that `&data` is not an operator, it's an `AddrOf` expression.
-#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum UnOp {
     /// The `*` operator for dereferencing
     Deref,
@@ -1215,7 +1220,7 @@ impl Stmt {
 }
 
 // Adding a new variant? Please update `test_stmt` in `tests/ui/macros/stringify.rs`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum StmtKind {
     /// A local (let) binding.
     Let(P<Local>),
@@ -1231,7 +1236,7 @@ pub enum StmtKind {
     MacCall(P<MacCallStmt>),
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct MacCallStmt {
     pub mac: P<MacCall>,
     pub style: MacStmtStyle,
@@ -1239,7 +1244,7 @@ pub struct MacCallStmt {
     pub tokens: Option<LazyAttrTokenStream>,
 }
 
-#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, Walkable)]
 pub enum MacStmtStyle {
     /// The macro statement had a trailing semicolon (e.g., `foo! { ... };`
     /// `foo!(...);`, `foo![...];`).
@@ -1253,7 +1258,7 @@ pub enum MacStmtStyle {
 }
 
 /// Local represents a `let` statement, e.g., `let <pat>:<ty> = <expr>;`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Local {
     pub id: NodeId,
     pub super_: Option<Span>,
@@ -1266,7 +1271,7 @@ pub struct Local {
     pub tokens: Option<LazyAttrTokenStream>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum LocalKind {
     /// Local declaration.
     /// Example: `let x;`
@@ -1306,7 +1311,7 @@ impl LocalKind {
 ///     _ => { println!("no match!") },
 /// }
 /// ```
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Arm {
     pub attrs: AttrVec,
     /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`.
@@ -1321,7 +1326,7 @@ pub struct Arm {
 }
 
 /// A single field in a struct expression, e.g. `x: value` and `y` in `Foo { x: value, y }`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct ExprField {
     pub attrs: AttrVec,
     pub id: NodeId,
@@ -1332,13 +1337,13 @@ pub struct ExprField {
     pub is_placeholder: bool,
 }
 
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)]
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Walkable)]
 pub enum BlockCheckMode {
     Default,
     Unsafe(UnsafeSource),
 }
 
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)]
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Walkable)]
 pub enum UnsafeSource {
     CompilerGenerated,
     UserProvided,
@@ -1349,7 +1354,7 @@ pub enum UnsafeSource {
 /// These are usually found nested inside types (e.g., array lengths)
 /// or expressions (e.g., repeat counts), and also used to define
 /// explicit discriminant values for enum variants.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct AnonConst {
     pub id: NodeId,
     pub value: P<Expr>,
@@ -1633,7 +1638,7 @@ impl From<P<Expr>> for Expr {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Closure {
     pub binder: ClosureBinder,
     pub capture_clause: CaptureBy,
@@ -1649,7 +1654,7 @@ pub struct Closure {
 }
 
 /// Limit types of a range (inclusive or exclusive).
-#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug)]
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, Walkable)]
 pub enum RangeLimits {
     /// Inclusive at the beginning, exclusive at the end.
     HalfOpen,
@@ -1680,7 +1685,7 @@ pub struct MethodCall {
     pub span: Span,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum StructRest {
     /// `..x`.
     Base(P<Expr>),
@@ -1690,7 +1695,7 @@ pub enum StructRest {
     None,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct StructExpr {
     pub qself: Option<P<QSelf>>,
     pub path: Path,
@@ -1880,14 +1885,14 @@ pub enum ExprKind {
 }
 
 /// Used to differentiate between `for` loops and `for await` loops.
-#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
 pub enum ForLoopKind {
     For,
     ForAwait,
 }
 
 /// Used to differentiate between `async {}` blocks and `gen {}` blocks.
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
 pub enum GenBlockKind {
     Async,
     Gen,
@@ -1912,7 +1917,7 @@ impl GenBlockKind {
 
 /// Whether we're unwrapping or wrapping an unsafe binder
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(Encodable, Decodable, HashStable_Generic)]
+#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum UnsafeBinderCastKind {
     // e.g. `&i32` -> `unsafe<'a> &'a i32`
     Wrap,
@@ -1934,7 +1939,7 @@ pub enum UnsafeBinderCastKind {
 ///  ^~~~~    ^
 ///  ty       position = 0
 /// ```
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct QSelf {
     pub ty: P<Ty>,
 
@@ -1946,7 +1951,7 @@ pub struct QSelf {
 }
 
 /// A capture clause used in closures and `async` blocks.
-#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub enum CaptureBy {
     /// `move |x| y + x`.
     Value {
@@ -1967,7 +1972,7 @@ pub enum CaptureBy {
 }
 
 /// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum ClosureBinder {
     /// The binder is not present, all closure lifetimes are inferred.
     NotPresent,
@@ -1993,7 +1998,7 @@ pub enum ClosureBinder {
 
 /// Represents a macro invocation. The `path` indicates which macro
 /// is being invoked, and the `args` are arguments passed to it.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct MacCall {
     pub path: Path,
     pub args: P<DelimArgs>,
@@ -2006,7 +2011,7 @@ impl MacCall {
 }
 
 /// Arguments passed to an attribute macro.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum AttrArgs {
     /// No arguments: `#[attr]`.
     Empty,
@@ -2041,7 +2046,7 @@ impl AttrArgs {
 }
 
 /// Delimited arguments, as used in `#[attr()/[]/{}]` or `mac!()/[]/{}`.
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub struct DelimArgs {
     pub dspan: DelimSpan,
     pub delim: Delimiter, // Note: `Delimiter::Invisible` never occurs
@@ -2057,7 +2062,7 @@ impl DelimArgs {
 }
 
 /// Represents a macro definition.
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub struct MacroDef {
     pub body: P<DelimArgs>,
     /// `true` if macro was defined with `macro_rules`.
@@ -2065,7 +2070,7 @@ pub struct MacroDef {
 }
 
 #[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum StrStyle {
     /// A regular string, like `"foo"`.
     Cooked,
@@ -2076,7 +2081,7 @@ pub enum StrStyle {
 }
 
 /// The kind of match expression
-#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Walkable)]
 pub enum MatchKind {
     /// match expr { ... }
     Prefix,
@@ -2085,7 +2090,7 @@ pub enum MatchKind {
 }
 
 /// The kind of yield expression
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum YieldKind {
     /// yield expr { ... }
     Prefix(Option<P<Expr>>),
@@ -2136,7 +2141,7 @@ pub struct MetaItemLit {
 }
 
 /// Similar to `MetaItemLit`, but restricted to string literals.
-#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)]
 pub struct StrLit {
     /// The original literal as written in source code.
     pub symbol: Symbol,
@@ -2265,7 +2270,7 @@ impl LitKind {
 
 // N.B., If you change this, you'll probably want to change the corresponding
 // type structure in `middle/ty.rs` as well.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct MutTy {
     pub ty: P<Ty>,
     pub mutbl: Mutability,
@@ -2389,7 +2394,7 @@ impl UintTy {
 /// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy`
 /// * the `C = { Ct }` in `Trait<C = { Ct }>` (feature `associated_const_equality`)
 /// * the `f(..): Bound` in `Trait<f(..): Bound>` (feature `return_type_notation`)
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct AssocItemConstraint {
     pub id: NodeId,
     pub ident: Ident,
@@ -2398,7 +2403,7 @@ pub struct AssocItemConstraint {
     pub span: Span,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum Term {
     Ty(P<Ty>),
     Const(AnonConst),
@@ -2417,7 +2422,7 @@ impl From<AnonConst> for Term {
 }
 
 /// The kind of [associated item constraint][AssocItemConstraint].
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum AssocItemConstraintKind {
     /// An equality constraint for an associated item (e.g., `AssocTy = Ty` in `Trait<AssocTy = Ty>`).
     ///
@@ -2427,10 +2432,13 @@ pub enum AssocItemConstraintKind {
     /// bindings*. Similarly with associated const equality constraints and *associated const bindings*.
     Equality { term: Term },
     /// A bound on an associated type (e.g., `AssocTy: Bound` in `Trait<AssocTy: Bound>`).
-    Bound { bounds: GenericBounds },
+    Bound {
+        #[visitable(extra = BoundKind::Bound)]
+        bounds: GenericBounds,
+    },
 }
 
-#[derive(Encodable, Decodable, Debug)]
+#[derive(Encodable, Decodable, Debug, Walkable)]
 pub struct Ty {
     pub id: NodeId,
     pub kind: TyKind,
@@ -2474,7 +2482,7 @@ impl Ty {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FnPtrTy {
     pub safety: Safety,
     pub ext: Extern,
@@ -2485,7 +2493,7 @@ pub struct FnPtrTy {
     pub decl_span: Span,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct UnsafeBinderTy {
     pub generic_params: ThinVec<GenericParam>,
     pub inner_ty: P<Ty>,
@@ -2494,7 +2502,7 @@ pub struct UnsafeBinderTy {
 /// The various kinds of type recognized by the compiler.
 //
 // Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum TyKind {
     /// A variable-length slice (`[T]`).
     Slice(P<Ty>),
@@ -2503,11 +2511,11 @@ pub enum TyKind {
     /// A raw pointer (`*const T` or `*mut T`).
     Ptr(MutTy),
     /// A reference (`&'a T` or `&'a mut T`).
-    Ref(Option<Lifetime>, MutTy),
+    Ref(#[visitable(extra = LifetimeCtxt::Ref)] Option<Lifetime>, MutTy),
     /// A pinned reference (`&'a pin const T` or `&'a pin mut T`).
     ///
     /// Desugars into `Pin<&'a T>` or `Pin<&'a mut T>`.
-    PinnedRef(Option<Lifetime>, MutTy),
+    PinnedRef(#[visitable(extra = LifetimeCtxt::Ref)] Option<Lifetime>, MutTy),
     /// A function pointer type (e.g., `fn(usize) -> bool`).
     FnPtr(P<FnPtrTy>),
     /// An unsafe existential lifetime binder (e.g., `unsafe<'a> &'a ()`).
@@ -2523,14 +2531,14 @@ pub enum TyKind {
     Path(Option<P<QSelf>>, Path),
     /// A trait object type `Bound1 + Bound2 + Bound3`
     /// where `Bound` is a trait or a lifetime.
-    TraitObject(GenericBounds, TraitObjectSyntax),
+    TraitObject(#[visitable(extra = BoundKind::TraitObject)] GenericBounds, TraitObjectSyntax),
     /// An `impl Bound1 + Bound2 + Bound3` type
     /// where `Bound` is a trait or a lifetime.
     ///
     /// The `NodeId` exists to prevent lowering from having to
     /// generate `NodeId`s on the fly, which would complicate
     /// the generation of opaque `type Foo = impl Trait` items significantly.
-    ImplTrait(NodeId, GenericBounds),
+    ImplTrait(NodeId, #[visitable(extra = BoundKind::Impl)] GenericBounds),
     /// No-op; kept solely so that we can pretty-print faithfully.
     Paren(P<Ty>),
     /// Unused for now.
@@ -2608,7 +2616,7 @@ impl TyKind {
 }
 
 /// A pattern type pattern.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct TyPat {
     pub id: NodeId,
     pub kind: TyPatKind,
@@ -2619,7 +2627,7 @@ pub struct TyPat {
 /// All the different flavors of pattern that Rust recognizes.
 //
 // Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum TyPatKind {
     /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
     Range(Option<P<AnonConst>>, Option<P<AnonConst>>, Spanned<RangeEnd>),
@@ -2631,7 +2639,7 @@ pub enum TyPatKind {
 }
 
 /// Syntax used to declare a trait object.
-#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 #[repr(u8)]
 pub enum TraitObjectSyntax {
     // SAFETY: When adding new variants make sure to update the `Tag` impl.
@@ -2658,10 +2666,10 @@ unsafe impl Tag for TraitObjectSyntax {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum PreciseCapturingArg {
     /// Lifetime parameter.
-    Lifetime(Lifetime),
+    Lifetime(#[visitable(extra = LifetimeCtxt::GenericArg)] Lifetime),
     /// Type or const parameter.
     Arg(Path, NodeId),
 }
@@ -2669,7 +2677,7 @@ pub enum PreciseCapturingArg {
 /// Inline assembly operand explicit register or register class.
 ///
 /// E.g., `"eax"` as in `asm!("mov eax, 2", out("eax") result)`.
-#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)]
 pub enum InlineAsmRegOrRegClass {
     Reg(Symbol),
     RegClass(Symbol),
@@ -2738,7 +2746,7 @@ impl std::fmt::Debug for InlineAsmOptions {
     }
 }
 
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Hash, HashStable_Generic)]
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Hash, HashStable_Generic, Walkable)]
 pub enum InlineAsmTemplatePiece {
     String(Cow<'static, str>),
     Placeholder { operand_idx: usize, modifier: Option<char>, span: Span },
@@ -2786,7 +2794,7 @@ impl InlineAsmTemplatePiece {
 /// `DefCollector`. Instead this is deferred until AST lowering where we
 /// lower it to an `AnonConst` (for functions) or a `Path` (for statics)
 /// depending on what the path resolves to.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct InlineAsmSym {
     pub id: NodeId,
     pub qself: Option<P<QSelf>>,
@@ -2796,7 +2804,7 @@ pub struct InlineAsmSym {
 /// Inline assembly operand.
 ///
 /// E.g., `out("eax") result` as in `asm!("mov eax, 2", out("eax") result)`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum InlineAsmOperand {
     In {
         reg: InlineAsmRegOrRegClass,
@@ -2841,7 +2849,7 @@ impl InlineAsmOperand {
     }
 }
 
-#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub enum AsmMacro {
     /// The `asm!` macro
     Asm,
@@ -2880,13 +2888,14 @@ impl AsmMacro {
 /// Inline assembly.
 ///
 /// E.g., `asm!("NOP");`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct InlineAsm {
     pub asm_macro: AsmMacro,
     pub template: Vec<InlineAsmTemplatePiece>,
     pub template_strs: Box<[(Symbol, Option<Symbol>, Span)]>,
     pub operands: Vec<(InlineAsmOperand, Span)>,
     pub clobber_abis: Vec<(Symbol, Span)>,
+    #[visitable(ignore)]
     pub options: InlineAsmOptions,
     pub line_spans: Vec<Span>,
 }
@@ -2894,7 +2903,7 @@ pub struct InlineAsm {
 /// A parameter in a function header.
 ///
 /// E.g., `bar: usize` as in `fn foo(bar: usize)`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Param {
     pub attrs: AttrVec,
     pub ty: P<Ty>,
@@ -3022,7 +3031,7 @@ impl Param {
 ///
 /// Please note that it's different from `FnHeader` structure
 /// which contains metadata about function safety, asyncness, constness and ABI.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FnDecl {
     pub inputs: ThinVec<Param>,
     pub output: FnRetTy,
@@ -3038,7 +3047,7 @@ impl FnDecl {
 }
 
 /// Is the trait definition an auto trait?
-#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub enum IsAuto {
     Yes,
     No,
@@ -3046,7 +3055,7 @@ pub enum IsAuto {
 
 /// Safety of items.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum Safety {
     /// `unsafe` an item is explicitly marked as `unsafe`.
     Unsafe(Span),
@@ -3062,7 +3071,7 @@ pub enum Safety {
 /// Coroutine markers are things that cause the function to generate a coroutine, such as `async`,
 /// which makes the function return `impl Future`, or `gen`, which makes the function return `impl
 /// Iterator`.
-#[derive(Copy, Clone, Encodable, Decodable, Debug)]
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum CoroutineKind {
     /// `async`, which returns an `impl Future`.
     Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
@@ -3111,7 +3120,7 @@ impl CoroutineKind {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum Const {
     Yes(Span),
     No,
@@ -3119,13 +3128,13 @@ pub enum Const {
 
 /// Item defaultness.
 /// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532).
-#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
 pub enum Defaultness {
     Default(Span),
     Final,
 }
 
-#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum ImplPolarity {
     /// `impl Trait for Type`
     Positive,
@@ -3144,7 +3153,7 @@ impl fmt::Debug for ImplPolarity {
 
 /// The polarity of a trait bound.
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum BoundPolarity {
     /// `Type: Trait`
     Positive,
@@ -3166,7 +3175,7 @@ impl BoundPolarity {
 
 /// The constness of a trait bound.
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum BoundConstness {
     /// `Type: Trait`
     Never,
@@ -3188,7 +3197,7 @@ impl BoundConstness {
 
 /// The asyncness of a trait bound.
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, Walkable)]
 pub enum BoundAsyncness {
     /// `Type: Trait`
     Normal,
@@ -3205,7 +3214,7 @@ impl BoundAsyncness {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum FnRetTy {
     /// Returns type is not specified.
     ///
@@ -3225,14 +3234,14 @@ impl FnRetTy {
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, Walkable)]
 pub enum Inline {
     Yes,
     No,
 }
 
 /// Module item kind.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum ModKind {
     /// Module with inlined definition `mod foo { ... }`,
     /// or with definition outlined to a separate file `mod foo;` and already loaded from it.
@@ -3243,7 +3252,7 @@ pub enum ModKind {
     Unloaded,
 }
 
-#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct ModSpans {
     /// `inner_span` covers the body of the module; for a file module, its the whole file.
     /// For an inline module, its the span inside the `{ ... }`, not including the curly braces.
@@ -3254,7 +3263,7 @@ pub struct ModSpans {
 /// Foreign module declaration.
 ///
 /// E.g., `extern { .. }` or `extern "C" { .. }`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct ForeignMod {
     /// Span of the `extern` keyword.
     pub extern_span: Span,
@@ -3265,12 +3274,13 @@ pub struct ForeignMod {
     pub items: ThinVec<P<ForeignItem>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct EnumDef {
     pub variants: ThinVec<Variant>,
 }
+
 /// Enum variant.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Variant {
     /// Attributes of the variant.
     pub attrs: AttrVec,
@@ -3292,7 +3302,7 @@ pub struct Variant {
 }
 
 /// Part of `use` item to the right of its prefix.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum UseTreeKind {
     /// `use prefix` or `use prefix as rename`
     Simple(Option<Ident>),
@@ -3311,7 +3321,7 @@ pub enum UseTreeKind {
 
 /// A tree of paths sharing common prefixes.
 /// Used in `use` items both at top-level and inside of braces in import groups.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct UseTree {
     pub prefix: Path,
     pub kind: UseTreeKind,
@@ -3333,7 +3343,7 @@ impl UseTree {
 /// Distinguishes between `Attribute`s that decorate items and Attributes that
 /// are contained as statements within items. These two cases need to be
 /// distinguished for pretty-printing.
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, HashStable_Generic)]
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, HashStable_Generic, Walkable)]
 pub enum AttrStyle {
     Outer,
     Inner,
@@ -3343,7 +3353,7 @@ pub enum AttrStyle {
 pub type AttrVec = ThinVec<Attribute>;
 
 /// A syntax-level representation of an attribute.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Attribute {
     pub kind: AttrKind,
     pub id: AttrId,
@@ -3353,7 +3363,7 @@ pub struct Attribute {
     pub span: Span,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum AttrKind {
     /// A normal attribute.
     Normal(P<NormalAttr>),
@@ -3364,7 +3374,7 @@ pub enum AttrKind {
     DocComment(CommentKind, Symbol),
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct NormalAttr {
     pub item: AttrItem,
     // Tokens for the full attribute, e.g. `#[foo]`, `#![bar]`.
@@ -3385,7 +3395,7 @@ impl NormalAttr {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct AttrItem {
     pub unsafety: Safety,
     pub path: Path,
@@ -3411,20 +3421,20 @@ impl AttrItem {
 /// that the `ref_id` is for. The `impl_id` maps to the "self type" of this impl.
 /// If this impl is an `ItemKind::Impl`, the `impl_id` is redundant (it could be the
 /// same as the impl's `NodeId`).
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct TraitRef {
     pub path: Path,
     pub ref_id: NodeId,
 }
 
 /// Whether enclosing parentheses are present or not.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum Parens {
     Yes,
     No,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct PolyTraitRef {
     /// The `'a` in `for<'a> Foo<&'a T>`.
     pub bound_generic_params: ThinVec<GenericParam>,
@@ -3460,14 +3470,14 @@ impl PolyTraitRef {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Visibility {
     pub kind: VisibilityKind,
     pub span: Span,
     pub tokens: Option<LazyAttrTokenStream>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum VisibilityKind {
     Public,
     Restricted { path: P<Path>, id: NodeId, shorthand: bool },
@@ -3483,7 +3493,7 @@ impl VisibilityKind {
 /// Field definition in a struct, variant or union.
 ///
 /// E.g., `bar: usize` as in `struct Foo { bar: usize }`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FieldDef {
     pub attrs: AttrVec,
     pub id: NodeId,
@@ -3498,14 +3508,14 @@ pub struct FieldDef {
 }
 
 /// Was parsing recovery performed?
-#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)]
+#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub enum Recovered {
     No,
     Yes(ErrorGuaranteed),
 }
 
 /// Fields and constructor ids of enum variants and structs.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum VariantData {
     /// Struct variant.
     ///
@@ -3591,7 +3601,7 @@ impl Item {
 }
 
 /// `extern` qualifier on a function item or function type.
-#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)]
 pub enum Extern {
     /// No explicit extern keyword was used.
     ///
@@ -3622,7 +3632,7 @@ impl Extern {
 ///
 /// All the information between the visibility and the name of the function is
 /// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`).
-#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)]
 pub struct FnHeader {
     /// Whether this is `unsafe`, or has a default safety.
     pub safety: Safety,
@@ -3688,14 +3698,16 @@ impl Default for FnHeader {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Trait {
     pub constness: Const,
     pub safety: Safety,
     pub is_auto: IsAuto,
     pub ident: Ident,
     pub generics: Generics,
+    #[visitable(extra = BoundKind::SuperTraits)]
     pub bounds: GenericBounds,
+    #[visitable(extra = AssocCtxt::Trait)]
     pub items: ThinVec<P<AssocItem>>,
 }
 
@@ -3717,14 +3729,14 @@ pub struct Trait {
 /// ```
 ///
 /// If there is no where clause, then this is `false` with `DUMMY_SP`.
-#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct TyAliasWhereClause {
     pub has_where_token: bool,
     pub span: Span,
 }
 
 /// The span information for the two where clauses on a `TyAlias`.
-#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct TyAliasWhereClauses {
     /// Before the equals sign.
     pub before: TyAliasWhereClause,
@@ -3736,12 +3748,13 @@ pub struct TyAliasWhereClauses {
     pub split: usize,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct TyAlias {
     pub defaultness: Defaultness,
     pub ident: Ident,
     pub generics: Generics,
     pub where_clauses: TyAliasWhereClauses,
+    #[visitable(extra = BoundKind::Bound)]
     pub bounds: GenericBounds,
     pub ty: Option<P<Ty>>,
 }
@@ -3759,7 +3772,7 @@ pub struct Impl {
     pub items: ThinVec<P<AssocItem>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
 pub struct FnContract {
     pub requires: Option<P<Expr>>,
     pub ensures: Option<P<Expr>>,
@@ -3776,7 +3789,7 @@ pub struct Fn {
     pub body: Option<P<Block>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct Delegation {
     /// Path resolution id.
     pub id: NodeId,
@@ -3789,7 +3802,7 @@ pub struct Delegation {
     pub from_glob: bool,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct DelegationMac {
     pub qself: Option<P<QSelf>>,
     pub prefix: Path,
@@ -3798,7 +3811,7 @@ pub struct DelegationMac {
     pub body: Option<P<Block>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct StaticItem {
     pub ident: Ident,
     pub ty: P<Ty>,
@@ -3808,7 +3821,7 @@ pub struct StaticItem {
     pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct ConstItem {
     pub defaultness: Defaultness,
     pub ident: Ident,
diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs
index 28d260419c5..c2a1de60a98 100644
--- a/compiler/rustc_ast/src/format.rs
+++ b/compiler/rustc_ast/src/format.rs
@@ -1,5 +1,5 @@
 use rustc_data_structures::fx::FxHashMap;
-use rustc_macros::{Decodable, Encodable};
+use rustc_macros::{Decodable, Encodable, Walkable};
 use rustc_span::{Ident, Span, Symbol};
 
 use crate::Expr;
@@ -41,7 +41,7 @@ use crate::token::LitKind;
 /// Basically the "AST" for a complete `format_args!()`.
 ///
 /// E.g., `format_args!("hello {name}");`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FormatArgs {
     pub span: Span,
     pub template: Vec<FormatArgsPiece>,
@@ -63,7 +63,7 @@ pub struct FormatArgs {
 /// A piece of a format template string.
 ///
 /// E.g. "hello" or "{name}".
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum FormatArgsPiece {
     Literal(Symbol),
     Placeholder(FormatPlaceholder),
@@ -73,7 +73,7 @@ pub enum FormatArgsPiece {
 ///
 /// E.g. `1, 2, name="ferris", n=3`,
 /// but also implicit captured arguments like `x` in `format_args!("{x}")`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FormatArguments {
     arguments: Vec<FormatArgument>,
     num_unnamed_args: usize,
@@ -144,13 +144,13 @@ impl FormatArguments {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub struct FormatArgument {
     pub kind: FormatArgumentKind,
     pub expr: P<Expr>,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
 pub enum FormatArgumentKind {
     /// `format_args(…, arg)`
     Normal,
@@ -170,24 +170,28 @@ impl FormatArgumentKind {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
 pub struct FormatPlaceholder {
     /// Index into [`FormatArgs::arguments`].
     pub argument: FormatArgPosition,
     /// The span inside the format string for the full `{…}` placeholder.
     pub span: Option<Span>,
     /// `{}`, `{:?}`, or `{:x}`, etc.
+    #[visitable(ignore)]
     pub format_trait: FormatTrait,
     /// `{}` or `{:.5}` or `{:-^20}`, etc.
+    #[visitable(ignore)]
     pub format_options: FormatOptions,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
 pub struct FormatArgPosition {
     /// Which argument this position refers to (Ok),
     /// or would've referred to if it existed (Err).
+    #[visitable(ignore)]
     pub index: Result<usize, usize>,
     /// What kind of position this is. See [`FormatArgPositionKind`].
+    #[visitable(ignore)]
     pub kind: FormatArgPositionKind,
     /// The span of the name or number.
     pub span: Option<Span>,
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 3eae19f4daa..06708e2e703 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -12,14 +12,14 @@ use std::panic;
 
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_span::source_map::Spanned;
-use rustc_span::{Ident, Span};
+use rustc_span::{Ident, Span, Symbol};
 use smallvec::{SmallVec, smallvec};
 use thin_vec::ThinVec;
 
 use crate::ast::*;
 use crate::ptr::P;
 use crate::tokenstream::*;
-use crate::visit::{AssocCtxt, BoundKind, FnCtxt, VisitorResult, try_visit, visit_opt, walk_list};
+use crate::visit::{AssocCtxt, BoundKind, FnCtxt, LifetimeCtxt, VisitorResult, try_visit};
 
 mod sealed {
     use rustc_ast_ir::visit::VisitorResult;
@@ -36,11 +36,249 @@ mod sealed {
 
 use sealed::MutVisitorResult;
 
+pub(crate) trait MutVisitable<V: MutVisitor> {
+    type Extra: Copy;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra);
+}
+
+impl<V: MutVisitor, T: ?Sized> MutVisitable<V> for P<T>
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        (**self).visit_mut(visitor, extra)
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for Option<T>
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        if let Some(this) = self {
+            this.visit_mut(visitor, extra)
+        }
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for Spanned<T>
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        let Spanned { span, node } = self;
+        span.visit_mut(visitor, ());
+        node.visit_mut(visitor, extra);
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for [T]
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        for item in self {
+            item.visit_mut(visitor, extra);
+        }
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for Vec<T>
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        for item in self {
+            item.visit_mut(visitor, extra);
+        }
+    }
+}
+
+impl<V: MutVisitor, T> MutVisitable<V> for (T,)
+where
+    T: MutVisitable<V>,
+{
+    type Extra = T::Extra;
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        self.0.visit_mut(visitor, extra);
+    }
+}
+
+impl<V: MutVisitor, T1, T2> MutVisitable<V> for (T1, T2)
+where
+    T1: MutVisitable<V, Extra = ()>,
+    T2: MutVisitable<V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        self.0.visit_mut(visitor, extra);
+        self.1.visit_mut(visitor, extra);
+    }
+}
+
+impl<V: MutVisitor, T1, T2, T3> MutVisitable<V> for (T1, T2, T3)
+where
+    T1: MutVisitable<V, Extra = ()>,
+    T2: MutVisitable<V, Extra = ()>,
+    T3: MutVisitable<V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        self.0.visit_mut(visitor, extra);
+        self.1.visit_mut(visitor, extra);
+        self.2.visit_mut(visitor, extra);
+    }
+}
+
+impl<V: MutVisitor, T1, T2, T3, T4> MutVisitable<V> for (T1, T2, T3, T4)
+where
+    T1: MutVisitable<V, Extra = ()>,
+    T2: MutVisitable<V, Extra = ()>,
+    T3: MutVisitable<V, Extra = ()>,
+    T4: MutVisitable<V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+        self.0.visit_mut(visitor, extra);
+        self.1.visit_mut(visitor, extra);
+        self.2.visit_mut(visitor, extra);
+        self.3.visit_mut(visitor, extra);
+    }
+}
+
+pub trait MutWalkable<V: MutVisitor> {
+    fn walk_mut(&mut self, visitor: &mut V);
+}
+
+macro_rules! visit_visitable {
+    (mut $visitor:expr, $($expr:expr),* $(,)?) => {{
+        $(MutVisitable::visit_mut($expr, $visitor, ());)*
+    }};
+}
+
+macro_rules! visit_visitable_with {
+    (mut $visitor:expr, $expr:expr, $extra:expr $(,)?) => {
+        MutVisitable::visit_mut($expr, $visitor, $extra)
+    };
+}
+
+macro_rules! walk_walkable {
+    ($visitor:expr, $expr:expr, mut) => {
+        MutWalkable::walk_mut($expr, $visitor)
+    };
+}
+
+macro_rules! impl_visitable {
+    (|&mut $self:ident: $self_ty:ty,
+      $vis:ident: &mut $vis_ty:ident,
+      $extra:ident: $extra_ty:ty| $block:block) => {
+        #[allow(unused_parens, non_local_definitions)]
+        impl<$vis_ty: MutVisitor> MutVisitable<$vis_ty> for $self_ty {
+            type Extra = $extra_ty;
+            fn visit_mut(&mut $self, $vis: &mut $vis_ty, $extra: Self::Extra) -> V::Result {
+                $block
+            }
+        }
+    };
+}
+
+macro_rules! impl_walkable {
+    ($(<$K:ident: $Kb:ident>)? |&mut $self:ident: $self_ty:ty,
+      $vis:ident: &mut $vis_ty:ident| $block:block) => {
+        #[allow(unused_parens, non_local_definitions)]
+        impl<$($K: $Kb,)? $vis_ty: MutVisitor> MutWalkable<$vis_ty> for $self_ty {
+            fn walk_mut(&mut $self, $vis: &mut $vis_ty) -> V::Result {
+                $block
+            }
+        }
+    };
+}
+
+macro_rules! impl_visitable_noop {
+    (<mut> $($ty:ty,)*) => {
+        $(
+            impl_visitable!(|&mut self: $ty, _vis: &mut V, _extra: ()| {});
+        )*
+    };
+}
+
+macro_rules! impl_visitable_list {
+    (<mut> $($ty:ty,)*) => {
+        $(impl<V: MutVisitor, T> MutVisitable<V> for $ty
+        where
+            for<'a> &'a mut $ty: IntoIterator<Item = &'a mut T>,
+            T: MutVisitable<V>,
+        {
+            type Extra = <T as MutVisitable<V>>::Extra;
+
+            #[inline]
+            fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
+                for i in self {
+                    i.visit_mut(visitor, extra);
+                }
+            }
+        })*
+    }
+}
+
+macro_rules! impl_visitable_direct {
+    (<mut> $($ty:ty,)*) => {
+        $(impl_visitable!(
+            |&mut self: $ty, visitor: &mut V, _extra: ()| {
+                MutWalkable::walk_mut(self, visitor)
+            }
+        );)*
+    }
+}
+
+macro_rules! impl_visitable_calling_walkable {
+    (<mut>
+        $( fn $method:ident($ty:ty $(, $extra_name:ident: $extra_ty:ty)?); )*
+    ) => {
+        $(fn $method(&mut self, node: &mut $ty $(, $extra_name:$extra_ty)?) {
+            impl_visitable!(|&mut self: $ty, visitor: &mut V, extra: ($($extra_ty)?)| {
+                let ($($extra_name)?) = extra;
+                visitor.$method(self $(, $extra_name)?);
+            });
+            walk_walkable!(self, node, mut)
+        })*
+    }
+}
+
+macro_rules! define_named_walk {
+    ((mut) $Visitor:ident
+        $( pub fn $method:ident($ty:ty); )*
+    ) => {
+        $(pub fn $method<V: $Visitor>(visitor: &mut V, node: &mut $ty) {
+            walk_walkable!(visitor, node, mut)
+        })*
+    };
+}
+
 super::common_visitor_and_walkers!((mut) MutVisitor);
 
 macro_rules! generate_flat_map_visitor_fns {
     ($($name:ident, $Ty:ty, $flat_map_fn:ident$(, $param:ident: $ParamTy:ty)*;)+) => {
         $(
+            #[allow(unused_parens)]
+            impl<V: MutVisitor> MutVisitable<V> for ThinVec<$Ty> {
+                type Extra = ($($ParamTy),*);
+
+                #[inline]
+                fn visit_mut(
+                    &mut self,
+                    visitor: &mut V,
+                    ($($param),*): Self::Extra,
+                ) -> V::Result {
+                    $name(visitor, self $(, $param)*)
+                }
+            }
+
             fn $name<V: MutVisitor>(
                 vis: &mut V,
                 values: &mut ThinVec<$Ty>,
@@ -78,15 +316,6 @@ pub fn walk_flat_map_pat_field<T: MutVisitor>(
     smallvec![fp]
 }
 
-fn visit_nested_use_tree<V: MutVisitor>(
-    vis: &mut V,
-    nested_tree: &mut UseTree,
-    nested_id: &mut NodeId,
-) {
-    vis.visit_id(nested_id);
-    vis.visit_use_tree(nested_tree);
-}
-
 macro_rules! generate_walk_flat_map_fns {
     ($($fn_name:ident($Ty:ty$(,$extra_name:ident: $ExtraTy:ty)*) => $visit_fn_name:ident;)+) => {$(
         pub fn $fn_name<V: MutVisitor>(vis: &mut V, mut value: $Ty$(,$extra_name: $ExtraTy)*) -> SmallVec<[$Ty; 1]> {
@@ -109,14 +338,6 @@ generate_walk_flat_map_fns! {
     walk_flat_map_assoc_item(P<AssocItem>, ctxt: AssocCtxt) => visit_assoc_item;
 }
 
-fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWhereClauses) {
-    let TyAliasWhereClauses { before, after, split: _ } = tawcs;
-    let TyAliasWhereClause { has_where_token: _, span: span_before } = before;
-    let TyAliasWhereClause { has_where_token: _, span: span_after } = after;
-    vis.visit_span(span_before);
-    vis.visit_span(span_after);
-}
-
 pub fn walk_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Option<P<Expr>> {
     vis.visit_expr(&mut e);
     Some(e)
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index c60185cdde0..e55399adfb8 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -20,7 +20,7 @@ use std::{cmp, fmt, iter, mem};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync;
-use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+use rustc_macros::{Decodable, Encodable, HashStable_Generic, Walkable};
 use rustc_serialize::{Decodable, Encodable};
 use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym};
 use thin_vec::ThinVec;
@@ -977,7 +977,7 @@ impl TokenCursor {
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
+#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
 pub struct DelimSpan {
     pub open: Span,
     pub close: Span,
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index a344f23c345..ab15cb28fa1 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -16,7 +16,7 @@
 pub use rustc_ast_ir::visit::VisitorResult;
 pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list};
 use rustc_span::source_map::Spanned;
-use rustc_span::{Ident, Span};
+use rustc_span::{Ident, Span, Symbol};
 use thin_vec::ThinVec;
 
 use crate::ast::*;
@@ -75,6 +75,241 @@ pub enum LifetimeCtxt {
     GenericArg,
 }
 
+pub(crate) trait Visitable<'a, V: Visitor<'a>> {
+    type Extra: Copy;
+
+    #[must_use]
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result;
+}
+
+impl<'a, V: Visitor<'a>, T: ?Sized> Visitable<'a, V> for P<T>
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        (**self).visit(visitor, extra)
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for Option<T>
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        if let Some(this) = self {
+            try_visit!(this.visit(visitor, extra));
+        }
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for Spanned<T>
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        let Spanned { span: _, node } = self;
+        node.visit(visitor, extra)
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for [T]
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        for item in self {
+            try_visit!(item.visit(visitor, extra));
+        }
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for Vec<T>
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        for item in self {
+            try_visit!(item.visit(visitor, extra));
+        }
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T> Visitable<'a, V> for (T,)
+where
+    T: Visitable<'a, V>,
+{
+    type Extra = T::Extra;
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        self.0.visit(visitor, extra)
+    }
+}
+
+impl<'a, V: Visitor<'a>, T1, T2> Visitable<'a, V> for (T1, T2)
+where
+    T1: Visitable<'a, V, Extra = ()>,
+    T2: Visitable<'a, V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        try_visit!(self.0.visit(visitor, extra));
+        try_visit!(self.1.visit(visitor, extra));
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T1, T2, T3> Visitable<'a, V> for (T1, T2, T3)
+where
+    T1: Visitable<'a, V, Extra = ()>,
+    T2: Visitable<'a, V, Extra = ()>,
+    T3: Visitable<'a, V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        try_visit!(self.0.visit(visitor, extra));
+        try_visit!(self.1.visit(visitor, extra));
+        try_visit!(self.2.visit(visitor, extra));
+        V::Result::output()
+    }
+}
+
+impl<'a, V: Visitor<'a>, T1, T2, T3, T4> Visitable<'a, V> for (T1, T2, T3, T4)
+where
+    T1: Visitable<'a, V, Extra = ()>,
+    T2: Visitable<'a, V, Extra = ()>,
+    T3: Visitable<'a, V, Extra = ()>,
+    T4: Visitable<'a, V, Extra = ()>,
+{
+    type Extra = ();
+    fn visit(&'a self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+        try_visit!(self.0.visit(visitor, extra));
+        try_visit!(self.1.visit(visitor, extra));
+        try_visit!(self.2.visit(visitor, extra));
+        try_visit!(self.3.visit(visitor, extra));
+        V::Result::output()
+    }
+}
+
+pub(crate) trait Walkable<'a, V: Visitor<'a>> {
+    #[must_use]
+    fn walk_ref(&'a self, visitor: &mut V) -> V::Result;
+}
+
+macro_rules! visit_visitable {
+    ($visitor:expr, $($expr:expr),* $(,)?) => {{
+        $(try_visit!(Visitable::visit($expr, $visitor, ()));)*
+    }};
+}
+
+macro_rules! visit_visitable_with {
+    ($visitor:expr, $expr:expr, $extra:expr $(,)?) => {
+        try_visit!(Visitable::visit($expr, $visitor, $extra))
+    };
+}
+
+macro_rules! walk_walkable {
+    ($visitor:expr, $expr:expr, ) => {
+        Walkable::walk_ref($expr, $visitor)
+    };
+}
+
+macro_rules! impl_visitable {
+    (|&$lt:lifetime $self:ident: $self_ty:ty,
+      $vis:ident: &mut $vis_ty:ident,
+      $extra:ident: $extra_ty:ty| $block:block) => {
+        #[allow(unused_parens, non_local_definitions)]
+        impl<$lt, $vis_ty: Visitor<$lt>> Visitable<$lt, $vis_ty> for $self_ty {
+            type Extra = $extra_ty;
+            fn visit(&$lt $self, $vis: &mut $vis_ty, $extra: Self::Extra) -> V::Result {
+                $block
+            }
+        }
+    };
+}
+
+macro_rules! impl_walkable {
+    ($(<$K:ident: $Kb:ident>)? |&$lt:lifetime $self:ident: $self_ty:ty,
+      $vis:ident: &mut $vis_ty:ident| $block:block) => {
+        #[allow(unused_parens, non_local_definitions)]
+        impl<$($K: $Kb,)? $lt, $vis_ty: Visitor<$lt>> Walkable<$lt, $vis_ty> for $self_ty {
+            fn walk_ref(&$lt $self, $vis: &mut $vis_ty) -> V::Result {
+                $block
+            }
+        }
+    };
+}
+
+macro_rules! impl_visitable_noop {
+    (<$lt:lifetime> $($ty:ty,)*) => {
+        $(
+            impl_visitable!(|&$lt self: $ty, _vis: &mut V, _extra: ()| {
+                V::Result::output()
+            });
+        )*
+    };
+}
+
+macro_rules! impl_visitable_list {
+    (<$lt:lifetime> $($ty:ty,)*) => {
+        $(impl<$lt, V: Visitor<$lt>, T> Visitable<$lt, V> for $ty
+        where
+            &$lt $ty: IntoIterator<Item = &$lt T>,
+            T: $lt + Visitable<$lt, V>,
+        {
+            type Extra = <T as Visitable<$lt, V>>::Extra;
+
+            #[inline]
+            fn visit(&$lt self, visitor: &mut V, extra: Self::Extra) -> V::Result {
+                for i in self {
+                    try_visit!(i.visit(visitor, extra));
+                }
+                V::Result::output()
+            }
+        })*
+    };
+}
+
+macro_rules! impl_visitable_direct {
+    (<$lt:lifetime> $($ty:ty,)*) => {
+        $(impl_visitable!(
+            |&$lt self: $ty, visitor: &mut V, _extra: ()| {
+                Walkable::walk_ref(self, visitor)
+            }
+        );)*
+    };
+}
+
+macro_rules! impl_visitable_calling_walkable {
+    (<$lt:lifetime>
+        $( fn $method:ident($ty:ty $(, $extra_name:ident: $extra_ty:ty)?); )*
+    ) => {
+        $(fn $method(&mut self, node: &$lt $ty $(, $extra_name:$extra_ty)?) -> Self::Result {
+            impl_visitable!(|&$lt self: $ty, visitor: &mut V, extra: ($($extra_ty)?)| {
+                let ($($extra_name)?) = extra;
+                visitor.$method(self $(, $extra_name)?)
+            });
+            walk_walkable!(self, node, )
+        })*
+    };
+}
+
+macro_rules! define_named_walk {
+    ($Visitor:ident<$lt:lifetime>
+        $( pub fn $method:ident($ty:ty); )*
+    ) => {
+        $(pub fn $method<$lt, V: $Visitor<$lt>>(visitor: &mut V, node: &$lt $ty) -> V::Result {
+            walk_walkable!(visitor, node,)
+        })*
+    };
+}
+
 #[macro_export]
 macro_rules! common_visitor_and_walkers {
     ($(($mut: ident))? $Visitor:ident$(<$lt:lifetime>)?) => {
@@ -120,6 +355,139 @@ macro_rules! common_visitor_and_walkers {
             }
         }
 
+        // This macro generates `impl Visitable` and `impl MutVisitable` that do nothing.
+        impl_visitable_noop!(<$($lt)? $($mut)?>
+            AttrId,
+            bool,
+            rustc_span::ByteSymbol,
+            char,
+            crate::token::CommentKind,
+            crate::token::Delimiter,
+            crate::token::Lit,
+            crate::token::LitKind,
+            crate::tokenstream::LazyAttrTokenStream,
+            crate::tokenstream::TokenStream,
+            Movability,
+            Mutability,
+            Result<(), rustc_span::ErrorGuaranteed>,
+            rustc_data_structures::fx::FxHashMap<Symbol, usize>,
+            rustc_span::ErrorGuaranteed,
+            std::borrow::Cow<'_, str>,
+            Symbol,
+            u8,
+            usize,
+        );
+        // `Span` is only a no-op for the non-mutable visitor.
+        $(impl_visitable_noop!(<$lt> Span,);)?
+
+        // This macro generates `impl Visitable` and `impl MutVisitable` that simply iterate over
+        // their contents. We do not use a generic impl for `ThinVec` because we want to allow
+        // custom visits for the `MutVisitor`.
+        impl_visitable_list!(<$($lt)? $($mut)?>
+            ThinVec<AngleBracketedArg>,
+            ThinVec<Attribute>,
+            ThinVec<(Ident, Option<Ident>)>,
+            ThinVec<(NodeId, Path)>,
+            ThinVec<PathSegment>,
+            ThinVec<PreciseCapturingArg>,
+            ThinVec<P<Pat>>,
+            ThinVec<P<Ty>>,
+            ThinVec<P<TyPat>>,
+        );
+
+        // This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
+        // or `MutWalkable`. By default, all types that do not have a custom visit method in the
+        // visitor should appear here.
+        impl_visitable_direct!(<$($lt)? $($mut)?>
+            AngleBracketedArg,
+            AngleBracketedArgs,
+            AsmMacro,
+            AssignOpKind,
+            AssocItemConstraintKind,
+            AttrArgs,
+            AttrItem,
+            AttrKind,
+            AttrStyle,
+            FnPtrTy,
+            BindingMode,
+            GenBlockKind,
+            RangeLimits,
+            UnsafeBinderCastKind,
+            BinOpKind,
+            BlockCheckMode,
+            BorrowKind,
+            BoundAsyncness,
+            BoundConstness,
+            BoundPolarity,
+            ByRef,
+            Closure,
+            Const,
+            ConstItem,
+            Defaultness,
+            Delegation,
+            DelegationMac,
+            DelimArgs,
+            DelimSpan,
+            EnumDef,
+            Extern,
+            ForLoopKind,
+            FormatArgPosition,
+            FormatArgsPiece,
+            FormatArgument,
+            FormatArgumentKind,
+            FormatArguments,
+            FormatPlaceholder,
+            GenericParamKind,
+            Impl,
+            ImplPolarity,
+            Inline,
+            InlineAsmOperand,
+            InlineAsmRegOrRegClass,
+            InlineAsmTemplatePiece,
+            IsAuto,
+            LocalKind,
+            MacCallStmt,
+            MacStmtStyle,
+            MatchKind,
+            MethodCall,
+            ModKind,
+            ModSpans,
+            MutTy,
+            NormalAttr,
+            Parens,
+            ParenthesizedArgs,
+            PatFieldsRest,
+            PatKind,
+            RangeEnd,
+            RangeSyntax,
+            Recovered,
+            Safety,
+            StaticItem,
+            StrLit,
+            StrStyle,
+            StructExpr,
+            StructRest,
+            Term,
+            Trait,
+            TraitBoundModifiers,
+            TraitObjectSyntax,
+            TyAlias,
+            TyAliasWhereClause,
+            TyAliasWhereClauses,
+            TyKind,
+            TyPatKind,
+            UnOp,
+            UnsafeBinderTy,
+            UnsafeSource,
+            UseTreeKind,
+            VisibilityKind,
+            WhereBoundPredicate,
+            WhereClause,
+            WhereEqPredicate,
+            WhereRegionPredicate,
+            YieldKind,
+        );
+
         /// Each method of this trait is a hook to be potentially
         /// overridden. Each method's default implementation recursively visits
         /// the substructure of the input via the corresponding `walk` method;
@@ -169,47 +537,82 @@ macro_rules! common_visitor_and_walkers {
             // field access version will continue working and it would be easy to
             // forget to add handling for it.
             fn visit_ident(&mut self, Ident { name: _, span }: &$($lt)? $($mut)? Ident) -> Self::Result {
+                impl_visitable!(|&$($lt)? $($mut)? self: Ident, visitor: &mut V, _extra: ()| {
+                    visitor.visit_ident(self)
+                });
                 visit_span(self, span)
             }
 
-            fn visit_foreign_mod(&mut self, nm: &$($lt)? $($mut)? ForeignMod) -> Self::Result {
-                walk_foreign_mod(self, nm)
-            }
-
-            fn visit_foreign_item(&mut self, i: &$($lt)? $($mut)? ForeignItem) -> Self::Result {
-                walk_item(self, i)
-            }
-
-            fn visit_item(&mut self, i: &$($lt)? $($mut)? Item) -> Self::Result {
-                walk_item(self, i)
-            }
-
-            fn visit_local(&mut self, l: &$($lt)? $($mut)? Local) -> Self::Result {
-                walk_local(self, l)
-            }
-
-            fn visit_block(&mut self, b: &$($lt)? $($mut)? Block) -> Self::Result {
-                walk_block(self, b)
-            }
-
-            fn visit_param(&mut self, param: &$($lt)? $($mut)? Param) -> Self::Result {
-                walk_param(self, param)
-            }
-
-            fn visit_arm(&mut self, a: &$($lt)? $($mut)? Arm) -> Self::Result {
-                walk_arm(self, a)
-            }
-
-            fn visit_pat(&mut self, p: &$($lt)? $($mut)? Pat) -> Self::Result {
-                walk_pat(self, p)
-            }
-
-            fn visit_anon_const(&mut self, c: &$($lt)? $($mut)? AnonConst) -> Self::Result {
-                walk_anon_const(self, c)
-            }
-
-            fn visit_expr(&mut self, ex: &$($lt)? $($mut)? Expr) -> Self::Result {
-                walk_expr(self, ex)
+            // This macro defines a custom visit method for each listed type.
+            // It implements `impl Visitable` and `impl MutVisitable` to call those methods on the
+            // visitor.
+            impl_visitable_calling_walkable!(<$($lt)? $($mut)?>
+                fn visit_anon_const(AnonConst);
+                fn visit_arm(Arm);
+                //fn visit_assoc_item(AssocItem, _ctxt: AssocCtxt);
+                fn visit_assoc_item_constraint(AssocItemConstraint);
+                fn visit_attribute(Attribute);
+                fn visit_block(Block);
+                //fn visit_nested_use_tree((UseTree, NodeId));
+                fn visit_capture_by(CaptureBy);
+                fn visit_closure_binder(ClosureBinder);
+                fn visit_contract(FnContract);
+                fn visit_coroutine_kind(CoroutineKind);
+                fn visit_crate(Crate);
+                fn visit_expr(Expr);
+                fn visit_expr_field(ExprField);
+                fn visit_field_def(FieldDef);
+                fn visit_fn_decl(FnDecl);
+                fn visit_fn_header(FnHeader);
+                fn visit_fn_ret_ty(FnRetTy);
+                //fn visit_foreign_item(ForeignItem);
+                fn visit_foreign_mod(ForeignMod);
+                fn visit_format_args(FormatArgs);
+                fn visit_generic_arg(GenericArg);
+                fn visit_generic_args(GenericArgs);
+                fn visit_generic_param(GenericParam);
+                fn visit_generics(Generics);
+                fn visit_inline_asm(InlineAsm);
+                fn visit_inline_asm_sym(InlineAsmSym);
+                //fn visit_item(Item);
+                fn visit_label(Label);
+                fn visit_lifetime(Lifetime, _ctxt: LifetimeCtxt);
+                fn visit_local(Local);
+                fn visit_mac_call(MacCall);
+                fn visit_macro_def(MacroDef);
+                fn visit_param_bound(GenericBound, _ctxt: BoundKind);
+                fn visit_param(Param);
+                fn visit_pat_field(PatField);
+                fn visit_path(Path);
+                fn visit_path_segment(PathSegment);
+                fn visit_pat(Pat);
+                fn visit_poly_trait_ref(PolyTraitRef);
+                fn visit_precise_capturing_arg(PreciseCapturingArg);
+                fn visit_qself(QSelf);
+                fn visit_trait_ref(TraitRef);
+                fn visit_ty_pat(TyPat);
+                fn visit_ty(Ty);
+                fn visit_use_tree(UseTree);
+                fn visit_variant_data(VariantData);
+                fn visit_variant(Variant);
+                fn visit_vis(Visibility);
+                fn visit_where_predicate_kind(WherePredicateKind);
+                fn visit_where_predicate(WherePredicate);
+            );
+
+            // We want `Visitor` to take the `NodeId` by value.
+            fn visit_id(&mut self, _id: $(&$mut)? NodeId) -> Self::Result {
+                $(impl_visitable!(
+                    |&$lt self: NodeId, visitor: &mut V, _extra: ()| {
+                        visitor.visit_id(*self)
+                    }
+                );)?
+                $(impl_visitable!(
+                    |&$mut self: NodeId, visitor: &mut V, _extra: ()| {
+                        visitor.visit_id(self)
+                    }
+                );)?
+                Self::Result::output()
             }
 
             /// This method is a hack to workaround unstable of `stmt_expr_attributes`.
@@ -218,34 +621,25 @@ macro_rules! common_visitor_and_walkers {
                 self.visit_expr(ex)
             }
 
-            fn visit_ty(&mut self, t: &$($lt)? $($mut)? Ty) -> Self::Result {
-                walk_ty(self, t)
-            }
-
-            fn visit_ty_pat(&mut self, t: &$($lt)? $($mut)? TyPat) -> Self::Result {
-                walk_ty_pat(self, t)
-            }
-
-            fn visit_generic_param(&mut self, param: &$($lt)? $($mut)? GenericParam) -> Self::Result {
-                walk_generic_param(self, param)
-            }
-
-            fn visit_generics(&mut self, g: &$($lt)? $($mut)? Generics) -> Self::Result {
-                walk_generics(self, g)
-            }
-            fn visit_closure_binder(&mut self, b: &$($lt)? $($mut)? ClosureBinder) -> Self::Result {
-                walk_closure_binder(self, b)
-            }
-            fn visit_contract(&mut self, c: &$($lt)? $($mut)? FnContract) -> Self::Result {
-                walk_contract(self, c)
+            fn visit_item(&mut self, item: &$($lt)? $($mut)? Item) -> Self::Result {
+                impl_visitable!(|&$($lt)? $($mut)? self: Item, vis: &mut V, _extra: ()| {
+                    vis.visit_item(self)
+                });
+                walk_item(self, item)
             }
 
-            fn visit_where_predicate(&mut self, p: &$($lt)? $($mut)? WherePredicate) -> Self::Result {
-                walk_where_predicate(self, p)
+            fn visit_foreign_item(&mut self, item: &$($lt)? $($mut)? ForeignItem) -> Self::Result {
+                impl_visitable!(|&$($lt)? $($mut)? self: ForeignItem, vis: &mut V, _extra: ()| {
+                    vis.visit_foreign_item(self)
+                });
+                walk_item(self, item)
             }
 
-            fn visit_where_predicate_kind(&mut self, k: &$($lt)? $($mut)? WherePredicateKind) -> Self::Result {
-                walk_where_predicate_kind(self, k)
+            fn visit_assoc_item(&mut self, item: &$($lt)? $($mut)? AssocItem, ctxt: AssocCtxt) -> Self::Result {
+                impl_visitable!(|&$($lt)? $($mut)? self: AssocItem, vis: &mut V, ctxt: AssocCtxt| {
+                    vis.visit_assoc_item(self, ctxt)
+                });
+                walk_assoc_item(self, item, ctxt)
             }
 
             // for `MutVisitor`: `Span` and `NodeId` are mutated at the caller site.
@@ -258,141 +652,6 @@ macro_rules! common_visitor_and_walkers {
                 walk_fn(self, fk)
             }
 
-            fn visit_assoc_item(&mut self, i: &$($lt)? $($mut)? AssocItem, ctxt: AssocCtxt) -> Self::Result {
-                walk_assoc_item(self, i, ctxt)
-            }
-
-            fn visit_trait_ref(&mut self, t: &$($lt)? $($mut)? TraitRef) -> Self::Result {
-                walk_trait_ref(self, t)
-            }
-
-            fn visit_param_bound(&mut self, bounds: &$($lt)? $($mut)? GenericBound, _ctxt: BoundKind) -> Self::Result {
-                walk_param_bound(self, bounds)
-            }
-
-            fn visit_precise_capturing_arg(&mut self, arg: &$($lt)? $($mut)? PreciseCapturingArg) -> Self::Result {
-                walk_precise_capturing_arg(self, arg)
-            }
-
-            fn visit_poly_trait_ref(&mut self, t: &$($lt)? $($mut)? PolyTraitRef) -> Self::Result {
-                walk_poly_trait_ref(self, t)
-            }
-
-            fn visit_variant_data(&mut self, s: &$($lt)? $($mut)? VariantData) -> Self::Result {
-                walk_variant_data(self, s)
-            }
-
-            fn visit_field_def(&mut self, s: &$($lt)? $($mut)? FieldDef) -> Self::Result {
-                walk_field_def(self, s)
-            }
-
-            fn visit_variant(&mut self, v: &$($lt)? $($mut)? Variant) -> Self::Result {
-                walk_variant(self, v)
-            }
-
-            fn visit_label(&mut self, label: &$($lt)? $($mut)? Label) -> Self::Result {
-                walk_label(self, label)
-            }
-
-            fn visit_lifetime(&mut self, lifetime: &$($lt)? $($mut)? Lifetime, $(${ignore($lt)} _: LifetimeCtxt )?) -> Self::Result {
-                walk_lifetime(self, lifetime)
-            }
-
-            fn visit_mac_call(&mut self, mac: &$($lt)? $($mut)? MacCall) -> Self::Result {
-                walk_mac(self, mac)
-            }
-
-            fn visit_id(&mut self, _id: $(&$mut)? NodeId) -> Self::Result {
-                Self::Result::output()
-            }
-
-            fn visit_macro_def(&mut self, macro_def: &$($lt)? $($mut)? MacroDef) -> Self::Result {
-                walk_macro_def(self, macro_def)
-            }
-
-            fn visit_path(&mut self, path: &$($lt)? $($mut)? Path) -> Self::Result {
-                walk_path(self, path)
-            }
-
-            fn visit_use_tree(&mut self, use_tree: &$($lt)? $($mut)? UseTree) -> Self::Result {
-                walk_use_tree(self, use_tree)
-            }
-
-            fn visit_path_segment(&mut self, path_segment: &$($lt)? $($mut)? PathSegment) -> Self::Result {
-                walk_path_segment(self, path_segment)
-            }
-
-            fn visit_generic_args(&mut self, generic_args: &$($lt)? $($mut)? GenericArgs) -> Self::Result {
-                walk_generic_args(self, generic_args)
-            }
-
-            fn visit_generic_arg(&mut self, generic_arg: &$($lt)? $($mut)? GenericArg) -> Self::Result {
-                walk_generic_arg(self, generic_arg)
-            }
-
-            fn visit_assoc_item_constraint(
-                &mut self,
-                constraint: &$($lt)? $($mut)? AssocItemConstraint,
-            ) -> Self::Result {
-                walk_assoc_item_constraint(self, constraint)
-            }
-
-            fn visit_attribute(&mut self, attr: &$($lt)? $($mut)? Attribute) -> Self::Result {
-                walk_attribute(self, attr)
-            }
-
-            fn visit_vis(&mut self, vis: &$($lt)? $($mut)? Visibility) -> Self::Result {
-                walk_vis(self, vis)
-            }
-
-            fn visit_fn_ret_ty(&mut self, ret_ty: &$($lt)? $($mut)? FnRetTy) -> Self::Result {
-                walk_fn_ret_ty(self, ret_ty)
-            }
-
-            fn visit_fn_header(&mut self, header: &$($lt)? $($mut)? FnHeader) -> Self::Result {
-                walk_fn_header(self, header)
-            }
-
-            fn visit_expr_field(&mut self, f: &$($lt)? $($mut)? ExprField) -> Self::Result {
-                walk_expr_field(self, f)
-            }
-
-            fn visit_pat_field(&mut self, fp: &$($lt)? $($mut)? PatField) -> Self::Result {
-                walk_pat_field(self, fp)
-            }
-
-            fn visit_crate(&mut self, krate: &$($lt)? $($mut)? Crate) -> Self::Result {
-                walk_crate(self, krate)
-            }
-
-            fn visit_inline_asm(&mut self, asm: &$($lt)? $($mut)? InlineAsm) -> Self::Result {
-                walk_inline_asm(self, asm)
-            }
-
-            fn visit_format_args(&mut self, fmt: &$($lt)? $($mut)? FormatArgs) -> Self::Result {
-                walk_format_args(self, fmt)
-            }
-
-            fn visit_inline_asm_sym(&mut self, sym: &$($lt)? $($mut)? InlineAsmSym) -> Self::Result {
-                walk_inline_asm_sym(self, sym)
-            }
-
-            fn visit_capture_by(&mut self, capture_by: &$($lt)? $($mut)? CaptureBy) -> Self::Result {
-                walk_capture_by(self, capture_by)
-            }
-
-            fn visit_coroutine_kind(&mut self, coroutine_kind: &$($lt)? $($mut)? CoroutineKind) -> Self::Result {
-                walk_coroutine_kind(self, coroutine_kind)
-            }
-
-            fn visit_fn_decl(&mut self, fn_decl: &$($lt)? $($mut)? FnDecl) -> Self::Result {
-                walk_fn_decl(self, fn_decl)
-            }
-
-            fn visit_qself(&mut self, qs: &$($lt)? $($mut)? Option<P<QSelf>>) -> Self::Result {
-                walk_qself(self, qs)
-            }
-
             // (non-mut) `Visitor`-only methods
             $(
                 fn visit_stmt(&mut self, s: &$lt Stmt) -> Self::Result {
@@ -407,6 +666,16 @@ macro_rules! common_visitor_and_walkers {
 
             // `MutVisitor`-only methods
             $(
+                // Span visiting is no longer used, but we keep it for now,
+                // in case it's needed for something like #127241.
+                #[inline]
+                fn visit_span(&mut self, _sp: &$mut Span) {
+                    impl_visitable!(|&mut self: Span, visitor: &mut V, _extra: ()| {
+                        visitor.visit_span(self)
+                    });
+                    // Do nothing.
+                }
+
                 fn flat_map_foreign_item(&mut self, ni: P<ForeignItem>) -> SmallVec<[P<ForeignItem>; 1]> {
                     walk_flat_map_foreign_item(self, ni)
                 }
@@ -462,12 +731,6 @@ macro_rules! common_visitor_and_walkers {
                     walk_flat_map_where_predicate(self, where_predicate)
                 }
 
-                // Span visiting is no longer used, but we keep it for now,
-                // in case it's needed for something like #127241.
-                fn visit_span(&mut self, _sp: &$mut Span) {
-                    // Do nothing.
-                }
-
                 fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> {
                     walk_flat_map_pat_field(self, fp)
                 }
@@ -492,148 +755,45 @@ macro_rules! common_visitor_and_walkers {
             #[inline]
         )?
         fn visit_span<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, span: &$($lt)? $($mut)? Span) -> V::Result {
-            $(
-                ${ignore($mut)}
-                vis.visit_span(span);
-            )?
-            V::Result::output()
-        }
-
-        /// helper since `Visitor` wants `NodeId` but `MutVisitor` wants `&mut NodeId`
-        $(${ignore($lt)}
-            #[expect(rustc::pass_by_value)]
-        )?
-        #[inline]
-        fn visit_id<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, id: &$($lt)? $($mut)? NodeId) -> V::Result {
-            // deref `&NodeId` into `NodeId` only for `Visitor`
-            vis.visit_id( $(${ignore($lt)} * )? id)
-        }
-
-        // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier
-        fn visit_safety<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, safety: &$($lt)? $($mut)? Safety) -> V::Result {
-            match safety {
-                Safety::Unsafe(span) => visit_span(vis, span),
-                Safety::Safe(span) => visit_span(vis, span),
-                Safety::Default => { V::Result::output() }
-            }
-        }
-
-        fn visit_constness<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, constness: &$($lt)? $($mut)? Const) -> V::Result {
-            match constness {
-                Const::Yes(span) => visit_span(vis, span),
-                Const::No => {
-                    V::Result::output()
-                }
-            }
-        }
-
-        fn visit_defaultness<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, defaultness: &$($lt)? $($mut)? Defaultness) -> V::Result {
-            match defaultness {
-                Defaultness::Default(span) => visit_span(vis, span),
-                Defaultness::Final => {
-                    V::Result::output()
-                }
-            }
-        }
-
-        fn visit_polarity<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            polarity: &$($lt)? $($mut)? ImplPolarity,
-        ) -> V::Result {
-            match polarity {
-                ImplPolarity::Positive => { V::Result::output() }
-                ImplPolarity::Negative(span) => visit_span(vis, span),
-            }
-        }
-
-        $(${ignore($lt)}
-            #[inline]
-        )?
-        fn visit_modifiers<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            m: &$($lt)? $($mut)? TraitBoundModifiers
-        ) -> V::Result {
-            let TraitBoundModifiers { constness, asyncness, polarity } = m;
-            match constness {
-                BoundConstness::Never => {}
-                BoundConstness::Always(span) | BoundConstness::Maybe(span) => try_visit!(visit_span(vis, span)),
-            }
-            match asyncness {
-                BoundAsyncness::Normal => {}
-                BoundAsyncness::Async(span) => try_visit!(visit_span(vis, span)),
-            }
-            match polarity {
-                BoundPolarity::Positive => {}
-                BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => try_visit!(visit_span(vis, span)),
-            }
+            $(${ignore($mut)} vis.visit_span(span))?;
             V::Result::output()
         }
 
-        $(${ignore($lt)}
-            #[inline]
-        )?
-        fn walk_capture_by<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            capture_by: &$($lt)? $($mut)? CaptureBy
-        ) -> V::Result {
-            match capture_by {
-                CaptureBy::Ref => { V::Result::output() }
-                CaptureBy::Value { move_kw } => {
-                    visit_span(vis, move_kw)
-                }
-                CaptureBy::Use { use_kw } => {
-                    visit_span(vis, use_kw)
-                }
+        $(impl_visitable!(|&$lt self: ThinVec<(UseTree, NodeId)>, vis: &mut V, _extra: ()| {
+            for (nested_tree, nested_id) in self {
+                try_visit!(vis.visit_nested_use_tree(nested_tree, *nested_id));
             }
-        }
-
-        fn visit_bounds<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, bounds: &$($lt)? $($mut)? GenericBounds, ctxt: BoundKind) -> V::Result {
-            walk_list!(visitor, visit_param_bound, bounds, ctxt);
             V::Result::output()
-        }
+        });)?
+        $(impl_visitable_list!(<$mut> ThinVec<(UseTree, NodeId)>,);)?
 
-        pub fn walk_label<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, Label { ident }: &$($lt)? $($mut)? Label) -> V::Result {
-            visitor.visit_ident(ident)
-        }
-
-        pub fn walk_fn_header<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, header: &$($lt)? $($mut)? FnHeader) -> V::Result {
-            let FnHeader { safety, coroutine_kind, constness, ext: _ } = header;
-            try_visit!(visit_constness(visitor, constness));
-            visit_opt!(visitor, visit_coroutine_kind, coroutine_kind);
-            visit_safety(visitor, safety)
-        }
-
-        pub fn walk_lifetime<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, Lifetime { id, ident }: &$($lt)? $($mut)? Lifetime) -> V::Result {
-            try_visit!(visit_id(visitor, id));
-            visitor.visit_ident(ident)
-        }
-
-        fn walk_item_ctxt<$($lt,)? V: $Visitor$(<$lt>)?, K: WalkItemKind>(
+        fn walk_item_inner<$($lt,)? K: WalkItemKind, V: $Visitor$(<$lt>)?>(
             visitor: &mut V,
             item: &$($mut)? $($lt)? Item<K>,
             ctxt: K::Ctxt,
         ) -> V::Result {
             let Item { attrs, id, kind, vis, span, tokens: _ } = item;
-            try_visit!(visit_id(visitor, id));
-            walk_list!(visitor, visit_attribute, attrs);
-            try_visit!(visitor.visit_vis(vis));
+            visit_visitable!($($mut)? visitor, id, attrs, vis);
             try_visit!(kind.walk(*span, *id, vis, ctxt, visitor));
-            visit_span(visitor, span)
+            visit_visitable!($($mut)? visitor, span);
+            V::Result::output()
         }
 
-        pub fn walk_item<$($lt,)? V: $Visitor$(<$lt>)?, K: WalkItemKind<Ctxt = ()>>(
+        // Do not implement `Walkable`/`MutWalkable` for *Item to avoid confusion.
+        pub fn walk_item<$($lt,)? K: WalkItemKind<Ctxt = ()>, V: $Visitor$(<$lt>)?>(
             visitor: &mut V,
             item: &$($mut)? $($lt)? Item<K>,
         ) -> V::Result {
-            walk_item_ctxt(visitor, item, ())
+            walk_item_inner(visitor, item, ())
         }
 
-        pub fn walk_assoc_item<$($lt,)? V: $Visitor$(<$lt>)?>(
+        // Do not implement `Walkable`/`MutWalkable` for *Item to avoid confusion.
+        pub fn walk_assoc_item<$($lt,)? K: WalkItemKind<Ctxt = AssocCtxt>, V: $Visitor$(<$lt>)?>(
             visitor: &mut V,
-            item: &$($mut)? $($lt)? AssocItem,
+            item: &$($mut)? $($lt)? Item<K>,
             ctxt: AssocCtxt,
         ) -> V::Result {
-            walk_item_ctxt(visitor, item, ctxt)
+            walk_item_inner(visitor, item, ctxt)
         }
 
         impl WalkItemKind for ItemKind {
@@ -647,180 +807,52 @@ macro_rules! common_visitor_and_walkers {
                 vis: &mut V,
             ) -> V::Result {
                 match self {
-                    ItemKind::ExternCrate(_orig_name, ident) => vis.visit_ident(ident),
-                    ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree),
-                    ItemKind::Static(box StaticItem {
-                        ident,
-                        ty,
-                        safety: _,
-                        mutability: _,
-                        expr,
-                        define_opaque,
-                    }) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_ty(ty));
-                        visit_opt!(vis, visit_expr, expr);
-                        walk_define_opaques(vis, define_opaque)
-                    }
-                    ItemKind::Const(item) => {
-                        walk_const_item(vis, item)
-                    }
                     ItemKind::Fn(func) => {
                         let kind = FnKind::Fn(FnCtxt::Free, visibility, &$($mut)? *func);
-                        vis.visit_fn(kind, span, id)
-                    }
-                    ItemKind::Mod(safety, ident, mod_kind) => {
-                        try_visit!(visit_safety(vis, safety));
-                        try_visit!(vis.visit_ident(ident));
-                        match mod_kind {
-                            ModKind::Loaded(
-                                items,
-                                _inline,
-                                ModSpans { inner_span, inject_use_span },
-                                _,
-                            ) => {
-                                try_visit!(visit_items(vis, items));
-                                try_visit!(visit_span(vis, inner_span));
-                                try_visit!(visit_span(vis, inject_use_span));
-                            }
-                            ModKind::Unloaded => {}
-                        }
-                        V::Result::output()
-                    }
-                    ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm),
-                    ItemKind::GlobalAsm(asm) => vis.visit_inline_asm(asm),
-                    ItemKind::TyAlias(box TyAlias {
-                        defaultness,
-                        ident,
-                        generics,
-                        $(${ignore($lt)} #[expect(unused)])?
-                        where_clauses,
-                        bounds,
-                        ty,
-                    }) => {
-                        try_visit!(visit_defaultness(vis, defaultness));
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                        visit_opt!(vis, visit_ty, ty);
-                        $(${ignore($mut)}
-                            walk_ty_alias_where_clauses(vis, where_clauses);
-                        )?
-                        V::Result::output()
-                    }
-                    ItemKind::Enum(ident, generics, enum_definition) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        visit_variants(vis, &$($mut)? enum_definition.variants)
+                        try_visit!(vis.visit_fn(kind, span, id));
                     }
+                    ItemKind::ExternCrate(orig_name, ident) =>
+                        visit_visitable!($($mut)? vis, orig_name, ident),
+                    ItemKind::Use(use_tree) =>
+                        visit_visitable!($($mut)? vis, use_tree),
+                    ItemKind::Static(item) =>
+                        visit_visitable!($($mut)? vis, item),
+                    ItemKind::Const(item) =>
+                        visit_visitable!($($mut)? vis, item),
+                    ItemKind::Mod(safety, ident, mod_kind) =>
+                        visit_visitable!($($mut)? vis, safety, ident, mod_kind),
+                    ItemKind::ForeignMod(nm) =>
+                        visit_visitable!($($mut)? vis, nm),
+                    ItemKind::GlobalAsm(asm) =>
+                        visit_visitable!($($mut)? vis, asm),
+                    ItemKind::TyAlias(ty_alias) =>
+                        visit_visitable!($($mut)? vis, ty_alias),
+                    ItemKind::Enum(ident, generics, enum_definition) =>
+                        visit_visitable!($($mut)? vis, ident, generics, enum_definition),
                     ItemKind::Struct(ident, generics, variant_data)
-                    | ItemKind::Union(ident, generics, variant_data) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        vis.visit_variant_data(variant_data)
-                    }
-                    ItemKind::Impl(box Impl {
-                        defaultness,
-                        safety,
-                        generics,
-                        constness,
-                        polarity,
-                        of_trait,
-                        self_ty,
-                        items,
-                    }) => {
-                        try_visit!(visit_defaultness(vis, defaultness));
-                        try_visit!(visit_safety(vis, safety));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_constness(vis, constness));
-                        try_visit!(visit_polarity(vis, polarity));
-                        visit_opt!(vis, visit_trait_ref, of_trait);
-                        try_visit!(vis.visit_ty(self_ty));
-                        visit_assoc_items(vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() })
-                    }
-                    ItemKind::Trait(box Trait { constness, safety, is_auto: _, ident, generics, bounds, items }) => {
-                        try_visit!(visit_constness(vis, constness));
-                        try_visit!(visit_safety(vis, safety));
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                        visit_assoc_items(vis, items, AssocCtxt::Trait)
-                    }
+                    | ItemKind::Union(ident, generics, variant_data) =>
+                        visit_visitable!($($mut)? vis, ident, generics, variant_data),
+                    ItemKind::Impl(impl_) =>
+                        visit_visitable!($($mut)? vis, impl_),
+                    ItemKind::Trait(trait_) =>
+                        visit_visitable!($($mut)? vis, trait_),
                     ItemKind::TraitAlias(ident, generics, bounds) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        visit_bounds(vis, bounds, BoundKind::Bound)
-                    }
-                    ItemKind::MacCall(m) => vis.visit_mac_call(m),
-                    ItemKind::MacroDef(ident, def) => {
-                        try_visit!(vis.visit_ident(ident));
-                        vis.visit_macro_def(def)
-                    }
-                    ItemKind::Delegation(box Delegation {
-                        id,
-                        qself,
-                        path,
-                        ident,
-                        rename,
-                        body,
-                        from_glob: _,
-                    }) => {
-                        try_visit!(visit_id(vis, id));
-                        try_visit!(vis.visit_qself(qself));
-                        try_visit!(vis.visit_path(path));
-                        try_visit!(vis.visit_ident(ident));
-                        visit_opt!(vis, visit_ident, rename);
-                        visit_opt!(vis, visit_block, body);
-                        V::Result::output()
-                    }
-                    ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
-                        try_visit!(vis.visit_qself(qself));
-                        try_visit!(vis.visit_path(prefix));
-                        if let Some(suffixes) = suffixes {
-                            for (ident, rename) in suffixes {
-                                try_visit!(vis.visit_ident(ident));
-                                visit_opt!(vis, visit_ident, rename);
-                            }
-                        }
-                        visit_opt!(vis, visit_block, body);
-                        V::Result::output()
+                        visit_visitable!($($mut)? vis, ident, generics);
+                        visit_visitable_with!($($mut)? vis, bounds, BoundKind::Bound)
                     }
+                    ItemKind::MacCall(m) =>
+                        visit_visitable!($($mut)? vis, m),
+                    ItemKind::MacroDef(ident, def) =>
+                        visit_visitable!($($mut)? vis, ident, def),
+                    ItemKind::Delegation(delegation) =>
+                        visit_visitable!($($mut)? vis, delegation),
+                    ItemKind::DelegationMac(dm) =>
+                        visit_visitable!($($mut)? vis, dm),
                 }
+                V::Result::output()
             }
         }
 
-        fn walk_const_item<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            item: &$($lt)? $($mut)? ConstItem,
-        ) -> V::Result {
-            let ConstItem { defaultness, ident, generics, ty, expr, define_opaque } = item;
-            try_visit!(visit_defaultness(vis, defaultness));
-            try_visit!(vis.visit_ident(ident));
-            try_visit!(vis.visit_generics(generics));
-            try_visit!(vis.visit_ty(ty));
-            visit_opt!(vis, visit_expr, expr);
-            walk_define_opaques(vis, define_opaque)
-        }
-
-        fn walk_foreign_mod<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, foreign_mod: &$($lt)? $($mut)? ForeignMod) -> V::Result {
-            let ForeignMod { extern_span: _, safety, abi: _, items } = foreign_mod;
-            try_visit!(visit_safety(vis, safety));
-            visit_foreign_items(vis, items)
-        }
-
-        fn walk_define_opaques<$($lt,)? V: $Visitor$(<$lt>)?>(
-            visitor: &mut V,
-            define_opaque: &$($lt)? $($mut)? Option<ThinVec<(NodeId, Path)>>,
-        ) -> V::Result {
-            if let Some(define_opaque) = define_opaque {
-                for (id, path) in define_opaque {
-                    try_visit!(visit_id(visitor, id));
-                    try_visit!(visitor.visit_path(path));
-                }
-            }
-            V::Result::output()
-        }
-
         impl WalkItemKind for AssocItemKind {
             type Ctxt = AssocCtxt;
             fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
@@ -832,64 +864,22 @@ macro_rules! common_visitor_and_walkers {
                 vis: &mut V,
             ) -> V::Result {
                 match self {
-                    AssocItemKind::Const(item) => {
-                        walk_const_item(vis, item)
-                    }
+                    AssocItemKind::Const(item) =>
+                        visit_visitable!($($mut)? vis, item),
                     AssocItemKind::Fn(func) => {
-                        vis.visit_fn(FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func), span, id)
-                    }
-                    AssocItemKind::Type(box TyAlias {
-                        generics,
-                        ident,
-                        bounds,
-                        ty,
-                        defaultness,
-                        $(${ignore($lt)} #[expect(unused)])?
-                        where_clauses,
-                    }) => {
-                        try_visit!(visit_defaultness(vis, defaultness));
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                        visit_opt!(vis, visit_ty, ty);
-                        $(${ignore($mut)}
-                            walk_ty_alias_where_clauses(vis, where_clauses);
-                        )?
-                        V::Result::output()
-                    }
-                    AssocItemKind::MacCall(mac) => {
-                        vis.visit_mac_call(mac)
-                    }
-                    AssocItemKind::Delegation(box Delegation {
-                        id,
-                        qself,
-                        path,
-                        ident,
-                        rename,
-                        body,
-                        from_glob: _,
-                    }) => {
-                        try_visit!(visit_id(vis, id));
-                        try_visit!(vis.visit_qself(qself));
-                        try_visit!(vis.visit_path(path));
-                        try_visit!(vis.visit_ident(ident));
-                        visit_opt!(vis, visit_ident, rename);
-                        visit_opt!(vis, visit_block, body);
-                        V::Result::output()
-                    }
-                    AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
-                        try_visit!(vis.visit_qself(qself));
-                        try_visit!(vis.visit_path(prefix));
-                        if let Some(suffixes) = suffixes {
-                            for (ident, rename) in suffixes {
-                                try_visit!(vis.visit_ident(ident));
-                                visit_opt!(vis, visit_ident, rename);
-                            }
-                        }
-                        visit_opt!(vis, visit_block, body);
-                        V::Result::output()
+                        let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func);
+                        try_visit!(vis.visit_fn(kind, span, id))
                     }
+                    AssocItemKind::Type(alias) =>
+                        visit_visitable!($($mut)? vis, alias),
+                    AssocItemKind::MacCall(mac) =>
+                        visit_visitable!($($mut)? vis, mac),
+                    AssocItemKind::Delegation(delegation) =>
+                        visit_visitable!($($mut)? vis, delegation),
+                    AssocItemKind::DelegationMac(dm) =>
+                        visit_visitable!($($mut)? vis, dm),
                 }
+                V::Result::output()
             }
         }
 
@@ -904,545 +894,18 @@ macro_rules! common_visitor_and_walkers {
                 vis: &mut V,
             ) -> V::Result {
                 match self {
-                    ForeignItemKind::Static(box StaticItem {
-                        ident,
-                        ty,
-                        mutability: _,
-                        expr,
-                        safety: _,
-                        define_opaque,
-                    }) => {
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_ty(ty));
-                        visit_opt!(vis, visit_expr, expr);
-                        walk_define_opaques(vis, define_opaque)
-                    }
+                    ForeignItemKind::Static(item) =>
+                        visit_visitable!($($mut)? vis, item),
                     ForeignItemKind::Fn(func) => {
-                        vis.visit_fn(FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func), span, id)
-                    }
-                    ForeignItemKind::TyAlias(box TyAlias {
-                        defaultness,
-                        ident,
-                        generics,
-                        bounds,
-                        ty,
-                        $(${ignore($lt)} #[expect(unused)])?
-                        where_clauses,
-                    }) => {
-                        try_visit!(visit_defaultness(vis, defaultness));
-                        try_visit!(vis.visit_ident(ident));
-                        try_visit!(vis.visit_generics(generics));
-                        try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                        visit_opt!(vis, visit_ty, ty);
-                        $(${ignore($mut)}
-                            walk_ty_alias_where_clauses(vis, where_clauses);
-                        )?
-                        V::Result::output()
-                    }
-                    ForeignItemKind::MacCall(mac) => {
-                        vis.visit_mac_call(mac)
-                    }
-                }
-            }
-        }
-
-        fn walk_coroutine_kind<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            coroutine_kind: &$($lt)? $($mut)? CoroutineKind,
-        ) -> V::Result {
-            let (CoroutineKind::Async { span, closure_id, return_impl_trait_id }
-                | CoroutineKind::Gen { span, closure_id, return_impl_trait_id }
-                | CoroutineKind::AsyncGen { span, closure_id, return_impl_trait_id })
-                = coroutine_kind;
-            try_visit!(visit_id(vis, closure_id));
-            try_visit!(visit_id(vis, return_impl_trait_id));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_pat<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            pattern: &$($lt)? $($mut)? Pat
-        ) -> V::Result {
-            let Pat { id, kind, span, tokens: _ } = pattern;
-            try_visit!(visit_id(vis, id));
-            match kind {
-                PatKind::Err(_guar) => {}
-                PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {}
-                PatKind::Ident(_bmode, ident, optional_subpattern) => {
-                    try_visit!(vis.visit_ident(ident));
-                    visit_opt!(vis, visit_pat, optional_subpattern);
-                }
-                PatKind::Expr(expression) => try_visit!(vis.visit_expr(expression)),
-                PatKind::TupleStruct(opt_qself, path, elems) => {
-                    try_visit!(vis.visit_qself(opt_qself));
-                    try_visit!(vis.visit_path(path));
-                    walk_list!(vis, visit_pat, elems);
-                }
-                PatKind::Path(opt_qself, path) => {
-                    try_visit!(vis.visit_qself(opt_qself));
-                    try_visit!(vis.visit_path(path))
-                }
-                PatKind::Struct(opt_qself, path, fields, _rest) => {
-                    try_visit!(vis.visit_qself(opt_qself));
-                    try_visit!(vis.visit_path(path));
-                    try_visit!(visit_pat_fields(vis, fields));
-                }
-                PatKind::Box(subpattern) | PatKind::Deref(subpattern) | PatKind::Paren(subpattern) => {
-                    try_visit!(vis.visit_pat(subpattern));
-                }
-                PatKind::Ref(subpattern, _ /*mutbl*/) => {
-                    try_visit!(vis.visit_pat(subpattern));
-                }
-                PatKind::Range(lower_bound, upper_bound, _end) => {
-                    visit_opt!(vis, visit_expr, lower_bound);
-                    visit_opt!(vis, visit_expr, upper_bound);
-                    try_visit!(visit_span(vis, span));
-                }
-                PatKind::Guard(subpattern, guard_condition) => {
-                    try_visit!(vis.visit_pat(subpattern));
-                    try_visit!(vis.visit_expr(guard_condition));
-                }
-                PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
-                    walk_list!(vis, visit_pat, elems);
-                }
-                PatKind::MacCall(mac) => try_visit!(vis.visit_mac_call(mac)),
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_anon_const<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            constant: &$($lt)? $($mut)? AnonConst,
-        ) -> V::Result {
-            let AnonConst { id, value } = constant;
-            try_visit!(visit_id(vis, id));
-            vis.visit_expr(value)
-        }
-
-        pub fn walk_path_segment<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            segment: &$($lt)? $($mut)? PathSegment,
-        ) -> V::Result {
-            let PathSegment { ident, id, args } = segment;
-            try_visit!(visit_id(vis, id));
-            try_visit!(vis.visit_ident(ident));
-            visit_opt!(vis, visit_generic_args, args);
-            V::Result::output()
-        }
-
-        pub fn walk_block<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            block: &$($lt)? $($mut)? Block
-        ) -> V::Result {
-            let Block { stmts, id, rules: _, span, tokens: _ } = block;
-            try_visit!(visit_id(vis, id));
-            try_visit!(visit_stmts(vis, stmts));
-            visit_span(vis, span)
-        }
-
-
-        pub fn walk_ty<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V, ty: &$($lt)? $($mut)? Ty
-        ) -> V::Result {
-            let Ty { id, kind, span, tokens: _ } = ty;
-            try_visit!(visit_id(vis, id));
-            match kind {
-                TyKind::Err(_guar) => {}
-                TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Never | TyKind::CVarArgs => {}
-                TyKind::Slice(ty) | TyKind::Paren(ty) => try_visit!(vis.visit_ty(ty)),
-                TyKind::Ptr(MutTy { ty, mutbl: _ }) => try_visit!(vis.visit_ty(ty)),
-                TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ })
-                | TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl: _ }) => {
-                    // FIXME(fee1-dead) asymmetry
-                    visit_opt!(vis, visit_lifetime, opt_lifetime$(${ignore($lt)}, LifetimeCtxt::Ref)?);
-                    try_visit!(vis.visit_ty(ty));
-                }
-                TyKind::Tup(tuple_element_types) => {
-                    walk_list!(vis, visit_ty, tuple_element_types);
-                }
-                TyKind::FnPtr(function_declaration) => {
-                    let FnPtrTy { safety, ext: _, generic_params, decl, decl_span } =
-                        &$($mut)? **function_declaration;
-                    try_visit!(visit_safety(vis, safety));
-                    try_visit!(visit_generic_params(vis, generic_params));
-                    try_visit!(vis.visit_fn_decl(decl));
-                    try_visit!(visit_span(vis, decl_span));
-                }
-                TyKind::UnsafeBinder(binder) => {
-                    try_visit!(visit_generic_params(vis, &$($mut)? binder.generic_params));
-                    try_visit!(vis.visit_ty(&$($mut)? binder.inner_ty));
-                }
-                TyKind::Path(maybe_qself, path) => {
-                    try_visit!(vis.visit_qself(maybe_qself));
-                    try_visit!(vis.visit_path(path));
-                }
-                TyKind::Pat(ty, pat) => {
-                    try_visit!(vis.visit_ty(ty));
-                    try_visit!(vis.visit_ty_pat(pat));
-                }
-                TyKind::Array(ty, length) => {
-                    try_visit!(vis.visit_ty(ty));
-                    try_visit!(vis.visit_anon_const(length));
-                }
-                TyKind::TraitObject(bounds, _syntax) => {
-                    walk_list!(vis, visit_param_bound, bounds, BoundKind::TraitObject);
-                }
-                TyKind::ImplTrait(id, bounds) => {
-                    try_visit!(visit_id(vis, id));
-                    walk_list!(vis, visit_param_bound, bounds, BoundKind::Impl);
-                }
-                TyKind::Typeof(expression) => try_visit!(vis.visit_anon_const(expression)),
-
-                TyKind::MacCall(mac) => try_visit!(vis.visit_mac_call(mac)),
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_crate<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            krate: &$($lt)? $($mut)? Crate,
-        ) -> V::Result {
-            let Crate { attrs, items, spans, id, is_placeholder: _ } = krate;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(visit_items(vis, items));
-            let ModSpans { inner_span, inject_use_span } = spans;
-            try_visit!(visit_span(vis, inner_span));
-            visit_span(vis, inject_use_span)
-        }
-
-        pub fn walk_local<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            local: &$($lt)? $($mut)? Local,
-        ) -> V::Result {
-            let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens: _ } = local;
-            if let Some(sp) = super_ {
-                try_visit!(visit_span(vis, sp));
-            }
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_pat(pat));
-            visit_opt!(vis, visit_ty, ty);
-            match kind {
-                LocalKind::Decl => {}
-                LocalKind::Init(init) => {
-                    try_visit!(vis.visit_expr(init))
-                }
-                LocalKind::InitElse(init, els) => {
-                    try_visit!(vis.visit_expr(init));
-                    try_visit!(vis.visit_block(els));
-                }
-            }
-            if let Some(sp) = colon_sp {
-                try_visit!(visit_span(vis, sp));
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_poly_trait_ref<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            p: &$($lt)? $($mut)? PolyTraitRef,
-        ) -> V::Result {
-            let PolyTraitRef { bound_generic_params, modifiers, trait_ref, span, parens: _ } = p;
-            try_visit!(visit_modifiers(vis, modifiers));
-            try_visit!(visit_generic_params(vis, bound_generic_params));
-            try_visit!(vis.visit_trait_ref(trait_ref));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_trait_ref<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            TraitRef { path, ref_id }: &$($lt)? $($mut)? TraitRef,
-        ) -> V::Result {
-            try_visit!(vis.visit_path(path));
-            visit_id(vis, ref_id)
-        }
-
-        pub fn walk_variant<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            variant: &$($lt)? $($mut)? Variant,
-        ) -> V::Result {
-            let Variant { attrs, id, span, vis: visibility, ident, data, disr_expr, is_placeholder: _ } = variant;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_vis(visibility));
-            try_visit!(vis.visit_ident(ident));
-            try_visit!(vis.visit_variant_data(data));
-            visit_opt!(vis, visit_anon_const, disr_expr);
-            visit_span(vis, span)
-        }
-
-        pub fn walk_expr_field<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            f: &$($lt)? $($mut)? ExprField,
-        ) -> V::Result {
-            let ExprField { attrs, id, span, ident, expr, is_shorthand: _, is_placeholder: _ } = f;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_ident(ident));
-            try_visit!(vis.visit_expr(expr));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_pat_field<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            fp: &$($lt)? $($mut)? PatField,
-        ) -> V::Result {
-            let PatField { ident, pat, is_shorthand: _, attrs, id, span, is_placeholder: _ } = fp;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_ident(ident));
-            try_visit!(vis.visit_pat(pat));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_ty_pat<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            tp: &$($lt)? $($mut)? TyPat,
-        ) -> V::Result {
-            let TyPat { id, kind, span, tokens: _ } = tp;
-            try_visit!(visit_id(vis, id));
-            match kind {
-                TyPatKind::Range(start, end, Spanned { span, node: _include_end }) => {
-                    visit_opt!(vis, visit_anon_const, start);
-                    visit_opt!(vis, visit_anon_const, end);
-                    try_visit!(visit_span(vis, span));
-                }
-                TyPatKind::Or(variants) => walk_list!(vis, visit_ty_pat, variants),
-                TyPatKind::Err(_) => {}
-            }
-            visit_span(vis, span)
-        }
-
-        fn walk_qself<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            qself: &$($lt)? $($mut)? Option<P<QSelf>>,
-        ) -> V::Result {
-            if let Some(qself) = qself {
-                let QSelf { ty, path_span, position: _ } = &$($mut)? **qself;
-                try_visit!(vis.visit_ty(ty));
-                try_visit!(visit_span(vis, path_span));
-            }
-            V::Result::output()
-        }
-
-        pub fn walk_path<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            path: &$($lt)? $($mut)? Path,
-        ) -> V::Result {
-            let Path { span, segments, tokens: _ } = path;
-            walk_list!(vis, visit_path_segment, segments);
-            visit_span(vis, span)
-        }
-
-        pub fn walk_use_tree<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            use_tree: &$($lt)? $($mut)? UseTree,
-        ) -> V::Result {
-            let UseTree { prefix, kind, span } = use_tree;
-            try_visit!(vis.visit_path(prefix));
-            match kind {
-                UseTreeKind::Simple(rename) => {
-                    // The extra IDs are handled during AST lowering.
-                    visit_opt!(vis, visit_ident, rename);
-                }
-                UseTreeKind::Glob => {}
-                UseTreeKind::Nested { items, span } => {
-                    for (nested_tree, nested_id) in items {
-                        try_visit!(visit_nested_use_tree(vis, nested_tree, nested_id));
+                        let kind = FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func);
+                        try_visit!(vis.visit_fn(kind, span, id))
                     }
-                    try_visit!(visit_span(vis, span));
-                }
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_generic_args<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            generic_args: &$($lt)? $($mut)? GenericArgs
-        ) -> V::Result {
-            match generic_args {
-                GenericArgs::AngleBracketed(AngleBracketedArgs { span, args }) => {
-                    for arg in args {
-                        match arg {
-                            AngleBracketedArg::Arg(a) => try_visit!(vis.visit_generic_arg(a)),
-                            AngleBracketedArg::Constraint(c) => {
-                                try_visit!(vis.visit_assoc_item_constraint(c))
-                            }
-                        }
-                    }
-                    visit_span(vis, span)
-                }
-                GenericArgs::Parenthesized(data) => {
-                    let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
-                    walk_list!(vis, visit_ty, inputs);
-                    try_visit!(vis.visit_fn_ret_ty(output));
-                    try_visit!(visit_span(vis, span));
-                    visit_span(vis, inputs_span)
-                }
-                GenericArgs::ParenthesizedElided(span) => visit_span(vis, span)
-            }
-        }
-
-        pub fn walk_generic_arg<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            generic_arg: &$($lt)? $($mut)? GenericArg,
-        ) -> V::Result {
-            match generic_arg {
-                GenericArg::Lifetime(lt) => vis.visit_lifetime(lt, $(${ignore($lt)} LifetimeCtxt::GenericArg)? ),
-                GenericArg::Type(ty) => vis.visit_ty(ty),
-                GenericArg::Const(ct) => vis.visit_anon_const(ct),
-            }
-        }
-
-        pub fn walk_assoc_item_constraint<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            constraint: &$($lt)? $($mut)? AssocItemConstraint,
-        ) -> V::Result {
-            let AssocItemConstraint { id, ident, gen_args, kind, span } = constraint;
-            try_visit!(visit_id(vis, id));
-            try_visit!(vis.visit_ident(ident));
-            visit_opt!(vis, visit_generic_args, gen_args);
-            match kind {
-                AssocItemConstraintKind::Equality { term } => match term {
-                    Term::Ty(ty) => try_visit!(vis.visit_ty(ty)),
-                    Term::Const(c) => try_visit!(vis.visit_anon_const(c)),
-                },
-                AssocItemConstraintKind::Bound { bounds } => {
-                    try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
-                }
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_param_bound<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, bound: &$($lt)? $($mut)? GenericBound) -> V::Result {
-            match bound {
-                GenericBound::Trait(trait_ref) => vis.visit_poly_trait_ref(trait_ref),
-                GenericBound::Outlives(lifetime) => vis.visit_lifetime(lifetime, $(${ignore($lt)} LifetimeCtxt::Bound)?),
-                GenericBound::Use(args, span) => {
-                    walk_list!(vis, visit_precise_capturing_arg, args);
-                    visit_span(vis, span)
-                }
-            }
-        }
-
-        pub fn walk_precise_capturing_arg<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            arg: &$($lt)? $($mut)? PreciseCapturingArg,
-        ) -> V::Result {
-            match arg {
-                PreciseCapturingArg::Lifetime(lt) => vis.visit_lifetime(lt, $(${ignore($lt)} LifetimeCtxt::GenericArg)?),
-                PreciseCapturingArg::Arg(path, id) => {
-                    try_visit!(visit_id(vis, id));
-                    vis.visit_path(path)
-                }
-            }
-        }
-
-        pub fn walk_generic_param<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            param: &$($lt)? $($mut)? GenericParam,
-        ) -> V::Result {
-            let GenericParam { id, ident, attrs, bounds, is_placeholder: _, kind, colon_span } =
-                param;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_ident(ident));
-            walk_list!(vis, visit_param_bound, bounds, BoundKind::Bound);
-            match kind {
-                GenericParamKind::Lifetime => (),
-                GenericParamKind::Type { default } => visit_opt!(vis, visit_ty, default),
-                GenericParamKind::Const { ty, default, span } => {
-                    try_visit!(vis.visit_ty(ty));
-                    visit_opt!(vis, visit_anon_const, default);
-                    try_visit!(visit_span(vis, span));
-                }
-            }
-            if let Some(sp) = colon_span {
-                try_visit!(visit_span(vis, sp))
-            }
-            V::Result::output()
-        }
-
-        pub fn walk_generics<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, generics: &$($lt)? $($mut)? Generics) -> V::Result {
-            let Generics { params, where_clause, span } = generics;
-            let WhereClause { has_where_token: _, predicates, span: where_clause_span } = where_clause;
-            try_visit!(visit_generic_params(vis, params));
-            try_visit!(visit_where_predicates(vis, predicates));
-            try_visit!(visit_span(vis, span));
-            visit_span(vis, where_clause_span)
-        }
-
-        pub fn walk_contract<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, c: &$($lt)? $($mut)? FnContract) -> V::Result {
-            let FnContract { requires, ensures } = c;
-            visit_opt!(vis, visit_expr, requires);
-            visit_opt!(vis, visit_expr, ensures);
-            V::Result::output()
-        }
-
-        pub fn walk_where_predicate<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            predicate: &$($lt)? $($mut)? WherePredicate,
-        ) -> V::Result {
-            let WherePredicate { attrs, kind, id, span, is_placeholder: _ } = predicate;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(visit_span(vis, span));
-            vis.visit_where_predicate_kind(kind)
-        }
-
-        pub fn walk_closure_binder<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            binder: &$($lt)? $($mut)? ClosureBinder,
-        ) -> V::Result {
-            match binder {
-                ClosureBinder::NotPresent => {}
-                ClosureBinder::For { generic_params, span } => {
-                    try_visit!(visit_generic_params(vis, generic_params));
-                    try_visit!(visit_span(vis, span));
-                }
-            }
-            V::Result::output()
-        }
-
-        pub fn walk_where_predicate_kind<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            kind: &$($lt)? $($mut)? WherePredicateKind,
-        ) -> V::Result {
-            match kind {
-                WherePredicateKind::BoundPredicate(WhereBoundPredicate {
-                    bounded_ty,
-                    bounds,
-                    bound_generic_params,
-                }) => {
-                    try_visit!(visit_generic_params(vis, bound_generic_params));
-                    try_visit!(vis.visit_ty(bounded_ty));
-                    walk_list!(vis, visit_param_bound, bounds, BoundKind::Bound);
-                }
-                WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => {
-                    try_visit!(vis.visit_lifetime(lifetime, $(${ignore($lt)} LifetimeCtxt::Bound )?));
-                    walk_list!(vis, visit_param_bound, bounds, BoundKind::Bound);
-                }
-                WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => {
-                    try_visit!(vis.visit_ty(lhs_ty));
-                    try_visit!(vis.visit_ty(rhs_ty));
+                    ForeignItemKind::TyAlias(alias) =>
+                        visit_visitable!($($mut)? vis, alias),
+                    ForeignItemKind::MacCall(mac) =>
+                        visit_visitable!($($mut)? vis, mac),
                 }
-            }
-            V::Result::output()
-        }
-
-        pub fn walk_fn_decl<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            FnDecl { inputs, output }: &$($lt)? $($mut)? FnDecl,
-        ) -> V::Result {
-            try_visit!(visit_params(vis, inputs));
-            vis.visit_fn_ret_ty(output)
-        }
-
-        pub fn walk_fn_ret_ty<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, ret_ty: &$($lt)? $($mut)? FnRetTy) -> V::Result {
-            match ret_ty {
-                FnRetTy::Default(span) => visit_span(vis, span),
-                FnRetTy::Ty(output_ty) => vis.visit_ty(output_ty),
+                V::Result::output()
             }
         }
 
@@ -1450,455 +913,200 @@ macro_rules! common_visitor_and_walkers {
             match kind {
                 FnKind::Fn(
                     _ctxt,
+                    // Visibility is visited as a part of the item.
                     _vis,
-                    Fn {
-                        defaultness,
-                        ident,
-                        sig: FnSig { header, decl, span },
-                        generics,
-                        contract,
-                        body,
-                        define_opaque,
-                    },
+                    Fn { defaultness, ident, sig, generics, contract, body, define_opaque },
                 ) => {
-                    // Visibility is visited as a part of the item.
-                    try_visit!(visit_defaultness(vis, defaultness));
-                    try_visit!(vis.visit_ident(ident));
-                    try_visit!(vis.visit_fn_header(header));
-                    try_visit!(vis.visit_generics(generics));
-                    try_visit!(vis.visit_fn_decl(decl));
-                    visit_opt!(vis, visit_contract, contract);
-                    visit_opt!(vis, visit_block, body);
-                    try_visit!(visit_span(vis, span));
-                    walk_define_opaques(vis, define_opaque)
-                }
-                FnKind::Closure(binder, coroutine_kind, decl, body) => {
-                    try_visit!(vis.visit_closure_binder(binder));
-                    visit_opt!(vis, visit_coroutine_kind, coroutine_kind);
-                    try_visit!(vis.visit_fn_decl(decl));
-                    vis.visit_expr(body)
-                }
-            }
-        }
-
-        pub fn walk_variant_data<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, data: &$($lt)? $($mut)? VariantData) -> V::Result {
-            match data {
-                VariantData::Struct { fields, recovered: _ } => {
-                    visit_field_defs(vis, fields)
-                }
-                VariantData::Tuple(fields, id) => {
-                    try_visit!(visit_id(vis, id));
-                    visit_field_defs(vis, fields)
-                }
-                VariantData::Unit(id) => visit_id(vis, id),
-            }
-        }
-
-        pub fn walk_field_def<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, field: &$($lt)? $($mut)? FieldDef) -> V::Result {
-            let FieldDef { attrs, id, span, vis: visibility, ident, ty, is_placeholder: _, safety: _, default } =
-                field;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_vis(visibility));
-            visit_opt!(vis, visit_ident, ident);
-            try_visit!(vis.visit_ty(ty));
-            visit_opt!(vis, visit_anon_const, default);
-            visit_span(vis, span)
-        }
-
-        fn visit_delim_args<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, args: &$($lt)? $($mut)? DelimArgs) -> V::Result {
-            let DelimArgs { dspan, delim: _, tokens: _ } = args;
-            let DelimSpan { open, close } = dspan;
-            try_visit!(visit_span(vis, open));
-            visit_span(vis, close)
-        }
-
-        pub fn walk_mac<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, mac: &$($lt)? $($mut)? MacCall) -> V::Result {
-            let MacCall { path, args } = mac;
-            try_visit!(vis.visit_path(path));
-            visit_delim_args(vis, args)
-        }
-
-        fn walk_macro_def<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, macro_def: &$($lt)? $($mut)? MacroDef) -> V::Result {
-            let MacroDef { body, macro_rules: _ } = macro_def;
-            visit_delim_args(vis, body)
-        }
-
-        pub fn walk_inline_asm<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, asm: &$($lt)? $($mut)? InlineAsm) -> V::Result {
-            let InlineAsm {
-                asm_macro: _,
-                template,
-                template_strs,
-                operands,
-                clobber_abis,
-                options: _,
-                line_spans,
-            } = asm;
-            for piece in template {
-                match piece {
-                    InlineAsmTemplatePiece::String(_str) => {}
-                    InlineAsmTemplatePiece::Placeholder { operand_idx: _, modifier: _, span } => {
-                        try_visit!(visit_span(vis, span));
-                    }
+                    let FnSig { header, decl, span } = sig;
+                    visit_visitable!($($mut)? vis,
+                        defaultness, ident, header, generics, decl,
+                        contract, body, span, define_opaque
+                    )
                 }
-            }
-            for (_s1, _s2, span) in template_strs {
-                try_visit!(visit_span(vis, span));
-            }
-            for (op, span) in operands {
-                match op {
-                    InlineAsmOperand::In { expr, reg: _ }
-                    | InlineAsmOperand::Out { expr: Some(expr), reg: _, late: _ }
-                    | InlineAsmOperand::InOut { expr, reg: _, late: _ } => {
-                        try_visit!(vis.visit_expr(expr))
-                    }
-                    InlineAsmOperand::Out { expr: None, reg: _, late: _ } => {}
-                    InlineAsmOperand::SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
-                        try_visit!(vis.visit_expr(in_expr));
-                        visit_opt!(vis, visit_expr, out_expr);
-                    }
-                    InlineAsmOperand::Const { anon_const } => {
-                        try_visit!(vis.visit_anon_const(anon_const))
-                    }
-                    InlineAsmOperand::Sym { sym } => try_visit!(vis.visit_inline_asm_sym(sym)),
-                    InlineAsmOperand::Label { block } => try_visit!(vis.visit_block(block)),
-                }
-                try_visit!(visit_span(vis, span));
-            }
-            for (_s1, span) in clobber_abis {
-                try_visit!(visit_span(vis, span))
-            }
-            for span in line_spans {
-                try_visit!(visit_span(vis, span))
+                FnKind::Closure(binder, coroutine_kind, decl, body) =>
+                    visit_visitable!($($mut)? vis, binder, coroutine_kind, decl, body),
             }
             V::Result::output()
         }
 
-        pub fn walk_inline_asm_sym<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            InlineAsmSym { id, qself, path }: &$($lt)? $($mut)? InlineAsmSym,
-        ) -> V::Result {
-            try_visit!(visit_id(vis, id));
-            try_visit!(vis.visit_qself(qself));
-            vis.visit_path(path)
-        }
-
-        pub fn walk_format_args<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, fmt: &$($lt)? $($mut)? FormatArgs) -> V::Result {
-            let FormatArgs { span, template, arguments, uncooked_fmt_str: _, is_source_literal: _ } = fmt;
-
-            let args = $(${ignore($mut)} arguments.all_args_mut())? $(${ignore($lt)} arguments.all_args())? ;
-            for FormatArgument { kind, expr } in args {
-                match kind {
-                    FormatArgumentKind::Named(ident) | FormatArgumentKind::Captured(ident) => {
-                        try_visit!(vis.visit_ident(ident))
-                    }
-                    FormatArgumentKind::Normal => {}
-                }
-                try_visit!(vis.visit_expr(expr));
-            }
-            for piece in template {
-                match piece {
-                    FormatArgsPiece::Literal(_symbol) => {}
-                    FormatArgsPiece::Placeholder(placeholder) => try_visit!(walk_format_placeholder(vis, placeholder)),
-                }
-            }
-            visit_span(vis, span)
-        }
+        impl_walkable!(|&$($mut)? $($lt)? self: Impl, vis: &mut V| {
+            let Impl { defaultness, safety, generics, constness, polarity, of_trait, self_ty, items } = self;
+            visit_visitable!($($mut)? vis, defaultness, safety, generics, constness, polarity, of_trait, self_ty);
+            visit_visitable_with!($($mut)? vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() });
+            V::Result::output()
+        });
 
-        fn walk_format_placeholder<$($lt,)? V: $Visitor$(<$lt>)?>(
-            vis: &mut V,
-            placeholder: &$($lt)? $($mut)? FormatPlaceholder,
-        ) -> V::Result {
-            let FormatPlaceholder { argument, span, format_options, format_trait: _ } = placeholder;
-            if let Some(span) = span {
-                try_visit!(visit_span(vis, span));
-            }
-            let FormatArgPosition { span, index: _, kind: _ } = argument;
-            if let Some(span) = span {
-                try_visit!(visit_span(vis, span));
-            }
-            let FormatOptions {
-                width,
-                precision,
-                alignment: _,
-                fill: _,
-                sign: _,
-                alternate: _,
-                zero_pad: _,
-                debug_hex: _,
-            } = format_options;
-            match width {
-                None => {}
-                Some(FormatCount::Literal(_)) => {}
-                Some(FormatCount::Argument(FormatArgPosition { span, index: _, kind: _ })) => {
-                    if let Some(span) = span {
-                        try_visit!(visit_span(vis, span));
-                    }
-                }
-            }
-            match precision {
-                None => {}
-                Some(FormatCount::Literal(_)) => {}
-                Some(FormatCount::Argument(FormatArgPosition { span, index: _, kind: _ })) => {
-                    if let Some(span) = span {
-                        try_visit!(visit_span(vis, span));
-                    }
-                }
-            }
+        // Special case to call `visit_method_receiver_expr`.
+        impl_walkable!(|&$($mut)? $($lt)? self: MethodCall, vis: &mut V| {
+            let MethodCall { seg, receiver, args, span } = self;
+            try_visit!(vis.visit_method_receiver_expr(receiver));
+            visit_visitable!($($mut)? vis, seg, args, span);
             V::Result::output()
-        }
+        });
 
-        pub fn walk_expr<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, expression: &$($lt)? $($mut)? Expr) -> V::Result {
-            let Expr { id, kind, span, attrs, tokens: _ } = expression;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
+        impl_walkable!(|&$($mut)? $($lt)? self: Expr, vis: &mut V| {
+            let Expr { id, kind, span, attrs, tokens: _ } = self;
+            visit_visitable!($($mut)? vis, id, attrs);
             match kind {
-                ExprKind::Array(exprs) => {
-                    try_visit!(visit_exprs(vis, exprs));
-                }
-                ExprKind::ConstBlock(anon_const) => try_visit!(vis.visit_anon_const(anon_const)),
-                ExprKind::Repeat(element, count) => {
-                    try_visit!(vis.visit_expr(element));
-                    try_visit!(vis.visit_anon_const(count));
-                }
-                ExprKind::Struct(se) => {
-                    let StructExpr { qself, path, fields, rest } = &$($mut)?**se;
-                    try_visit!(vis.visit_qself(qself));
-                    try_visit!(vis.visit_path(path));
-                    try_visit!(visit_expr_fields(vis, fields));
-                    match rest {
-                        StructRest::Base(expr) => try_visit!(vis.visit_expr(expr)),
-                        StructRest::Rest(span) => try_visit!(visit_span(vis, span)),
-                        StructRest::None => {}
-                    }
-                }
-                ExprKind::Tup(exprs) => {
-                    try_visit!(visit_exprs(vis, exprs));
-                }
-                ExprKind::Call(callee_expression, arguments) => {
-                    try_visit!(vis.visit_expr(callee_expression));
-                    try_visit!(visit_exprs(vis, arguments));
-                }
-                ExprKind::MethodCall(box MethodCall { seg, receiver, args, span }) => {
-                    try_visit!(vis.visit_method_receiver_expr(receiver));
-                    try_visit!(vis.visit_path_segment(seg));
-                    try_visit!(visit_exprs(vis, args));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Binary(Spanned { span, node: _ }, left_expression, right_expression) => {
-                    try_visit!(vis.visit_expr(left_expression));
-                    try_visit!(vis.visit_expr(right_expression));
-                    try_visit!(visit_span(vis, span))
-                }
-                ExprKind::AddrOf(_kind, _mutbl, subexpression) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                }
-                ExprKind::Unary(_op, subexpression) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                }
-                ExprKind::Cast(subexpression, typ) | ExprKind::Type(subexpression, typ) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                    try_visit!(vis.visit_ty(typ));
-                }
-                ExprKind::Let(pat, expr, span, _recovered) => {
-                    try_visit!(vis.visit_pat(pat));
-                    try_visit!(vis.visit_expr(expr));
-                    try_visit!(visit_span(vis, span))
-                }
-                ExprKind::If(head_expression, if_block, optional_else) => {
-                    try_visit!(vis.visit_expr(head_expression));
-                    try_visit!(vis.visit_block(if_block));
-                    visit_opt!(vis, visit_expr, optional_else);
-                }
-                ExprKind::While(subexpression, block, opt_label) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                    try_visit!(vis.visit_expr(subexpression));
-                    try_visit!(vis.visit_block(block));
-                }
-                ExprKind::ForLoop { pat, iter, body, label, kind: _ } => {
-                    visit_opt!(vis, visit_label, label);
-                    try_visit!(vis.visit_pat(pat));
-                    try_visit!(vis.visit_expr(iter));
-                    try_visit!(vis.visit_block(body));
-                }
-                ExprKind::Loop(block, opt_label, span) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                    try_visit!(vis.visit_block(block));
-                    try_visit!(visit_span(vis, span))
-                }
-                ExprKind::Match(subexpression, arms, _kind) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                    try_visit!(visit_arms(vis, arms));
-                }
+                ExprKind::Array(exprs) =>
+                    visit_visitable!($($mut)? vis, exprs),
+                ExprKind::ConstBlock(anon_const) =>
+                    visit_visitable!($($mut)? vis, anon_const),
+                ExprKind::Repeat(element, count) =>
+                    visit_visitable!($($mut)? vis, element, count),
+                ExprKind::Struct(se) =>
+                    visit_visitable!($($mut)? vis, se),
+                ExprKind::Tup(exprs) =>
+                    visit_visitable!($($mut)? vis, exprs),
+                ExprKind::Call(callee_expression, arguments) =>
+                    visit_visitable!($($mut)? vis, callee_expression, arguments),
+                ExprKind::MethodCall(mc) =>
+                    visit_visitable!($($mut)? vis, mc),
+                ExprKind::Binary(op, lhs, rhs) =>
+                    visit_visitable!($($mut)? vis, op, lhs, rhs),
+                ExprKind::AddrOf(kind, mutbl, subexpression) =>
+                    visit_visitable!($($mut)? vis, kind, mutbl, subexpression),
+                ExprKind::Unary(op, subexpression) =>
+                    visit_visitable!($($mut)? vis, op, subexpression),
+                ExprKind::Cast(subexpression, typ) | ExprKind::Type(subexpression, typ) =>
+                    visit_visitable!($($mut)? vis, subexpression, typ),
+                ExprKind::Let(pat, expr, span, _recovered) =>
+                    visit_visitable!($($mut)? vis, pat, expr, span),
+                ExprKind::If(head_expression, if_block, optional_else) =>
+                    visit_visitable!($($mut)? vis, head_expression, if_block, optional_else),
+                ExprKind::While(subexpression, block, opt_label) =>
+                    visit_visitable!($($mut)? vis, subexpression, block, opt_label),
+                ExprKind::ForLoop { pat, iter, body, label, kind } =>
+                    visit_visitable!($($mut)? vis, pat, iter, body, label, kind),
+                ExprKind::Loop(block, opt_label, span) =>
+                    visit_visitable!($($mut)? vis, block, opt_label, span),
+                ExprKind::Match(subexpression, arms, kind) =>
+                    visit_visitable!($($mut)? vis, subexpression, arms, kind),
                 ExprKind::Closure(box Closure {
                     binder,
                     capture_clause,
                     coroutine_kind,
                     constness,
-                    movability: _,
+                    movability,
                     fn_decl,
                     body,
                     fn_decl_span,
                     fn_arg_span,
                 }) => {
-                    try_visit!(visit_constness(vis, constness));
-                    try_visit!(vis.visit_capture_by(capture_clause));
-                    try_visit!(vis.visit_fn(
-                        FnKind::Closure(binder, coroutine_kind, fn_decl, body),
-                        *span,
-                        *id
-                    ));
-                    try_visit!(visit_span(vis, fn_decl_span));
-                    try_visit!(visit_span(vis, fn_arg_span));
-                }
-                ExprKind::Block(block, opt_label) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                    try_visit!(vis.visit_block(block));
-                }
-                ExprKind::Gen(capture_clause, body, _kind, decl_span) => {
-                    try_visit!(vis.visit_capture_by(capture_clause));
-                    try_visit!(vis.visit_block(body));
-                    try_visit!(visit_span(vis, decl_span));
-                }
-                ExprKind::Await(expr, span) => {
-                    try_visit!(vis.visit_expr(expr));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Use(expr, span) => {
-                    try_visit!(vis.visit_expr(expr));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Assign(lhs, rhs, span) => {
-                    try_visit!(vis.visit_expr(lhs));
-                    try_visit!(vis.visit_expr(rhs));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::AssignOp(Spanned { span, node: _ }, left_expression, right_expression) => {
-                    try_visit!(vis.visit_expr(left_expression));
-                    try_visit!(vis.visit_expr(right_expression));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Field(subexpression, ident) => {
-                    try_visit!(vis.visit_expr(subexpression));
-                    try_visit!(vis.visit_ident(ident));
-                }
-                ExprKind::Index(main_expression, index_expression, span) => {
-                    try_visit!(vis.visit_expr(main_expression));
-                    try_visit!(vis.visit_expr(index_expression));
-                    try_visit!(visit_span(vis, span));
-                }
-                ExprKind::Range(start, end, _limit) => {
-                    visit_opt!(vis, visit_expr, start);
-                    visit_opt!(vis, visit_expr, end);
-                }
+                    visit_visitable!($($mut)? vis, constness, movability, capture_clause);
+                    let kind = FnKind::Closure(binder, coroutine_kind, fn_decl, body);
+                    try_visit!(vis.visit_fn(kind, *span, *id));
+                    visit_visitable!($($mut)? vis, fn_decl_span, fn_arg_span);
+                }
+                ExprKind::Block(block, opt_label) =>
+                    visit_visitable!($($mut)? vis, block, opt_label),
+                ExprKind::Gen(capt, body, kind, decl_span) =>
+                    visit_visitable!($($mut)? vis, capt, body, kind, decl_span),
+                ExprKind::Await(expr, span) | ExprKind::Use(expr, span) =>
+                    visit_visitable!($($mut)? vis, expr, span),
+                ExprKind::Assign(lhs, rhs, span) =>
+                    visit_visitable!($($mut)? vis, lhs, rhs, span),
+                ExprKind::AssignOp(op, lhs, rhs) =>
+                    visit_visitable!($($mut)? vis, op, lhs, rhs),
+                ExprKind::Field(subexpression, ident) =>
+                    visit_visitable!($($mut)? vis, subexpression, ident),
+                ExprKind::Index(main_expression, index_expression, span) =>
+                    visit_visitable!($($mut)? vis, main_expression, index_expression, span),
+                ExprKind::Range(start, end, limit) =>
+                    visit_visitable!($($mut)? vis, start, end, limit),
                 ExprKind::Underscore => {}
-                ExprKind::Path(maybe_qself, path) => {
-                    try_visit!(vis.visit_qself(maybe_qself));
-                    try_visit!(vis.visit_path(path));
-                }
-                ExprKind::Break(opt_label, opt_expr) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                    visit_opt!(vis, visit_expr, opt_expr);
-                }
-                ExprKind::Continue(opt_label) => {
-                    visit_opt!(vis, visit_label, opt_label);
-                }
-                ExprKind::Ret(optional_expression) => {
-                    visit_opt!(vis, visit_expr, optional_expression);
-                }
-                ExprKind::Yeet(optional_expression) => {
-                    visit_opt!(vis, visit_expr, optional_expression);
-                }
-                ExprKind::Become(expr) => try_visit!(vis.visit_expr(expr)),
-                ExprKind::MacCall(mac) => try_visit!(vis.visit_mac_call(mac)),
-                ExprKind::Paren(subexpression) => try_visit!(vis.visit_expr(subexpression)),
-                ExprKind::InlineAsm(asm) => try_visit!(vis.visit_inline_asm(asm)),
-                ExprKind::FormatArgs(f) => try_visit!(vis.visit_format_args(f)),
-                ExprKind::OffsetOf(container, fields) => {
-                    try_visit!(vis.visit_ty(container));
-                    walk_list!(vis, visit_ident, fields);
-                }
-                ExprKind::Yield(kind) => {
-                    match kind {
-                        YieldKind::Postfix(expr) => {
-                            try_visit!(vis.visit_expr(expr));
-                        }
-                        YieldKind::Prefix(expr) => {
-                            visit_opt!(vis, visit_expr, expr);
-                        }
-                    }
-                }
-                ExprKind::Try(subexpression) => try_visit!(vis.visit_expr(subexpression)),
-                ExprKind::TryBlock(body) => try_visit!(vis.visit_block(body)),
-                ExprKind::Lit(_token) => {}
-                ExprKind::IncludedBytes(_bytes) => {}
-                ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
-                    try_visit!(vis.visit_expr(expr));
-                    visit_opt!(vis, visit_ty, ty);
-                }
+                ExprKind::Path(maybe_qself, path) =>
+                    visit_visitable!($($mut)? vis, maybe_qself, path),
+                ExprKind::Break(opt_label, opt_expr) =>
+                    visit_visitable!($($mut)? vis, opt_label, opt_expr),
+                ExprKind::Continue(opt_label) =>
+                    visit_visitable!($($mut)? vis, opt_label),
+                ExprKind::Ret(optional_expression) | ExprKind::Yeet(optional_expression) =>
+                    visit_visitable!($($mut)? vis, optional_expression),
+                ExprKind::Become(expr) =>
+                    visit_visitable!($($mut)? vis, expr),
+                ExprKind::MacCall(mac) =>
+                    visit_visitable!($($mut)? vis, mac),
+                ExprKind::Paren(subexpression) =>
+                    visit_visitable!($($mut)? vis, subexpression),
+                ExprKind::InlineAsm(asm) =>
+                    visit_visitable!($($mut)? vis, asm),
+                ExprKind::FormatArgs(f) =>
+                    visit_visitable!($($mut)? vis, f),
+                ExprKind::OffsetOf(container, fields) =>
+                    visit_visitable!($($mut)? vis, container, fields),
+                ExprKind::Yield(kind) =>
+                    visit_visitable!($($mut)? vis, kind),
+                ExprKind::Try(subexpression) =>
+                    visit_visitable!($($mut)? vis, subexpression),
+                ExprKind::TryBlock(body) =>
+                    visit_visitable!($($mut)? vis, body),
+                ExprKind::Lit(token) =>
+                    visit_visitable!($($mut)? vis, token),
+                ExprKind::IncludedBytes(bytes) =>
+                    visit_visitable!($($mut)? vis, bytes),
+                ExprKind::UnsafeBinderCast(kind, expr, ty) =>
+                    visit_visitable!($($mut)? vis, kind, expr, ty),
                 ExprKind::Err(_guar) => {}
                 ExprKind::Dummy => {}
             }
 
             visit_span(vis, span)
-        }
-
-        pub fn walk_param<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, param: &$($lt)? $($mut)? Param) -> V::Result {
-            let Param { attrs, ty, pat, id, span, is_placeholder: _ } = param;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_pat(pat));
-            try_visit!(vis.visit_ty(ty));
-            visit_span(vis, span)
-        }
-
-        pub fn walk_arm<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, arm: &$($lt)? $($mut)? Arm) -> V::Result {
-            let Arm { attrs, pat, guard, body, span, id, is_placeholder: _ } = arm;
-            try_visit!(visit_id(vis, id));
-            walk_list!(vis, visit_attribute, attrs);
-            try_visit!(vis.visit_pat(pat));
-            visit_opt!(vis, visit_expr, guard);
-            visit_opt!(vis, visit_expr, body);
-            visit_span(vis, span)
-        }
-
-        pub fn walk_vis<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, visibility: &$($lt)? $($mut)? Visibility) -> V::Result {
-            let Visibility { kind, span, tokens: _ } = visibility;
-            match kind {
-                VisibilityKind::Restricted { path, id, shorthand: _ } => {
-                    try_visit!(visit_id(vis, id));
-                    try_visit!(vis.visit_path(path));
-                }
-                VisibilityKind::Public | VisibilityKind::Inherited => {}
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_attribute<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, attr: &$($lt)? $($mut)? Attribute) -> V::Result {
-            let Attribute { kind, id: _, style: _, span } = attr;
-            match kind {
-                AttrKind::Normal(normal) => {
-                    let NormalAttr { item, tokens: _ } = &$($mut)?**normal;
-                    let AttrItem { unsafety: _, path, args, tokens: _ } = item;
-                    try_visit!(vis.visit_path(path));
-                    try_visit!(walk_attr_args(vis, args));
-                }
-                AttrKind::DocComment(_kind, _sym) => {}
-            }
-            visit_span(vis, span)
-        }
-
-        pub fn walk_attr_args<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, args: &$($lt)? $($mut)? AttrArgs) -> V::Result {
-            match args {
-                AttrArgs::Empty => {}
-                AttrArgs::Delimited(args) => try_visit!(visit_delim_args(vis, args)),
-                AttrArgs::Eq { eq_span, expr } => {
-                    try_visit!(vis.visit_expr(expr));
-                    try_visit!(visit_span(vis, eq_span));
-                }
-            }
-            V::Result::output()
-        }
+        });
+
+        define_named_walk!($(($mut))? $Visitor$(<$lt>)?
+            pub fn walk_anon_const(AnonConst);
+            pub fn walk_arm(Arm);
+            //pub fn walk_assoc_item(AssocItem, _ctxt: AssocCtxt);
+            pub fn walk_assoc_item_constraint(AssocItemConstraint);
+            pub fn walk_attribute(Attribute);
+            pub fn walk_block(Block);
+            //pub fn walk_nested_use_tree((UseTree, NodeId));
+            pub fn walk_capture_by(CaptureBy);
+            pub fn walk_closure_binder(ClosureBinder);
+            pub fn walk_contract(FnContract);
+            pub fn walk_coroutine_kind(CoroutineKind);
+            pub fn walk_crate(Crate);
+            pub fn walk_expr(Expr);
+            pub fn walk_expr_field(ExprField);
+            pub fn walk_field_def(FieldDef);
+            pub fn walk_fn_decl(FnDecl);
+            pub fn walk_fn_header(FnHeader);
+            pub fn walk_fn_ret_ty(FnRetTy);
+            //pub fn walk_foreign_item(ForeignItem);
+            pub fn walk_foreign_mod(ForeignMod);
+            pub fn walk_format_args(FormatArgs);
+            pub fn walk_generic_arg(GenericArg);
+            pub fn walk_generic_args(GenericArgs);
+            pub fn walk_generic_param(GenericParam);
+            pub fn walk_generics(Generics);
+            pub fn walk_inline_asm(InlineAsm);
+            pub fn walk_inline_asm_sym(InlineAsmSym);
+            //pub fn walk_item(Item);
+            pub fn walk_label(Label);
+            pub fn walk_lifetime(Lifetime);
+            pub fn walk_local(Local);
+            pub fn walk_mac(MacCall);
+            pub fn walk_macro_def(MacroDef);
+            pub fn walk_param_bound(GenericBound);
+            pub fn walk_param(Param);
+            pub fn walk_pat_field(PatField);
+            pub fn walk_path(Path);
+            pub fn walk_path_segment(PathSegment);
+            pub fn walk_pat(Pat);
+            pub fn walk_poly_trait_ref(PolyTraitRef);
+            pub fn walk_precise_capturing_arg(PreciseCapturingArg);
+            pub fn walk_qself(QSelf);
+            pub fn walk_trait_ref(TraitRef);
+            pub fn walk_ty_pat(TyPat);
+            pub fn walk_ty(Ty);
+            pub fn walk_use_tree(UseTree);
+            pub fn walk_variant_data(VariantData);
+            pub fn walk_variant(Variant);
+            pub fn walk_vis(Visibility);
+            pub fn walk_where_predicate_kind(WherePredicateKind);
+            pub fn walk_where_predicate(WherePredicate);
+        );
     };
 }
 
@@ -1907,6 +1115,20 @@ common_visitor_and_walkers!(Visitor<'a>);
 macro_rules! generate_list_visit_fns {
     ($($name:ident, $Ty:ty, $visit_fn:ident$(, $param:ident: $ParamTy:ty)*;)+) => {
         $(
+            #[allow(unused_parens)]
+            impl<'a, V: Visitor<'a>> Visitable<'a, V> for ThinVec<$Ty> {
+                type Extra = ($($ParamTy),*);
+
+                #[inline]
+                fn visit(
+                    &'a self,
+                    visitor: &mut V,
+                    ($($param),*): Self::Extra,
+                ) -> V::Result {
+                    $name(visitor, self $(, $param)*)
+                }
+            }
+
             fn $name<'a, V: Visitor<'a>>(
                 vis: &mut V,
                 values: &'a ThinVec<$Ty>,
@@ -1937,18 +1159,9 @@ generate_list_visit_fns! {
     visit_arms, Arm, visit_arm;
 }
 
-#[expect(rustc::pass_by_value)] // needed for symmetry with mut_visit
-fn visit_nested_use_tree<'a, V: Visitor<'a>>(
-    vis: &mut V,
-    nested_tree: &'a UseTree,
-    &nested_id: &NodeId,
-) -> V::Result {
-    vis.visit_nested_use_tree(nested_tree, nested_id)
-}
-
 pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V::Result {
     let Stmt { id, kind, span: _ } = statement;
-    try_visit!(visit_id(visitor, id));
+    try_visit!(visitor.visit_id(*id));
     match kind {
         StmtKind::Let(local) => try_visit!(visitor.visit_local(local)),
         StmtKind::Item(item) => try_visit!(visitor.visit_item(item)),
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index 9f99b33adcc..1e2576bef2c 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -157,6 +157,19 @@ pub enum UsedBy {
     Linker,
 }
 
+#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum MacroUseArgs {
+    UseAll,
+    UseSpecific(ThinVec<Ident>),
+}
+
+impl Default for MacroUseArgs {
+    fn default() -> Self {
+        Self::UseSpecific(ThinVec::new())
+    }
+}
+
 #[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
 pub struct StrippedCfgItem<ModId = DefId> {
     pub parent_module: ModId,
@@ -351,9 +364,15 @@ pub enum AttributeKind {
     /// Represents `#[loop_match]`.
     LoopMatch(Span),
 
+    /// Represents `#[macro_escape]`.
+    MacroEscape(Span),
+
     /// Represents `#[rustc_macro_transparency]`.
     MacroTransparency(Transparency),
 
+    /// Represents `#[macro_use]`.
+    MacroUse { span: Span, arguments: MacroUseArgs },
+
     /// Represents `#[marker]`.
     Marker(Span),
 
diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
index 86d9ddba4d2..159b807a3b2 100644
--- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
+++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
@@ -45,7 +45,9 @@ impl AttributeKind {
             LinkOrdinal { .. } => No,
             LinkSection { .. } => Yes, // Needed for rustdoc
             LoopMatch(..) => No,
+            MacroEscape(..) => No,
             MacroTransparency(..) => Yes,
+            MacroUse { .. } => No,
             Marker(..) => No,
             MayDangle(..) => No,
             MustUse { .. } => Yes,
diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs
index ecca0e39063..4c5af805ca9 100644
--- a/compiler/rustc_attr_data_structures/src/lib.rs
+++ b/compiler/rustc_attr_data_structures/src/lib.rs
@@ -24,7 +24,7 @@ use rustc_ast::token::CommentKind;
 use rustc_ast::{AttrStyle, IntTy, UintTy};
 use rustc_ast_pretty::pp::Printer;
 use rustc_span::hygiene::Transparency;
-use rustc_span::{ErrorGuaranteed, Span, Symbol};
+use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
 pub use stability::*;
 use thin_vec::ThinVec;
 pub use version::*;
@@ -172,7 +172,7 @@ macro_rules! print_tup {
 print_tup!(A B C D E F G H);
 print_skip!(Span, (), ErrorGuaranteed);
 print_disp!(u16, bool, NonZero<u32>);
-print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
+print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
 
 /// Finds attributes in sequences of attributes by pattern matching.
 ///
diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
new file mode 100644
index 00000000000..eade49180ac
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
@@ -0,0 +1,115 @@
+use rustc_attr_data_structures::{AttributeKind, MacroUseArgs};
+use rustc_errors::DiagArgValue;
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Span, Symbol, sym};
+use thin_vec::ThinVec;
+
+use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate};
+use crate::context::{AcceptContext, FinalizeContext, Stage};
+use crate::parser::ArgParser;
+use crate::session_diagnostics;
+
+pub(crate) struct MacroEscapeParser;
+impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
+    const PATH: &[Symbol] = &[sym::macro_escape];
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::MacroEscape;
+}
+
+/// `#[macro_use]` attributes can either:
+/// - Use all macros from a crate, if provided without arguments
+/// - Use specific macros from a crate, if provided with arguments `#[macro_use(macro1, macro2)]`
+/// A warning should be provided if an use all is combined with specific uses, or if multiple use-alls are used.
+#[derive(Default)]
+pub(crate) struct MacroUseParser {
+    state: MacroUseArgs,
+
+    /// Spans of all `#[macro_use]` arguments with arguments, used for linting
+    uses_attr_spans: ThinVec<Span>,
+    /// If `state` is `UseSpecific`, stores the span of the first `#[macro_use]` argument, used as the span for this attribute
+    /// If `state` is `UseAll`, stores the span of the first `#[macro_use]` arguments without arguments
+    first_span: Option<Span>,
+}
+
+const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ...");
+
+impl<S: Stage> AttributeParser<S> for MacroUseParser {
+    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
+        &[sym::macro_use],
+        MACRO_USE_TEMPLATE,
+        |group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| {
+            let span = cx.attr_span;
+            group.first_span.get_or_insert(span);
+            match args {
+                ArgParser::NoArgs => {
+                    match group.state {
+                        MacroUseArgs::UseAll => {
+                            let first_span = group.first_span.expect(
+                                "State is UseAll is some so this is not the first attribute",
+                            );
+                            // Since there is a `#[macro_use]` import already, give a warning
+                            cx.warn_unused_duplicate(first_span, span);
+                        }
+                        MacroUseArgs::UseSpecific(_) => {
+                            group.state = MacroUseArgs::UseAll;
+                            group.first_span = Some(span);
+                            // If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
+                            for specific_use in group.uses_attr_spans.drain(..) {
+                                cx.warn_unused_duplicate(span, specific_use);
+                            }
+                        }
+                    }
+                }
+                ArgParser::List(list) => {
+                    if list.is_empty() {
+                        cx.warn_empty_attribute(list.span);
+                        return;
+                    }
+
+                    match &mut group.state {
+                        MacroUseArgs::UseAll => {
+                            let first_span = group.first_span.expect(
+                                "State is UseAll is some so this is not the first attribute",
+                            );
+                            cx.warn_unused_duplicate(first_span, span);
+                        }
+                        MacroUseArgs::UseSpecific(arguments) => {
+                            // Store here so if we encounter a `UseAll` later we can still lint this attribute
+                            group.uses_attr_spans.push(cx.attr_span);
+
+                            for item in list.mixed() {
+                                let Some(item) = item.meta_item() else {
+                                    cx.expected_identifier(item.span());
+                                    continue;
+                                };
+                                if let Err(err_span) = item.args().no_args() {
+                                    cx.expected_no_args(err_span);
+                                    continue;
+                                }
+                                let Some(item) = item.path().word() else {
+                                    cx.expected_identifier(item.span());
+                                    continue;
+                                };
+                                arguments.push(item);
+                            }
+                        }
+                    }
+                }
+                ArgParser::NameValue(_) => {
+                    let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
+                    cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
+                        num_suggestions: suggestions.len(),
+                        suggestions: DiagArgValue::StrListSepByAnd(
+                            suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
+                        ),
+                        span,
+                    });
+                }
+            }
+        },
+    )];
+
+    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
+        Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index 200f1381960..15b90bd2ed7 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -36,6 +36,7 @@ pub(crate) mod inline;
 pub(crate) mod link_attrs;
 pub(crate) mod lint_helpers;
 pub(crate) mod loop_match;
+pub(crate) mod macro_attrs;
 pub(crate) mod must_use;
 pub(crate) mod no_implicit_prelude;
 pub(crate) mod non_exhaustive;
diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
index e0a3e675509..42af3ed0bfa 100644
--- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
@@ -34,7 +34,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
                 ArgParser::List(_) => {
                     let suggestions =
                         <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
-                    cx.emit_err(session_diagnostics::MustUseIllFormedAttributeInput {
+                    cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
                         num_suggestions: suggestions.len(),
                         suggestions: DiagArgValue::StrListSepByAnd(
                             suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index 59337749c87..c54fc6b41f8 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -74,8 +74,15 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
             template!(NameValueStr: "deprecation message"),
             |this, cx, args| {
                 reject_outside_std!(cx);
-                this.allowed_through_unstable_modules =
-                    args.name_value().and_then(|i| i.value_as_str())
+                let Some(nv) = args.name_value() else {
+                    cx.expected_name_value(cx.attr_span, None);
+                    return;
+                };
+                let Some(value_str) = nv.value_as_str() else {
+                    cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
+                    return;
+                };
+                this.allowed_through_unstable_modules = Some(value_str);
             },
         ),
     ];
@@ -247,7 +254,12 @@ pub(crate) fn parse_stability<S: Stage>(
     let mut feature = None;
     let mut since = None;
 
-    for param in args.list()?.mixed() {
+    let ArgParser::List(list) = args else {
+        cx.expected_list(cx.attr_span);
+        return None;
+    };
+
+    for param in list.mixed() {
         let param_span = param.span();
         let Some(param) = param.meta_item() else {
             cx.emit_err(session_diagnostics::UnsupportedLiteral {
@@ -322,7 +334,13 @@ pub(crate) fn parse_unstability<S: Stage>(
     let mut is_soft = false;
     let mut implied_by = None;
     let mut old_name = None;
-    for param in args.list()?.mixed() {
+
+    let ArgParser::List(list) = args else {
+        cx.expected_list(cx.attr_span);
+        return None;
+    };
+
+    for param in list.mixed() {
         let Some(param) = param.meta_item() else {
             cx.emit_err(session_diagnostics::UnsupportedLiteral {
                 span: param.span(),
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 4d692d9562c..45bfe345207 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -33,6 +33,7 @@ use crate::attributes::lint_helpers::{
     AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
 };
 use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
+use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser};
 use crate::attributes::must_use::MustUseParser;
 use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
 use crate::attributes::non_exhaustive::NonExhaustiveParser;
@@ -126,6 +127,7 @@ attribute_parsers!(
         BodyStabilityParser,
         ConfusablesParser,
         ConstStabilityParser,
+        MacroUseParser,
         NakedParser,
         StabilityParser,
         UsedParser,
@@ -174,6 +176,7 @@ attribute_parsers!(
         Single<WithoutArgs<FfiPureParser>>,
         Single<WithoutArgs<FundamentalParser>>,
         Single<WithoutArgs<LoopMatchParser>>,
+        Single<WithoutArgs<MacroEscapeParser>>,
         Single<WithoutArgs<MarkerParser>>,
         Single<WithoutArgs<MayDangleParser>>,
         Single<WithoutArgs<NoImplicitPreludeParser>>,
@@ -386,6 +389,17 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         })
     }
 
+    /// emit an error that a `name` was expected here
+    pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedIdentifier,
+        })
+    }
+
     /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
     /// a nicer error message talking about the specific name that was found lacking a value.
     pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 9a400e0fe10..1de25ca252b 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -438,7 +438,7 @@ pub(crate) struct IllFormedAttributeInput {
 
 #[derive(Diagnostic)]
 #[diag(attr_parsing_ill_formed_attribute_input)]
-pub(crate) struct MustUseIllFormedAttributeInput {
+pub(crate) struct IllFormedAttributeInputLint {
     #[primary_span]
     pub span: Span,
     pub num_suggestions: usize,
@@ -549,6 +549,7 @@ pub(crate) enum AttributeParseErrorReason {
         /// Should we tell the user to write a list when they didn't?
         list: bool,
     },
+    ExpectedIdentifier,
 }
 
 pub(crate) struct AttributeParseError {
@@ -600,11 +601,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
                 diag.code(E0538);
             }
             AttributeParseErrorReason::UnexpectedLiteral => {
-                diag.span_label(self.span, format!("didn't expect a literal here"));
+                diag.span_label(self.span, "didn't expect a literal here");
                 diag.code(E0565);
             }
             AttributeParseErrorReason::ExpectedNoArgs => {
-                diag.span_label(self.span, format!("didn't expect any arguments here"));
+                diag.span_label(self.span, "didn't expect any arguments here");
                 diag.code(E0565);
             }
             AttributeParseErrorReason::ExpectedNameValue(None) => {
@@ -684,6 +685,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
                     }
                 }
             }
+            AttributeParseErrorReason::ExpectedIdentifier => {
+                diag.span_label(self.span, "expected a valid identifier here");
+            }
         }
 
         let suggestions = self.template.suggestions(false, &name);
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 85adf0f3716..a04cfa27237 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -74,7 +74,7 @@ pub(crate) fn codegen_tls_ref<'tcx>(
 pub(crate) fn eval_mir_constant<'tcx>(
     fx: &FunctionCx<'_, '_, 'tcx>,
     constant: &ConstOperand<'tcx>,
-) -> (ConstValue<'tcx>, Ty<'tcx>) {
+) -> (ConstValue, Ty<'tcx>) {
     let cv = fx.monomorphize(constant.const_);
     // This cannot fail because we checked all required_consts in advance.
     let val = cv
@@ -93,7 +93,7 @@ pub(crate) fn codegen_constant_operand<'tcx>(
 
 pub(crate) fn codegen_const_value<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
-    const_val: ConstValue<'tcx>,
+    const_val: ConstValue,
     ty: Ty<'tcx>,
 ) -> CValue<'tcx> {
     let layout = fx.layout_of(ty);
@@ -210,8 +210,7 @@ pub(crate) fn codegen_const_value<'tcx>(
                 .offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
             layout,
         ),
-        ConstValue::Slice { data, meta } => {
-            let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data);
+        ConstValue::Slice { alloc_id, meta } => {
             let ptr = pointer_for_allocation(fx, alloc_id).get_addr(fx);
             let len = fx.bcx.ins().iconst(fx.pointer_type, meta as i64);
             CValue::by_val_pair(ptr, len, layout)
diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 55a28bc9493..a70ac08f01a 100644
--- a/compiler/rustc_codegen_gcc/messages.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
@@ -3,12 +3,4 @@ codegen_gcc_unwinding_inline_asm =
 
 codegen_gcc_copy_bitcode = failed to copy bitcode to object file: {$err}
 
-codegen_gcc_dynamic_linking_with_lto =
-    cannot prefer dynamic linking when performing LTO
-    .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
-
-codegen_gcc_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs
-
-codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
-
 codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})
diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs
index e554dd2500b..d558dfbc1c4 100644
--- a/compiler/rustc_codegen_gcc/src/back/lto.rs
+++ b/compiler/rustc_codegen_gcc/src/back/lto.rs
@@ -25,35 +25,21 @@ use std::sync::Arc;
 use gccjit::{Context, OutputKind};
 use object::read::archive::ArchiveFile;
 use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
-use rustc_codegen_ssa::back::symbol_export;
 use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
 use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
 use rustc_data_structures::memmap::Mmap;
 use rustc_errors::{DiagCtxtHandle, FatalError};
-use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::WorkProduct;
-use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
-use rustc_session::config::{CrateType, Lto};
+use rustc_session::config::Lto;
 use rustc_target::spec::RelocModel;
 use tempfile::{TempDir, tempdir};
 
 use crate::back::write::save_temp_bitcode;
-use crate::errors::{DynamicLinkingWithLTO, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib};
+use crate::errors::LtoBitcodeFromRlib;
 use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level};
 
-pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
-    match crate_type {
-        CrateType::Executable
-        | CrateType::Dylib
-        | CrateType::Staticlib
-        | CrateType::Cdylib
-        | CrateType::Sdylib => true,
-        CrateType::Rlib | CrateType::ProcMacro => false,
-    }
-}
-
 struct LtoData {
     // TODO(antoyo): use symbols_below_threshold.
     //symbols_below_threshold: Vec<String>,
@@ -63,18 +49,9 @@ struct LtoData {
 
 fn prepare_lto(
     cgcx: &CodegenContext<GccCodegenBackend>,
+    each_linked_rlib_for_lto: &[PathBuf],
     dcx: DiagCtxtHandle<'_>,
 ) -> Result<LtoData, FatalError> {
-    let export_threshold = match cgcx.lto {
-        // We're just doing LTO for our one crate
-        Lto::ThinLocal => SymbolExportLevel::Rust,
-
-        // We're doing LTO for the entire crate graph
-        Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types),
-
-        Lto::No => panic!("didn't request LTO but we're doing LTO"),
-    };
-
     let tmp_path = match tempdir() {
         Ok(tmp_path) => tmp_path,
         Err(error) => {
@@ -83,20 +60,6 @@ fn prepare_lto(
         }
     };
 
-    let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
-        if info.level.is_below_threshold(export_threshold) || info.used {
-            Some(name.clone())
-        } else {
-            None
-        }
-    };
-    let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
-    let mut symbols_below_threshold = {
-        let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold");
-        exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::<Vec<String>>()
-    };
-    info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
-
     // If we're performing LTO for the entire crate graph, then for each of our
     // upstream dependencies, find the corresponding rlib and load the bitcode
     // from the archive.
@@ -105,32 +68,7 @@ fn prepare_lto(
     // with either fat or thin LTO
     let mut upstream_modules = Vec::new();
     if cgcx.lto != Lto::ThinLocal {
-        // Make sure we actually can run LTO
-        for crate_type in cgcx.crate_types.iter() {
-            if !crate_type_allows_lto(*crate_type) {
-                dcx.emit_err(LtoDisallowed);
-                return Err(FatalError);
-            }
-            if *crate_type == CrateType::Dylib && !cgcx.opts.unstable_opts.dylib_lto {
-                dcx.emit_err(LtoDylib);
-                return Err(FatalError);
-            }
-        }
-
-        if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
-            dcx.emit_err(DynamicLinkingWithLTO);
-            return Err(FatalError);
-        }
-
-        for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
-            let exported_symbols =
-                cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
-            {
-                let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold");
-                symbols_below_threshold
-                    .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter));
-            }
-
+        for path in each_linked_rlib_for_lto {
             let archive_data = unsafe {
                 Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib")
             };
@@ -174,19 +112,18 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> {
 /// for further optimization.
 pub(crate) fn run_fat(
     cgcx: &CodegenContext<GccCodegenBackend>,
+    each_linked_rlib_for_lto: &[PathBuf],
     modules: Vec<FatLtoInput<GccCodegenBackend>>,
-    cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<ModuleCodegen<GccContext>, FatalError> {
     let dcx = cgcx.create_dcx();
     let dcx = dcx.handle();
-    let lto_data = prepare_lto(cgcx, dcx)?;
+    let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx)?;
     /*let symbols_below_threshold =
     lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();*/
     fat_lto(
         cgcx,
         dcx,
         modules,
-        cached_modules,
         lto_data.upstream_modules,
         lto_data.tmp_path,
         //&lto_data.symbols_below_threshold,
@@ -197,7 +134,6 @@ fn fat_lto(
     cgcx: &CodegenContext<GccCodegenBackend>,
     _dcx: DiagCtxtHandle<'_>,
     modules: Vec<FatLtoInput<GccCodegenBackend>>,
-    cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
     mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
     tmp_path: TempDir,
     //symbols_below_threshold: &[String],
@@ -211,21 +147,12 @@ fn fat_lto(
     //   modules that are serialized in-memory.
     // * `in_memory` contains modules which are already parsed and in-memory,
     //   such as from multi-CGU builds.
-    //
-    // All of `cached_modules` (cached from previous incremental builds) can
-    // immediately go onto the `serialized_modules` modules list and then we can
-    // split the `modules` array into these two lists.
     let mut in_memory = Vec::new();
-    serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| {
-        info!("pushing cached module {:?}", wp.cgu_name);
-        (buffer, CString::new(wp.cgu_name).unwrap())
-    }));
     for module in modules {
         match module {
             FatLtoInput::InMemory(m) => in_memory.push(m),
             FatLtoInput::Serialized { name, buffer } => {
                 info!("pushing serialized module {:?}", name);
-                let buffer = SerializedModule::Local(buffer);
                 serialized_modules.push((buffer, CString::new(name).unwrap()));
             }
         }
@@ -356,12 +283,13 @@ impl ModuleBufferMethods for ModuleBuffer {
 /// can simply be copied over from the incr. comp. cache.
 pub(crate) fn run_thin(
     cgcx: &CodegenContext<GccCodegenBackend>,
+    each_linked_rlib_for_lto: &[PathBuf],
     modules: Vec<(String, ThinBuffer)>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<(Vec<ThinModule<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> {
     let dcx = cgcx.create_dcx();
     let dcx = dcx.handle();
-    let lto_data = prepare_lto(cgcx, dcx)?;
+    let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx)?;
     if cgcx.opts.cg.linker_plugin_lto.enabled() {
         unreachable!(
             "We should never reach this case if the LTO step \
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index b7e7343460f..0aa16bd88b4 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -15,19 +15,6 @@ pub(crate) struct CopyBitcode {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_gcc_dynamic_linking_with_lto)]
-#[note]
-pub(crate) struct DynamicLinkingWithLTO;
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_lto_disallowed)]
-pub(crate) struct LtoDisallowed;
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_lto_dylib)]
-pub(crate) struct LtoDylib;
-
-#[derive(Diagnostic)]
 #[diag(codegen_gcc_lto_bitcode_from_rlib)]
 pub(crate) struct LtoBitcodeFromRlib {
     pub gcc_err: String,
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index af416929ea7..71765c51138 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -81,6 +81,7 @@ mod type_of;
 use std::any::Any;
 use std::fmt::Debug;
 use std::ops::Deref;
+use std::path::PathBuf;
 #[cfg(not(feature = "master"))]
 use std::sync::atomic::AtomicBool;
 #[cfg(not(feature = "master"))]
@@ -358,23 +359,28 @@ impl WriteBackendMethods for GccCodegenBackend {
 
     fn run_and_optimize_fat_lto(
         cgcx: &CodegenContext<Self>,
+        // FIXME(bjorn3): Limit LTO exports to these symbols
+        _exported_symbols_for_lto: &[String],
+        each_linked_rlib_for_lto: &[PathBuf],
         modules: Vec<FatLtoInput<Self>>,
-        cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
         diff_fncs: Vec<AutoDiffItem>,
     ) -> Result<ModuleCodegen<Self::Module>, FatalError> {
         if !diff_fncs.is_empty() {
             unimplemented!();
         }
 
-        back::lto::run_fat(cgcx, modules, cached_modules)
+        back::lto::run_fat(cgcx, each_linked_rlib_for_lto, modules)
     }
 
     fn run_thin_lto(
         cgcx: &CodegenContext<Self>,
+        // FIXME(bjorn3): Limit LTO exports to these symbols
+        _exported_symbols_for_lto: &[String],
+        each_linked_rlib_for_lto: &[PathBuf],
         modules: Vec<(String, Self::ThinBuffer)>,
         cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
     ) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> {
-        back::lto::run_thin(cgcx, modules, cached_modules)
+        back::lto::run_thin(cgcx, each_linked_rlib_for_lto, modules, cached_modules)
     }
 
     fn print_pass_timings(&self) {
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index f197ea74473..3d5f17a6034 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -2,10 +2,6 @@ codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z au
 
 codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err}
 
-codegen_llvm_dynamic_linking_with_lto =
-    cannot prefer dynamic linking when performing LTO
-    .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
-
 
 codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
 
@@ -18,12 +14,6 @@ codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$na
 
 codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$llvm_err})
 
-codegen_llvm_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs
-
-codegen_llvm_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
-
-codegen_llvm_lto_proc_macro = lto cannot be used for `proc-macro` crate type without `-Zdylib-lto`
-
 codegen_llvm_mismatch_data_layout =
     data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}`
 
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 84302009da9..767835c34f0 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -1,33 +1,28 @@
 use std::collections::BTreeMap;
 use std::ffi::{CStr, CString};
 use std::fs::File;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::ptr::NonNull;
 use std::sync::Arc;
 use std::{io, iter, slice};
 
 use object::read::archive::ArchiveFile;
 use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
-use rustc_codegen_ssa::back::symbol_export;
 use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
 use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::memmap::Mmap;
 use rustc_errors::{DiagCtxtHandle, FatalError};
-use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::WorkProduct;
-use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
-use rustc_session::config::{self, CrateType, Lto};
+use rustc_session::config::{self, Lto};
 use tracing::{debug, info};
 
 use crate::back::write::{
     self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode,
 };
-use crate::errors::{
-    DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, LtoProcMacro,
-};
+use crate::errors::{LlvmError, LtoBitcodeFromRlib};
 use crate::llvm::AttributePlace::Function;
 use crate::llvm::{self, build_string};
 use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes};
@@ -36,45 +31,21 @@ use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes};
 /// session to determine which CGUs we can reuse.
 const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin";
 
-fn crate_type_allows_lto(crate_type: CrateType) -> bool {
-    match crate_type {
-        CrateType::Executable
-        | CrateType::Dylib
-        | CrateType::Staticlib
-        | CrateType::Cdylib
-        | CrateType::ProcMacro
-        | CrateType::Sdylib => true,
-        CrateType::Rlib => false,
-    }
-}
-
 fn prepare_lto(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
+    exported_symbols_for_lto: &[String],
+    each_linked_rlib_for_lto: &[PathBuf],
     dcx: DiagCtxtHandle<'_>,
 ) -> Result<(Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>), FatalError> {
-    let export_threshold = match cgcx.lto {
-        // We're just doing LTO for our one crate
-        Lto::ThinLocal => SymbolExportLevel::Rust,
-
-        // We're doing LTO for the entire crate graph
-        Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types),
-
-        Lto::No => panic!("didn't request LTO but we're doing LTO"),
-    };
+    let mut symbols_below_threshold = exported_symbols_for_lto
+        .iter()
+        .map(|symbol| CString::new(symbol.to_owned()).unwrap())
+        .collect::<Vec<CString>>();
 
-    let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
-        if info.level.is_below_threshold(export_threshold) || info.used {
-            Some(CString::new(name.as_str()).unwrap())
-        } else {
-            None
-        }
-    };
-    let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
-    let mut symbols_below_threshold = {
-        let _timer = cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold");
-        exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::<Vec<CString>>()
-    };
-    info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
+    // __llvm_profile_counter_bias is pulled in at link time by an undefined reference to
+    // __llvm_profile_runtime, therefore we won't know until link time if this symbol
+    // should have default visibility.
+    symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned());
 
     // If we're performing LTO for the entire crate graph, then for each of our
     // upstream dependencies, find the corresponding rlib and load the bitcode
@@ -84,37 +55,7 @@ fn prepare_lto(
     // with either fat or thin LTO
     let mut upstream_modules = Vec::new();
     if cgcx.lto != Lto::ThinLocal {
-        // Make sure we actually can run LTO
-        for crate_type in cgcx.crate_types.iter() {
-            if !crate_type_allows_lto(*crate_type) {
-                dcx.emit_err(LtoDisallowed);
-                return Err(FatalError);
-            } else if *crate_type == CrateType::Dylib {
-                if !cgcx.opts.unstable_opts.dylib_lto {
-                    dcx.emit_err(LtoDylib);
-                    return Err(FatalError);
-                }
-            } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto {
-                dcx.emit_err(LtoProcMacro);
-                return Err(FatalError);
-            }
-        }
-
-        if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
-            dcx.emit_err(DynamicLinkingWithLTO);
-            return Err(FatalError);
-        }
-
-        for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
-            let exported_symbols =
-                cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
-            {
-                let _timer =
-                    cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold");
-                symbols_below_threshold
-                    .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter));
-            }
-
+        for path in each_linked_rlib_for_lto {
             let archive_data = unsafe {
                 Mmap::map(std::fs::File::open(&path).expect("couldn't open rlib"))
                     .expect("couldn't map rlib")
@@ -147,10 +88,6 @@ fn prepare_lto(
         }
     }
 
-    // __llvm_profile_counter_bias is pulled in at link time by an undefined reference to
-    // __llvm_profile_runtime, therefore we won't know until link time if this symbol
-    // should have default visibility.
-    symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned());
     Ok((symbols_below_threshold, upstream_modules))
 }
 
@@ -199,15 +136,17 @@ fn get_bitcode_slice_from_object_data<'a>(
 /// for further optimization.
 pub(crate) fn run_fat(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
+    exported_symbols_for_lto: &[String],
+    each_linked_rlib_for_lto: &[PathBuf],
     modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
-    cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
     let dcx = cgcx.create_dcx();
     let dcx = dcx.handle();
-    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
+    let (symbols_below_threshold, upstream_modules) =
+        prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?;
     let symbols_below_threshold =
         symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
-    fat_lto(cgcx, dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold)
+    fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold)
 }
 
 /// Performs thin LTO by performing necessary global analysis and returning two
@@ -215,12 +154,15 @@ pub(crate) fn run_fat(
 /// can simply be copied over from the incr. comp. cache.
 pub(crate) fn run_thin(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
+    exported_symbols_for_lto: &[String],
+    each_linked_rlib_for_lto: &[PathBuf],
     modules: Vec<(String, ThinBuffer)>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
     let dcx = cgcx.create_dcx();
     let dcx = dcx.handle();
-    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
+    let (symbols_below_threshold, upstream_modules) =
+        prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?;
     let symbols_below_threshold =
         symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
     if cgcx.opts.cg.linker_plugin_lto.enabled() {
@@ -245,7 +187,6 @@ fn fat_lto(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
     dcx: DiagCtxtHandle<'_>,
     modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
-    cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
     mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
     symbols_below_threshold: &[*const libc::c_char],
 ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
@@ -258,21 +199,12 @@ fn fat_lto(
     //   modules that are serialized in-memory.
     // * `in_memory` contains modules which are already parsed and in-memory,
     //   such as from multi-CGU builds.
-    //
-    // All of `cached_modules` (cached from previous incremental builds) can
-    // immediately go onto the `serialized_modules` modules list and then we can
-    // split the `modules` array into these two lists.
     let mut in_memory = Vec::new();
-    serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| {
-        info!("pushing cached module {:?}", wp.cgu_name);
-        (buffer, CString::new(wp.cgu_name).unwrap())
-    }));
     for module in modules {
         match module {
             FatLtoInput::InMemory(m) => in_memory.push(m),
             FatLtoInput::Serialized { name, buffer } => {
                 info!("pushing serialized module {:?}", name);
-                let buffer = SerializedModule::Local(buffer);
                 serialized_modules.push((buffer, CString::new(name).unwrap()));
             }
         }
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs
index 39a59560c9d..574463be7ff 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs
@@ -39,7 +39,10 @@ impl Coords {
 /// or other expansions), and if it does happen then skipping a span or function is
 /// better than an ICE or `llvm-cov` failure that the user might have no way to avoid.
 pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span) -> Option<Coords> {
-    let span = ensure_non_empty_span(source_map, span)?;
+    if span.is_empty() {
+        debug_assert!(false, "can't make coords from empty span: {span:?}");
+        return None;
+    }
 
     let lo = span.lo();
     let hi = span.hi();
@@ -70,29 +73,6 @@ pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span)
     })
 }
 
-fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
-    if !span.is_empty() {
-        return Some(span);
-    }
-
-    // The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
-    source_map
-        .span_to_source(span, |src, start, end| try {
-            // Adjusting span endpoints by `BytePos(1)` is normally a bug,
-            // but in this case we have specifically checked that the character
-            // we're skipping over is one of two specific ASCII characters, so
-            // adjusting by exactly 1 byte is correct.
-            if src.as_bytes().get(end).copied() == Some(b'{') {
-                Some(span.with_hi(span.hi() + BytePos(1)))
-            } else if start > 0 && src.as_bytes()[start - 1] == b'}' {
-                Some(span.with_lo(span.lo() - BytePos(1)))
-            } else {
-                None
-            }
-        })
-        .ok()?
-}
-
 /// If `llvm-cov` sees a source region that is improperly ordered (end < start),
 /// it will immediately exit with a fatal error. To prevent that from happening,
 /// discard regions that are improperly ordered, or might be interpreted in a
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 31d49e86319..2a889888a39 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -20,11 +20,6 @@ pub(crate) struct SymbolAlreadyDefined<'a> {
 #[diag(codegen_llvm_sanitizer_memtag_requires_mte)]
 pub(crate) struct SanitizerMemtagRequiresMte;
 
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_dynamic_linking_with_lto)]
-#[note]
-pub(crate) struct DynamicLinkingWithLTO;
-
 pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>);
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
@@ -42,18 +37,6 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
 pub(crate) struct AutoDiffWithoutEnable;
 
 #[derive(Diagnostic)]
-#[diag(codegen_llvm_lto_disallowed)]
-pub(crate) struct LtoDisallowed;
-
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_lto_dylib)]
-pub(crate) struct LtoDylib;
-
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_lto_proc_macro)]
-pub(crate) struct LtoProcMacro;
-
-#[derive(Diagnostic)]
 #[diag(codegen_llvm_lto_bitcode_from_rlib)]
 pub(crate) struct LtoBitcodeFromRlib {
     pub llvm_err: String,
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index aaf21f9ada9..8b1913cfa75 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -22,6 +22,7 @@
 use std::any::Any;
 use std::ffi::CStr;
 use std::mem::ManuallyDrop;
+use std::path::PathBuf;
 
 use back::owned_target_machine::OwnedTargetMachine;
 use back::write::{create_informational_target_machine, create_target_machine};
@@ -176,11 +177,13 @@ impl WriteBackendMethods for LlvmCodegenBackend {
     }
     fn run_and_optimize_fat_lto(
         cgcx: &CodegenContext<Self>,
+        exported_symbols_for_lto: &[String],
+        each_linked_rlib_for_lto: &[PathBuf],
         modules: Vec<FatLtoInput<Self>>,
-        cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
         diff_fncs: Vec<AutoDiffItem>,
     ) -> Result<ModuleCodegen<Self::Module>, FatalError> {
-        let mut module = back::lto::run_fat(cgcx, modules, cached_modules)?;
+        let mut module =
+            back::lto::run_fat(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules)?;
 
         if !diff_fncs.is_empty() {
             builder::autodiff::differentiate(&module, cgcx, diff_fncs)?;
@@ -194,10 +197,18 @@ impl WriteBackendMethods for LlvmCodegenBackend {
     }
     fn run_thin_lto(
         cgcx: &CodegenContext<Self>,
+        exported_symbols_for_lto: &[String],
+        each_linked_rlib_for_lto: &[PathBuf],
         modules: Vec<(String, Self::ThinBuffer)>,
         cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
     ) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> {
-        back::lto::run_thin(cgcx, modules, cached_modules)
+        back::lto::run_thin(
+            cgcx,
+            exported_symbols_for_lto,
+            each_linked_rlib_for_lto,
+            modules,
+            cached_modules,
+        )
     }
     fn optimize(
         cgcx: &CodegenContext<Self>,
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index c7bd6ffd1f2..a70d0011d16 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -35,6 +35,10 @@ codegen_ssa_dlltool_fail_import_library =
     {$stdout}
     {$stderr}
 
+codegen_ssa_dynamic_linking_with_lto =
+    cannot prefer dynamic linking when performing LTO
+    .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
+
 codegen_ssa_error_calling_dlltool =
     Error calling dlltool '{$dlltool_path}': {$error}
 
@@ -191,6 +195,12 @@ codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for
 
 codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status}
 
+codegen_ssa_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs
+
+codegen_ssa_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
+
+codegen_ssa_lto_proc_macro = lto cannot be used for `proc-macro` crate type without `-Zdylib-lto`
+
 codegen_ssa_malformed_cgu_name =
     found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case).
 
diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs
index 74f39022afb..b9e0c957363 100644
--- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
 
 use rustc_abi::Endian;
 use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
-use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_data_structures::stable_hasher::StableHasher;
 use rustc_hashes::Hash128;
 use rustc_session::Session;
@@ -214,7 +214,7 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>(
 /// It exports all the provided symbols, but is otherwise empty.
 fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec<u8> {
     use object::write::elf as write;
-    use object::{Architecture, elf};
+    use object::{AddressSize, Architecture, elf};
 
     let mut stub_buf = Vec::new();
 
@@ -226,54 +226,94 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
     // It is important that the order of reservation matches the order of writing.
     // The object crate contains many debug asserts that fire if you get this wrong.
 
+    let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features)
+    else {
+        sess.dcx().fatal(format!(
+            "raw-dylib is not supported for the architecture `{}`",
+            sess.target.arch
+        ));
+    };
+
     let endianness = match sess.target.options.endian {
         Endian::Little => object::Endianness::Little,
         Endian::Big => object::Endianness::Big,
     };
-    let mut stub = write::Writer::new(endianness, true, &mut stub_buf);
+
+    let is_64 = match arch.address_size() {
+        Some(AddressSize::U8 | AddressSize::U16 | AddressSize::U32) => false,
+        Some(AddressSize::U64) => true,
+        _ => sess.dcx().fatal(format!(
+            "raw-dylib is not supported for the architecture `{}`",
+            sess.target.arch
+        )),
+    };
+
+    let mut stub = write::Writer::new(endianness, is_64, &mut stub_buf);
+
+    let mut vers = Vec::new();
+    let mut vers_map = FxHashMap::default();
+    let mut syms = Vec::new();
+
+    for symbol in symbols {
+        let symbol_name = symbol.name.as_str();
+        if let Some((name, version_name)) = symbol_name.split_once('@') {
+            assert!(!version_name.contains('@'));
+            let dynstr = stub.add_dynamic_string(name.as_bytes());
+            let ver = if let Some(&ver_id) = vers_map.get(version_name) {
+                ver_id
+            } else {
+                let id = vers.len();
+                vers_map.insert(version_name, id);
+                let dynstr = stub.add_dynamic_string(version_name.as_bytes());
+                vers.push((version_name, dynstr));
+                id
+            };
+            syms.push((name, dynstr, Some(ver)));
+        } else {
+            let dynstr = stub.add_dynamic_string(symbol_name.as_bytes());
+            syms.push((symbol_name, dynstr, None));
+        }
+    }
+
+    let soname = stub.add_dynamic_string(soname.as_bytes());
 
     // These initial reservations don't reserve any bytes in the binary yet,
     // they just allocate in the internal data structures.
 
-    // First, we crate the dynamic symbol table. It starts with a null symbol
+    // First, we create the dynamic symbol table. It starts with a null symbol
     // and then all the symbols and their dynamic strings.
     stub.reserve_null_dynamic_symbol_index();
 
-    let dynstrs = symbols
-        .iter()
-        .map(|sym| {
-            stub.reserve_dynamic_symbol_index();
-            (sym, stub.add_dynamic_string(sym.name.as_str().as_bytes()))
-        })
-        .collect::<Vec<_>>();
-
-    let soname = stub.add_dynamic_string(soname.as_bytes());
+    for _ in syms.iter() {
+        stub.reserve_dynamic_symbol_index();
+    }
 
     // Reserve the sections.
     // We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to.
     stub.reserve_shstrtab_section_index();
     let text_section_name = stub.add_section_name(".text".as_bytes());
     let text_section = stub.reserve_section_index();
-    stub.reserve_dynstr_section_index();
     stub.reserve_dynsym_section_index();
+    stub.reserve_dynstr_section_index();
+    if !vers.is_empty() {
+        stub.reserve_gnu_versym_section_index();
+        stub.reserve_gnu_verdef_section_index();
+    }
     stub.reserve_dynamic_section_index();
 
     // These reservations now determine the actual layout order of the object file.
     stub.reserve_file_header();
     stub.reserve_shstrtab();
     stub.reserve_section_headers();
-    stub.reserve_dynstr();
     stub.reserve_dynsym();
+    stub.reserve_dynstr();
+    if !vers.is_empty() {
+        stub.reserve_gnu_versym();
+        stub.reserve_gnu_verdef(1 + vers.len(), 1 + vers.len());
+    }
     stub.reserve_dynamic(2); // DT_SONAME, DT_NULL
 
     // First write the ELF header with the arch information.
-    let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features)
-    else {
-        sess.dcx().fatal(format!(
-            "raw-dylib is not supported for the architecture `{}`",
-            sess.target.arch
-        ));
-    };
     let e_machine = match (arch, sub_arch) {
         (Architecture::Aarch64, None) => elf::EM_AARCH64,
         (Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64,
@@ -342,18 +382,19 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
         sh_addralign: 1,
         sh_entsize: 0,
     });
-    stub.write_dynstr_section_header(0);
     stub.write_dynsym_section_header(0, 1);
+    stub.write_dynstr_section_header(0);
+    if !vers.is_empty() {
+        stub.write_gnu_versym_section_header(0);
+        stub.write_gnu_verdef_section_header(0);
+    }
     stub.write_dynamic_section_header(0);
 
-    // .dynstr
-    stub.write_dynstr();
-
     // .dynsym
     stub.write_null_dynamic_symbol();
-    for (_, name) in dynstrs {
+    for (_name, dynstr, _ver) in syms.iter().copied() {
         stub.write_dynamic_symbol(&write::Sym {
-            name: Some(name),
+            name: Some(dynstr),
             st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE,
             st_other: elf::STV_DEFAULT,
             section: Some(text_section),
@@ -363,10 +404,47 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
         });
     }
 
+    // .dynstr
+    stub.write_dynstr();
+
+    // ld.bfd is unhappy if these sections exist without any symbols, so we only generate them when necessary.
+    if !vers.is_empty() {
+        // .gnu_version
+        stub.write_null_gnu_versym();
+        for (_name, _dynstr, ver) in syms.iter().copied() {
+            stub.write_gnu_versym(if let Some(ver) = ver {
+                assert!((2 + ver as u16) < elf::VERSYM_HIDDEN);
+                elf::VERSYM_HIDDEN | (2 + ver as u16)
+            } else {
+                1
+            });
+        }
+
+        // .gnu_version_d
+        stub.write_align_gnu_verdef();
+        stub.write_gnu_verdef(&write::Verdef {
+            version: elf::VER_DEF_CURRENT,
+            flags: elf::VER_FLG_BASE,
+            index: 1,
+            aux_count: 1,
+            name: soname,
+        });
+        for (ver, (_name, dynstr)) in vers.into_iter().enumerate() {
+            stub.write_gnu_verdef(&write::Verdef {
+                version: elf::VER_DEF_CURRENT,
+                flags: 0,
+                index: 2 + ver as u16,
+                aux_count: 1,
+                name: dynstr,
+            });
+        }
+    }
+
     // .dynamic
     // the DT_SONAME will be used by the linker to populate DT_NEEDED
     // which the loader uses to find the library.
     // DT_NULL terminates the .dynamic table.
+    stub.write_align_dynamic();
     stub.write_dynamic_string(elf::DT_SONAME, soname);
     stub.write_dynamic(elf::DT_NULL, 0);
 
diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs
index b49b6783bbd..c95038375a1 100644
--- a/compiler/rustc_codegen_ssa/src/back/lto.rs
+++ b/compiler/rustc_codegen_ssa/src/back/lto.rs
@@ -2,7 +2,15 @@ use std::ffi::CString;
 use std::sync::Arc;
 
 use rustc_data_structures::memmap::Mmap;
+use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
+use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel};
+use rustc_middle::ty::TyCtxt;
+use rustc_session::config::{CrateType, Lto};
+use tracing::info;
 
+use crate::back::symbol_export::{self, symbol_name_for_instance_in_crate};
+use crate::back::write::CodegenContext;
+use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro};
 use crate::traits::*;
 
 pub struct ThinModule<B: WriteBackendMethods> {
@@ -52,3 +60,86 @@ impl<M: ModuleBufferMethods> SerializedModule<M> {
         }
     }
 }
+
+fn crate_type_allows_lto(crate_type: CrateType) -> bool {
+    match crate_type {
+        CrateType::Executable
+        | CrateType::Dylib
+        | CrateType::Staticlib
+        | CrateType::Cdylib
+        | CrateType::ProcMacro
+        | CrateType::Sdylib => true,
+        CrateType::Rlib => false,
+    }
+}
+
+pub(super) fn exported_symbols_for_lto(
+    tcx: TyCtxt<'_>,
+    each_linked_rlib_for_lto: &[CrateNum],
+) -> Vec<String> {
+    let export_threshold = match tcx.sess.lto() {
+        // We're just doing LTO for our one crate
+        Lto::ThinLocal => SymbolExportLevel::Rust,
+
+        // We're doing LTO for the entire crate graph
+        Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&tcx.crate_types()),
+
+        Lto::No => return vec![],
+    };
+
+    let copy_symbols = |cnum| {
+        tcx.exported_non_generic_symbols(cnum)
+            .iter()
+            .chain(tcx.exported_generic_symbols(cnum))
+            .filter_map(|&(s, info): &(ExportedSymbol<'_>, SymbolExportInfo)| {
+                if info.level.is_below_threshold(export_threshold) || info.used {
+                    Some(symbol_name_for_instance_in_crate(tcx, s, cnum))
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>()
+    };
+    let mut symbols_below_threshold = {
+        let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold");
+        copy_symbols(LOCAL_CRATE)
+    };
+    info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
+
+    // If we're performing LTO for the entire crate graph, then for each of our
+    // upstream dependencies, include their exported symbols.
+    if tcx.sess.lto() != Lto::ThinLocal {
+        for &cnum in each_linked_rlib_for_lto {
+            let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold");
+            symbols_below_threshold.extend(copy_symbols(cnum));
+        }
+    }
+
+    symbols_below_threshold
+}
+
+pub(super) fn check_lto_allowed<B: WriteBackendMethods>(cgcx: &CodegenContext<B>) {
+    if cgcx.lto == Lto::ThinLocal {
+        // Crate local LTO is always allowed
+        return;
+    }
+
+    let dcx = cgcx.create_dcx();
+
+    // Make sure we actually can run LTO
+    for crate_type in cgcx.crate_types.iter() {
+        if !crate_type_allows_lto(*crate_type) {
+            dcx.handle().emit_fatal(LtoDisallowed);
+        } else if *crate_type == CrateType::Dylib {
+            if !cgcx.opts.unstable_opts.dylib_lto {
+                dcx.handle().emit_fatal(LtoDylib);
+            }
+        } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto {
+            dcx.handle().emit_fatal(LtoProcMacro);
+        }
+    }
+
+    if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
+        dcx.handle().emit_fatal(DynamicLinkingWithLTO);
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 24e0a4eb533..7be274df1d4 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -9,7 +9,7 @@ use std::{fs, io, mem, str, thread};
 use rustc_abi::Size;
 use rustc_ast::attr;
 use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
-use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::jobserver::{self, Acquired};
 use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
@@ -20,14 +20,12 @@ use rustc_errors::{
     Suggestions,
 };
 use rustc_fs_util::link_or_copy;
-use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_incremental::{
     copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
 };
 use rustc_metadata::fs::copy_to_stdout;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::exported_symbols::SymbolExportInfo;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_session::config::{
@@ -40,7 +38,7 @@ use tracing::debug;
 
 use super::link::{self, ensure_removed};
 use super::lto::{self, SerializedModule};
-use super::symbol_export::symbol_name_for_instance_in_crate;
+use crate::back::lto::check_lto_allowed;
 use crate::errors::{AutodiffWithoutLto, ErrorCreatingRemarkDir};
 use crate::traits::*;
 use crate::{
@@ -332,8 +330,6 @@ pub type TargetMachineFactoryFn<B> = Arc<
         + Sync,
 >;
 
-type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo)>>>;
-
 /// Additional resources used by optimize_and_codegen (not module specific)
 #[derive(Clone)]
 pub struct CodegenContext<B: WriteBackendMethods> {
@@ -343,10 +339,8 @@ pub struct CodegenContext<B: WriteBackendMethods> {
     pub save_temps: bool,
     pub fewer_names: bool,
     pub time_trace: bool,
-    pub exported_symbols: Option<Arc<ExportedSymbols>>,
     pub opts: Arc<config::Options>,
     pub crate_types: Vec<CrateType>,
-    pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>,
     pub output_filenames: Arc<OutputFilenames>,
     pub invocation_temp: Option<String>,
     pub regular_module_config: Arc<ModuleConfig>,
@@ -401,13 +395,21 @@ impl<B: WriteBackendMethods> CodegenContext<B> {
 
 fn generate_thin_lto_work<B: ExtraBackendMethods>(
     cgcx: &CodegenContext<B>,
+    exported_symbols_for_lto: &[String],
+    each_linked_rlib_for_lto: &[PathBuf],
     needs_thin_lto: Vec<(String, B::ThinBuffer)>,
     import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
 ) -> Vec<(WorkItem<B>, u64)> {
     let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work");
 
-    let (lto_modules, copy_jobs) =
-        B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise());
+    let (lto_modules, copy_jobs) = B::run_thin_lto(
+        cgcx,
+        exported_symbols_for_lto,
+        each_linked_rlib_for_lto,
+        needs_thin_lto,
+        import_only_modules,
+    )
+    .unwrap_or_else(|e| e.raise());
     lto_modules
         .into_iter()
         .map(|module| {
@@ -723,6 +725,8 @@ pub(crate) enum WorkItem<B: WriteBackendMethods> {
     CopyPostLtoArtifacts(CachedModuleCodegen),
     /// Performs fat LTO on the given module.
     FatLto {
+        exported_symbols_for_lto: Arc<Vec<String>>,
+        each_linked_rlib_for_lto: Vec<PathBuf>,
         needs_fat_lto: Vec<FatLtoInput<B>>,
         import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
         autodiff: Vec<AutoDiffItem>,
@@ -810,7 +814,7 @@ pub(crate) enum WorkItemResult<B: WriteBackendMethods> {
 }
 
 pub enum FatLtoInput<B: WriteBackendMethods> {
-    Serialized { name: String, buffer: B::ModuleBuffer },
+    Serialized { name: String, buffer: SerializedModule<B::ModuleBuffer> },
     InMemory(ModuleCodegen<B::Module>),
 }
 
@@ -899,7 +903,10 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
                 fs::write(&path, buffer.data()).unwrap_or_else(|e| {
                     panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e);
                 });
-                Ok(WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { name, buffer }))
+                Ok(WorkItemResult::NeedsFatLto(FatLtoInput::Serialized {
+                    name,
+                    buffer: SerializedModule::Local(buffer),
+                }))
             }
             None => Ok(WorkItemResult::NeedsFatLto(FatLtoInput::InMemory(module))),
         },
@@ -992,12 +999,24 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
 
 fn execute_fat_lto_work_item<B: ExtraBackendMethods>(
     cgcx: &CodegenContext<B>,
-    needs_fat_lto: Vec<FatLtoInput<B>>,
+    exported_symbols_for_lto: &[String],
+    each_linked_rlib_for_lto: &[PathBuf],
+    mut needs_fat_lto: Vec<FatLtoInput<B>>,
     import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
     autodiff: Vec<AutoDiffItem>,
     module_config: &ModuleConfig,
 ) -> Result<WorkItemResult<B>, FatalError> {
-    let module = B::run_and_optimize_fat_lto(cgcx, needs_fat_lto, import_only_modules, autodiff)?;
+    for (module, wp) in import_only_modules {
+        needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module })
+    }
+
+    let module = B::run_and_optimize_fat_lto(
+        cgcx,
+        exported_symbols_for_lto,
+        each_linked_rlib_for_lto,
+        needs_fat_lto,
+        autodiff,
+    )?;
     let module = B::codegen(cgcx, module, module_config)?;
     Ok(WorkItemResult::Finished(module))
 }
@@ -1032,7 +1051,7 @@ pub(crate) enum Message<B: WriteBackendMethods> {
 
     /// The backend has finished processing a work item for a codegen unit.
     /// Sent from a backend worker thread.
-    WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>>, worker_id: usize },
+    WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>> },
 
     /// The frontend has finished generating something (backend IR or a
     /// post-LTO artifact) for a codegen unit, and it should be passed to the
@@ -1113,42 +1132,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
     let autodiff_items = autodiff_items.to_vec();
 
     let mut each_linked_rlib_for_lto = Vec::new();
+    let mut each_linked_rlib_file_for_lto = Vec::new();
     drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| {
         if link::ignored_for_lto(sess, crate_info, cnum) {
             return;
         }
-        each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
+        each_linked_rlib_for_lto.push(cnum);
+        each_linked_rlib_file_for_lto.push(path.to_path_buf());
     }));
 
     // Compute the set of symbols we need to retain when doing LTO (if we need to)
-    let exported_symbols = {
-        let mut exported_symbols = FxHashMap::default();
-
-        let copy_symbols = |cnum| {
-            let symbols = tcx
-                .exported_non_generic_symbols(cnum)
-                .iter()
-                .chain(tcx.exported_generic_symbols(cnum))
-                .map(|&(s, lvl)| (symbol_name_for_instance_in_crate(tcx, s, cnum), lvl))
-                .collect();
-            Arc::new(symbols)
-        };
-
-        match sess.lto() {
-            Lto::No => None,
-            Lto::ThinLocal => {
-                exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE));
-                Some(Arc::new(exported_symbols))
-            }
-            Lto::Fat | Lto::Thin => {
-                exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE));
-                for &(cnum, ref _path) in &each_linked_rlib_for_lto {
-                    exported_symbols.insert(cnum, copy_symbols(cnum));
-                }
-                Some(Arc::new(exported_symbols))
-            }
-        }
-    };
+    let exported_symbols_for_lto =
+        Arc::new(lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto));
 
     // First up, convert our jobserver into a helper thread so we can use normal
     // mpsc channels to manage our messages and such.
@@ -1183,14 +1178,12 @@ fn start_executing_work<B: ExtraBackendMethods>(
 
     let cgcx = CodegenContext::<B> {
         crate_types: tcx.crate_types().to_vec(),
-        each_linked_rlib_for_lto,
         lto: sess.lto(),
         fewer_names: sess.fewer_names(),
         save_temps: sess.opts.cg.save_temps,
         time_trace: sess.opts.unstable_opts.llvm_time_trace,
         opts: Arc::new(sess.opts.clone()),
         prof: sess.prof.clone(),
-        exported_symbols,
         remark: sess.opts.cg.remark.clone(),
         remark_dir,
         incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
@@ -1350,18 +1343,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
     // necessary. There's already optimizations in place to avoid sending work
     // back to the coordinator if LTO isn't requested.
     return B::spawn_named_thread(cgcx.time_trace, "coordinator".to_string(), move || {
-        let mut worker_id_counter = 0;
-        let mut free_worker_ids = Vec::new();
-        let mut get_worker_id = |free_worker_ids: &mut Vec<usize>| {
-            if let Some(id) = free_worker_ids.pop() {
-                id
-            } else {
-                let id = worker_id_counter;
-                worker_id_counter += 1;
-                id
-            }
-        };
-
         // This is where we collect codegen units that have gone all the way
         // through codegen and LLVM.
         let mut compiled_modules = vec![];
@@ -1442,12 +1423,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
                         let (item, _) =
                             work_items.pop().expect("queue empty - queue_full_enough() broken?");
                         main_thread_state = MainThreadState::Lending;
-                        spawn_work(
-                            &cgcx,
-                            &mut llvm_start_time,
-                            get_worker_id(&mut free_worker_ids),
-                            item,
-                        );
+                        spawn_work(&cgcx, &mut llvm_start_time, item);
                     }
                 }
             } else if codegen_state == Completed {
@@ -1474,12 +1450,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
                     let needs_fat_lto = mem::take(&mut needs_fat_lto);
                     let needs_thin_lto = mem::take(&mut needs_thin_lto);
                     let import_only_modules = mem::take(&mut lto_import_only_modules);
+                    let each_linked_rlib_file_for_lto =
+                        mem::take(&mut each_linked_rlib_file_for_lto);
+
+                    check_lto_allowed(&cgcx);
 
                     if !needs_fat_lto.is_empty() {
                         assert!(needs_thin_lto.is_empty());
 
                         work_items.push((
                             WorkItem::FatLto {
+                                exported_symbols_for_lto: Arc::clone(&exported_symbols_for_lto),
+                                each_linked_rlib_for_lto: each_linked_rlib_file_for_lto,
                                 needs_fat_lto,
                                 import_only_modules,
                                 autodiff: autodiff_items.clone(),
@@ -1495,9 +1477,13 @@ fn start_executing_work<B: ExtraBackendMethods>(
                             dcx.handle().emit_fatal(AutodiffWithoutLto {});
                         }
 
-                        for (work, cost) in
-                            generate_thin_lto_work(&cgcx, needs_thin_lto, import_only_modules)
-                        {
+                        for (work, cost) in generate_thin_lto_work(
+                            &cgcx,
+                            &exported_symbols_for_lto,
+                            &each_linked_rlib_file_for_lto,
+                            needs_thin_lto,
+                            import_only_modules,
+                        ) {
                             let insertion_index = work_items
                                 .binary_search_by_key(&cost, |&(_, cost)| cost)
                                 .unwrap_or_else(|e| e);
@@ -1516,12 +1502,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
                     MainThreadState::Idle => {
                         if let Some((item, _)) = work_items.pop() {
                             main_thread_state = MainThreadState::Lending;
-                            spawn_work(
-                                &cgcx,
-                                &mut llvm_start_time,
-                                get_worker_id(&mut free_worker_ids),
-                                item,
-                            );
+                            spawn_work(&cgcx, &mut llvm_start_time, item);
                         } else {
                             // There is no unstarted work, so let the main thread
                             // take over for a running worker. Otherwise the
@@ -1557,12 +1538,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
                 while running_with_own_token < tokens.len()
                     && let Some((item, _)) = work_items.pop()
                 {
-                    spawn_work(
-                        &cgcx,
-                        &mut llvm_start_time,
-                        get_worker_id(&mut free_worker_ids),
-                        item,
-                    );
+                    spawn_work(&cgcx, &mut llvm_start_time, item);
                     running_with_own_token += 1;
                 }
             }
@@ -1570,21 +1546,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
             // Relinquish accidentally acquired extra tokens.
             tokens.truncate(running_with_own_token);
 
-            // If a thread exits successfully then we drop a token associated
-            // with that worker and update our `running_with_own_token` count.
-            // We may later re-acquire a token to continue running more work.
-            // We may also not actually drop a token here if the worker was
-            // running with an "ephemeral token".
-            let mut free_worker = |worker_id| {
-                if main_thread_state == MainThreadState::Lending {
-                    main_thread_state = MainThreadState::Idle;
-                } else {
-                    running_with_own_token -= 1;
-                }
-
-                free_worker_ids.push(worker_id);
-            };
-
             let msg = coordinator_receive.recv().unwrap();
             match *msg.downcast::<Message<B>>().ok().unwrap() {
                 // Save the token locally and the next turn of the loop will use
@@ -1653,8 +1614,17 @@ fn start_executing_work<B: ExtraBackendMethods>(
                     codegen_state = Aborted;
                 }
 
-                Message::WorkItem { result, worker_id } => {
-                    free_worker(worker_id);
+                Message::WorkItem { result } => {
+                    // If a thread exits successfully then we drop a token associated
+                    // with that worker and update our `running_with_own_token` count.
+                    // We may later re-acquire a token to continue running more work.
+                    // We may also not actually drop a token here if the worker was
+                    // running with an "ephemeral token".
+                    if main_thread_state == MainThreadState::Lending {
+                        main_thread_state = MainThreadState::Idle;
+                    } else {
+                        running_with_own_token -= 1;
+                    }
 
                     match result {
                         Ok(WorkItemResult::Finished(compiled_module)) => {
@@ -1800,7 +1770,6 @@ pub(crate) struct WorkerFatalError;
 fn spawn_work<'a, B: ExtraBackendMethods>(
     cgcx: &'a CodegenContext<B>,
     llvm_start_time: &mut Option<VerboseTimingGuard<'a>>,
-    worker_id: usize,
     work: WorkItem<B>,
 ) {
     if cgcx.config(work.module_kind()).time_module && llvm_start_time.is_none() {
@@ -1815,24 +1784,21 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
         struct Bomb<B: ExtraBackendMethods> {
             coordinator_send: Sender<Box<dyn Any + Send>>,
             result: Option<Result<WorkItemResult<B>, FatalError>>,
-            worker_id: usize,
         }
         impl<B: ExtraBackendMethods> Drop for Bomb<B> {
             fn drop(&mut self) {
-                let worker_id = self.worker_id;
                 let msg = match self.result.take() {
-                    Some(Ok(result)) => Message::WorkItem::<B> { result: Ok(result), worker_id },
+                    Some(Ok(result)) => Message::WorkItem::<B> { result: Ok(result) },
                     Some(Err(FatalError)) => {
-                        Message::WorkItem::<B> { result: Err(Some(WorkerFatalError)), worker_id }
+                        Message::WorkItem::<B> { result: Err(Some(WorkerFatalError)) }
                     }
-                    None => Message::WorkItem::<B> { result: Err(None), worker_id },
+                    None => Message::WorkItem::<B> { result: Err(None) },
                 };
                 drop(self.coordinator_send.send(Box::new(msg)));
             }
         }
 
-        let mut bomb =
-            Bomb::<B> { coordinator_send: cgcx.coordinator_send.clone(), result: None, worker_id };
+        let mut bomb = Bomb::<B> { coordinator_send: cgcx.coordinator_send.clone(), result: None };
 
         // Execute the work itself, and if it finishes successfully then flag
         // ourselves as a success as well.
@@ -1856,12 +1822,20 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
                     );
                     Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config))
                 }
-                WorkItem::FatLto { needs_fat_lto, import_only_modules, autodiff } => {
+                WorkItem::FatLto {
+                    exported_symbols_for_lto,
+                    each_linked_rlib_for_lto,
+                    needs_fat_lto,
+                    import_only_modules,
+                    autodiff,
+                } => {
                     let _timer = cgcx
                         .prof
                         .generic_activity_with_arg("codegen_module_perform_lto", "everything");
                     execute_fat_lto_work_item(
                         &cgcx,
+                        &exported_symbols_for_lto,
+                        &each_linked_rlib_for_lto,
                         needs_fat_lto,
                         import_only_modules,
                         autodiff,
diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs
index 48565e0b4de..a6fd6c763ed 100644
--- a/compiler/rustc_codegen_ssa/src/common.rs
+++ b/compiler/rustc_codegen_ssa/src/common.rs
@@ -148,7 +148,7 @@ pub(crate) fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 pub fn asm_const_to_str<'tcx>(
     tcx: TyCtxt<'tcx>,
     sp: Span,
-    const_value: mir::ConstValue<'tcx>,
+    const_value: mir::ConstValue,
     ty_and_layout: TyAndLayout<'tcx>,
 ) -> String {
     let mir::ConstValue::Scalar(scalar) = const_value else {
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 9040915b6af..3d787d8bdbd 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -1294,3 +1294,20 @@ pub(crate) struct FeatureNotValid<'a> {
     #[help]
     pub plus_hint: bool,
 }
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_lto_disallowed)]
+pub(crate) struct LtoDisallowed;
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_lto_dylib)]
+pub(crate) struct LtoDylib;
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_lto_proc_macro)]
+pub(crate) struct LtoProcMacro;
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_dynamic_linking_with_lto)]
+#[note]
+pub(crate) struct DynamicLinkingWithLTO;
diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs
index 68a56044a07..11b6ab3cdf1 100644
--- a/compiler/rustc_codegen_ssa/src/mir/constant.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs
@@ -20,7 +20,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         OperandRef::from_const(bx, val, ty)
     }
 
-    pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue<'tcx> {
+    pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue {
         // `MirUsedCollector` visited all required_consts before codegen began, so if we got here
         // there can be no more constants that fail to evaluate.
         self.monomorphize(constant.const_)
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index 06bedaaa4a2..8e308aac769 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -140,7 +140,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
 
     pub(crate) fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
         bx: &mut Bx,
-        val: mir::ConstValue<'tcx>,
+        val: mir::ConstValue,
         ty: Ty<'tcx>,
     ) -> Self {
         let layout = bx.layout_of(ty);
@@ -154,14 +154,11 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
                 OperandValue::Immediate(llval)
             }
             ConstValue::ZeroSized => return OperandRef::zero_sized(layout),
-            ConstValue::Slice { data, meta } => {
+            ConstValue::Slice { alloc_id, meta } => {
                 let BackendRepr::ScalarPair(a_scalar, _) = layout.backend_repr else {
                     bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
                 };
-                let a = Scalar::from_pointer(
-                    Pointer::new(bx.tcx().reserve_and_set_memory_alloc(data).into(), Size::ZERO),
-                    &bx.tcx(),
-                );
+                let a = Scalar::from_pointer(Pointer::new(alloc_id.into(), Size::ZERO), &bx.tcx());
                 let a_llval = bx.scalar_to_backend(
                     a,
                     a_scalar,
diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs
index 5e993640472..8e78cbe1963 100644
--- a/compiler/rustc_codegen_ssa/src/traits/write.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/write.rs
@@ -1,3 +1,5 @@
+use std::path::PathBuf;
+
 use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
 use rustc_errors::{DiagCtxtHandle, FatalError};
 use rustc_middle::dep_graph::WorkProduct;
@@ -24,8 +26,9 @@ pub trait WriteBackendMethods: Clone + 'static {
     /// if necessary and running any further optimizations
     fn run_and_optimize_fat_lto(
         cgcx: &CodegenContext<Self>,
+        exported_symbols_for_lto: &[String],
+        each_linked_rlib_for_lto: &[PathBuf],
         modules: Vec<FatLtoInput<Self>>,
-        cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
         diff_fncs: Vec<AutoDiffItem>,
     ) -> Result<ModuleCodegen<Self::Module>, FatalError>;
     /// Performs thin LTO by performing necessary global analysis and returning two
@@ -33,6 +36,8 @@ pub trait WriteBackendMethods: Clone + 'static {
     /// can simply be copied over from the incr. comp. cache.
     fn run_thin_lto(
         cgcx: &CodegenContext<Self>,
+        exported_symbols_for_lto: &[String],
+        each_linked_rlib_for_lto: &[PathBuf],
         modules: Vec<(String, Self::ThinBuffer)>,
         cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
     ) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError>;
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index f584f6c948e..5835660e1c3 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -152,7 +152,7 @@ pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>(
 pub fn mk_eval_cx_for_const_val<'tcx>(
     tcx: TyCtxtAt<'tcx>,
     typing_env: ty::TypingEnv<'tcx>,
-    val: mir::ConstValue<'tcx>,
+    val: mir::ConstValue,
     ty: Ty<'tcx>,
 ) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> {
     let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, typing_env, CanAccessMutGlobal::No);
@@ -172,7 +172,7 @@ pub(super) fn op_to_const<'tcx>(
     ecx: &CompileTimeInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     for_diagnostics: bool,
-) -> ConstValue<'tcx> {
+) -> ConstValue {
     // Handle ZST consistently and early.
     if op.layout.is_zst() {
         return ConstValue::ZeroSized;
@@ -241,10 +241,9 @@ pub(super) fn op_to_const<'tcx>(
                 let (prov, offset) =
                     ptr.into_pointer_or_addr().expect(msg).prov_and_relative_offset();
                 let alloc_id = prov.alloc_id();
-                let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
                 assert!(offset == abi::Size::ZERO, "{}", msg);
                 let meta = b.to_target_usize(ecx).expect(msg);
-                ConstValue::Slice { data, meta }
+                ConstValue::Slice { alloc_id, meta }
             }
             Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty),
         },
@@ -256,7 +255,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
     constant: ConstAlloc<'tcx>,
     key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
-) -> ConstValue<'tcx> {
+) -> ConstValue {
     let cid = key.value;
     let def_id = cid.instance.def.def_id();
     let is_static = tcx.is_static(def_id);
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 0082f90f3b8..624ca1dd2da 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -28,7 +28,7 @@ const VALTREE_MAX_NODES: usize = 100000;
 #[instrument(skip(tcx), level = "debug")]
 pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
     tcx: TyCtxt<'tcx>,
-    val: mir::ConstValue<'tcx>,
+    val: mir::ConstValue,
     ty: Ty<'tcx>,
 ) -> Option<mir::DestructuredConstant<'tcx>> {
     let typing_env = ty::TypingEnv::fully_monomorphized();
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 5ab72c853c4..37c6c4a61d8 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -259,7 +259,7 @@ pub fn valtree_to_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
     typing_env: ty::TypingEnv<'tcx>,
     cv: ty::Value<'tcx>,
-) -> mir::ConstValue<'tcx> {
+) -> mir::ConstValue {
     // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s
     // (those for constants with type bool, int, uint, float or char).
     // For all other types we create an `MPlace` and fill that by walking
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 11e7706fe60..0c888694e49 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -582,8 +582,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         span: Span,
         layout: Option<TyAndLayout<'tcx>>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| {
-            let const_val = val.eval(*ecx.tcx, ecx.typing_env, span).map_err(|err| {
+        let const_val = val.eval(*self.tcx, self.typing_env, span).map_err(|err| {
                 if M::ALL_CONSTS_ARE_PRECHECKED {
                     match err {
                         ErrorHandled::TooGeneric(..) => {},
@@ -599,11 +598,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                         }
                     }
                 }
-                err.emit_note(*ecx.tcx);
+                err.emit_note(*self.tcx);
                 err
             })?;
-            ecx.const_val_to_op(const_val, val.ty(), layout)
-        })
+        self.const_val_to_op(const_val, val.ty(), layout)
     }
 
     #[must_use]
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index e24a355891d..5e3d0a15d8b 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -6,7 +6,7 @@ use std::assert_matches::assert_matches;
 
 use rustc_abi::{FieldIdx, HasDataLayout, Size};
 use rustc_apfloat::ieee::{Double, Half, Quad, Single};
-use rustc_middle::mir::interpret::{read_target_uint, write_target_uint};
+use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
 use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{Ty, TyCtxt};
@@ -17,17 +17,18 @@ use tracing::trace;
 use super::memory::MemoryKind;
 use super::util::ensure_monomorphic_enough;
 use super::{
-    Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy,
-    PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format,
-    interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
+    AllocId, CheckInAllocMsg, ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Pointer,
+    PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, throw_inval,
+    throw_ub_custom, throw_ub_format,
 };
 use crate::fluent_generated as fluent;
 
 /// Directly returns an `Allocation` containing an absolute path representation of the given type.
-pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> {
+pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) {
     let path = crate::util::type_name(tcx, ty);
-    let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes(), ());
-    tcx.mk_const_alloc(alloc)
+    let bytes = path.into_bytes();
+    let len = bytes.len().try_into().unwrap();
+    (tcx.allocate_bytes_dedup(bytes, CTFE_ALLOC_SALT), len)
 }
 impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// Generates a value of `TypeId` for `ty` in-place.
@@ -126,8 +127,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             sym::type_name => {
                 let tp_ty = instance.args.type_at(0);
                 ensure_monomorphic_enough(tcx, tp_ty)?;
-                let alloc = alloc_type_name(tcx, tp_ty);
-                let val = ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() };
+                let (alloc_id, meta) = alloc_type_name(tcx, tp_ty);
+                let val = ConstValue::Slice { alloc_id, meta };
                 let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?;
                 self.copy_op(&val, dest)?;
             }
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index e981f3973ae..e22629993fb 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -12,7 +12,6 @@ use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::{mir, ty};
-use rustc_span::Span;
 use rustc_span::def_id::DefId;
 use rustc_target::callconv::FnAbi;
 
@@ -587,27 +586,6 @@ pub trait Machine<'tcx>: Sized {
         interp_ok(())
     }
 
-    /// Evaluate the given constant. The `eval` function will do all the required evaluation,
-    /// but this hook has the chance to do some pre/postprocessing.
-    #[inline(always)]
-    fn eval_mir_constant<F>(
-        ecx: &InterpCx<'tcx, Self>,
-        val: mir::Const<'tcx>,
-        span: Span,
-        layout: Option<TyAndLayout<'tcx>>,
-        eval: F,
-    ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>
-    where
-        F: Fn(
-            &InterpCx<'tcx, Self>,
-            mir::Const<'tcx>,
-            Span,
-            Option<TyAndLayout<'tcx>>,
-        ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>,
-    {
-        eval(ecx, val, span, layout)
-    }
-
     /// Returns the salt to be used for a deduplicated global alloation.
     /// If the allocation is for a function, the instance is provided as well
     /// (this lets Miri ensure unique addresses for some functions).
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 34297a61648..47bebf5371a 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -1000,7 +1000,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         ptr: Pointer<Option<M::Provenance>>,
     ) -> InterpResult<'tcx, (Ty<'tcx>, u64)> {
         let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?;
-        let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else {
+        let Some(GlobalAlloc::TypeId { ty }) = self.tcx.try_get_global_alloc(alloc_id) else {
             throw_ub_format!("invalid `TypeId` value: not all bytes carry type id metadata")
         };
         interp_ok((ty, offset.bytes()))
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 62cbbae24a8..21afd082a05 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -836,7 +836,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
     pub(crate) fn const_val_to_op(
         &self,
-        val_val: mir::ConstValue<'tcx>,
+        val_val: mir::ConstValue,
         ty: Ty<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
@@ -860,9 +860,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             }
             mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
             mir::ConstValue::ZeroSized => Immediate::Uninit,
-            mir::ConstValue::Slice { data, meta } => {
+            mir::ConstValue::Slice { alloc_id, meta } => {
                 // This is const data, no mutation allowed.
-                let alloc_id = self.tcx.reserve_and_set_memory_alloc(data);
                 let ptr = Pointer::new(CtfeProvenance::from(alloc_id).as_immutable(), Size::ZERO);
                 Immediate::new_slice(self.global_root_pointer(ptr)?.into(), meta, self)
             }
diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs
index f489b05fbbd..c437934eaab 100644
--- a/compiler/rustc_const_eval/src/util/caller_location.rs
+++ b/compiler/rustc_const_eval/src/util/caller_location.rs
@@ -57,7 +57,7 @@ pub(crate) fn const_caller_location_provider(
     file: Symbol,
     line: u32,
     col: u32,
-) -> mir::ConstValue<'_> {
+) -> mir::ConstValue {
     trace!("const_caller_location: {}:{}:{}", file, line, col);
     let mut ecx = mk_eval_cx_to_read_const_val(
         tcx,
diff --git a/compiler/rustc_error_codes/src/error_codes/E0466.md b/compiler/rustc_error_codes/src/error_codes/E0466.md
index 7aefedbc087..28016eb395e 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0466.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0466.md
@@ -1,8 +1,10 @@
+#### Note: this error code is no longer emitted by the compiler.
+
 Macro import declaration was malformed.
 
 Erroneous code examples:
 
-```compile_fail,E0466
+```compile_fail
 #[macro_use(a_macro(another_macro))] // error: invalid import declaration
 extern crate core as some_crate;
 
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 1928cfd9048..25ec5401111 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
 use std::default::Default;
 use std::iter;
 use std::path::Component::Prefix;
@@ -361,17 +362,13 @@ where
 }
 
 /// Represents a thing that maps token trees to Macro Results
-pub trait TTMacroExpander {
+pub trait TTMacroExpander: Any {
     fn expand<'cx>(
         &self,
         ecx: &'cx mut ExtCtxt<'_>,
         span: Span,
         input: TokenStream,
     ) -> MacroExpanderResult<'cx>;
-
-    fn get_unused_rule(&self, _rule_i: usize) -> Option<(&Ident, Span)> {
-        None
-    }
 }
 
 pub type MacroExpanderResult<'cx> = ExpandResult<Box<dyn MacResult + 'cx>, ()>;
@@ -379,7 +376,7 @@ pub type MacroExpanderResult<'cx> = ExpandResult<Box<dyn MacResult + 'cx>, ()>;
 pub type MacroExpanderFn =
     for<'cx> fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>;
 
-impl<F> TTMacroExpander for F
+impl<F: 'static> TTMacroExpander for F
 where
     F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>,
 {
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index 64be7649775..b54dabbb8e2 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -22,7 +22,7 @@ mod placeholders;
 mod proc_macro_server;
 mod stats;
 
-pub use mbe::macro_rules::compile_declarative_macro;
+pub use mbe::macro_rules::{MacroRulesMacroExpander, compile_declarative_macro};
 pub mod base;
 pub mod config;
 pub mod expand;
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 2d792355b27..2f713a09b95 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -128,7 +128,7 @@ pub(super) struct MacroRule {
     rhs: mbe::TokenTree,
 }
 
-struct MacroRulesMacroExpander {
+pub struct MacroRulesMacroExpander {
     node_id: NodeId,
     name: Ident,
     span: Span,
@@ -136,6 +136,14 @@ struct MacroRulesMacroExpander {
     rules: Vec<MacroRule>,
 }
 
+impl MacroRulesMacroExpander {
+    pub fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, Span)> {
+        // If the rhs contains an invocation like `compile_error!`, don't report it as unused.
+        let rule = &self.rules[rule_i];
+        if has_compile_error_macro(&rule.rhs) { None } else { Some((&self.name, rule.lhs_span)) }
+    }
+}
+
 impl TTMacroExpander for MacroRulesMacroExpander {
     fn expand<'cx>(
         &self,
@@ -154,12 +162,6 @@ impl TTMacroExpander for MacroRulesMacroExpander {
             &self.rules,
         ))
     }
-
-    fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, Span)> {
-        // If the rhs contains an invocation like `compile_error!`, don't report it as unused.
-        let rule = &self.rules[rule_i];
-        if has_compile_error_macro(&rule.rhs) { None } else { Some((&self.name, rule.lhs_span)) }
-    }
 }
 
 struct DummyExpander(ErrorGuaranteed);
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index e7898648c2b..e83f6a1df72 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1302,6 +1302,7 @@ impl AttributeExt for Attribute {
             // FIXME: should not be needed anymore when all attrs are parsed
             Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
+            Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
             Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
index 9abae33ffdb..646ff3ca08d 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -447,17 +447,30 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) {
         let mut parents = self.tcx().hir_parent_iter(self_ty.hir_id);
 
-        if let Some((_, hir::Node::AssocItemConstraint(constraint))) = parents.next()
+        if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next()
             && let Some(obj_ty) = constraint.ty()
+            && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next()
         {
-            if let Some((_, hir::Node::TraitRef(..))) = parents.next()
-                && let Some((_, hir::Node::Ty(ty))) = parents.next()
+            if let Some((_, hir::Node::Ty(ty))) = parents.next()
                 && let hir::TyKind::TraitObject(..) = ty.kind
             {
                 // Assoc ty bounds aren't permitted inside trait object types.
                 return;
             }
 
+            if trait_ref
+                .path
+                .segments
+                .iter()
+                .find_map(|seg| {
+                    seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id))
+                })
+                .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No)
+            {
+                // Only consider angle-bracketed args (where we have a `=` to replace with `:`).
+                return;
+            }
+
             let lo = if constraint.gen_args.span_ext.is_dummy() {
                 constraint.ident.span
             } else {
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index be774106abf..df38c3a1214 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -1047,7 +1047,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             }
                         }
                     }
-                    lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
+                    lint.note("for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
 
                     let diagnostic_msg = format!(
                         "add a dummy let to cause {migrated_variables_concat} to be fully captured"
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 1a1cfc9fa6f..69fe7fe83ff 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -593,7 +593,7 @@ lint_non_camel_case_type = {$sort} `{$name}` should have an upper camel case nam
 
 lint_non_fmt_panic = panic message is not a string literal
     .note = this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021
-    .more_info_note = for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+    .more_info_note = for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/panic-macro-consistency.html>
     .supports_fmt_note = the `{$name}!()` macro supports formatting, so there's no need for the `format!()` macro here
     .supports_fmt_suggestion = remove the `format!(..)` macro call
     .display_suggestion = add a "{"{"}{"}"}" format string to `Display` the message
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 73e68834232..eb4c3703dbd 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1654,7 +1654,7 @@ declare_lint! {
     "`...` range patterns are deprecated",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/warnings-promoted-to-error.html>",
     };
 }
 
@@ -1835,7 +1835,7 @@ declare_lint! {
     "detects edition keywords being used as an identifier",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/gen-keyword.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/gen-keyword.html>",
     };
 }
 
diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs
index 263ea6fa070..ff67ed1bc55 100644
--- a/compiler/rustc_lint/src/if_let_rescope.rs
+++ b/compiler/rustc_lint/src/if_let_rescope.rs
@@ -87,7 +87,7 @@ declare_lint! {
     rewriting in `match` is an option to preserve the semantics up to Edition 2021",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/temporary-if-let-scope.html>",
     };
 }
 
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index c17281deff4..b9afb62cf1c 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -71,7 +71,7 @@ declare_lint! {
     "`impl Trait` will capture more lifetimes than possibly intended in edition 2024",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rpit-lifetime-capture.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/rpit-lifetime-capture.html>",
     };
 }
 
diff --git a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
index ce280fef8b6..7de6fbd941b 100644
--- a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
+++ b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
@@ -65,7 +65,7 @@ declare_lint! {
     /// to ensure the macros implement the desired behavior.
     ///
     /// [editions]: https://doc.rust-lang.org/edition-guide/
-    /// [macro matcher fragment specifiers]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html
+    /// [macro matcher fragment specifiers]: https://doc.rust-lang.org/edition-guide/rust-2024/macro-fragment-specifiers.html
     /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
     pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
     Allow,
@@ -73,7 +73,7 @@ declare_lint! {
     To keep the existing behavior, use the `expr_2021` fragment specifier.",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>",
+        reference: "Migration Guide <https://doc.rust-lang.org/edition-guide/rust-2024/macro-fragment-specifiers.html>",
     };
 }
 
diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs
index 00fa0499556..d296ae46f43 100644
--- a/compiler/rustc_lint/src/shadowed_into_iter.rs
+++ b/compiler/rustc_lint/src/shadowed_into_iter.rs
@@ -32,7 +32,7 @@ declare_lint! {
     "detects calling `into_iter` on arrays in Rust 2015 and 2018",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
     };
 }
 
@@ -61,7 +61,7 @@ declare_lint! {
     "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/intoiterator-box-slice.html>"
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/intoiterator-box-slice.html>"
     };
 }
 
diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs
index 4dda3c7951b..16e1fb0192b 100644
--- a/compiler/rustc_lint/src/static_mut_refs.rs
+++ b/compiler/rustc_lint/src/static_mut_refs.rs
@@ -54,7 +54,7 @@ declare_lint! {
     "creating a shared reference to mutable static",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>",
         explain_reason: false,
     };
     @edition Edition2024 => Deny;
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index a08d68e2d15..b1edb5c3044 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -1814,7 +1814,7 @@ declare_lint! {
     "suggest using `dyn Trait` for trait objects",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/warnings-promoted-to-error.html>",
     };
 }
 
@@ -2472,7 +2472,7 @@ declare_lint! {
     "unsafe operations in unsafe functions without an explicit unsafe block are deprecated",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html>",
         explain_reason: false
     };
     @edition Edition2024 => Warn;
@@ -3445,7 +3445,7 @@ declare_lint! {
     "detects usage of old versions of or-patterns",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/or-patterns-macro-rules.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html>",
     };
 }
 
@@ -3494,7 +3494,7 @@ declare_lint! {
         prelude in future editions",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/prelude.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/prelude.html>",
     };
 }
 
@@ -3534,7 +3534,7 @@ declare_lint! {
         prelude in future editions",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/prelude.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/prelude.html>",
     };
 }
 
@@ -3571,7 +3571,7 @@ declare_lint! {
     "identifiers that will be parsed as a prefix in Rust 2021",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/reserving-syntax.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html>",
     };
     crate_level_only
 }
@@ -4100,7 +4100,7 @@ declare_lint! {
     "never type fallback affecting unsafe function calls",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/never-type-fallback.html>",
         report_in_deps: true,
     };
     @edition Edition2024 => Deny;
@@ -4155,7 +4155,7 @@ declare_lint! {
     "never type fallback affecting unsafe function calls",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionAndFutureReleaseError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/never-type-fallback.html>",
         report_in_deps: true,
     };
     report_in_external_macro
@@ -4740,7 +4740,7 @@ declare_lint! {
     "detects unsafe functions being used as safe functions",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/newly-unsafe-functions.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/newly-unsafe-functions.html>",
     };
 }
 
@@ -4776,7 +4776,7 @@ declare_lint! {
     "detects missing unsafe keyword on extern declarations",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-extern.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-extern.html>",
     };
 }
 
@@ -4817,7 +4817,7 @@ declare_lint! {
     "detects unsafe attributes outside of unsafe",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-attributes.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-attributes.html>",
     };
 }
 
@@ -5014,7 +5014,7 @@ declare_lint! {
     "Detect and warn on significant change in drop order in tail expression location",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/temporary-tail-expr-scope.html>",
     };
 }
 
@@ -5053,7 +5053,7 @@ declare_lint! {
     "will be parsed as a guarded string in Rust 2024",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/reserved-syntax.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/reserved-syntax.html>",
     };
     crate_level_only
 }
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 1006ea3ba10..101f5ccb7a4 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -21,6 +21,7 @@ mod symbols;
 mod try_from;
 mod type_foldable;
 mod type_visitable;
+mod visitable;
 
 // Reads the rust version (e.g. "1.75.0") from the CFG_RELEASE env var and
 // produces a `RustcVersion` literal containing that version (e.g.
@@ -101,6 +102,16 @@ decl_derive!(
     /// visited (and its type is not required to implement `TypeVisitable`).
     type_visitable::type_visitable_derive
 );
+decl_derive!(
+    [Walkable, attributes(visitable)] =>
+    /// Derives `Walkable` for the annotated `struct` or `enum` (`union` is not supported).
+    ///
+    /// Each field of the struct or enum variant will be visited in definition order, using the
+    /// `Walkable` implementation for its type. However, if a field of a struct or an enum
+    /// variant is annotated with `#[visitable(ignore)]` then that field will not be
+    /// visited (and its type is not required to implement `Walkable`).
+    visitable::visitable_derive
+);
 decl_derive!([Lift, attributes(lift)] => lift::lift_derive);
 decl_derive!(
     [Diagnostic, attributes(
diff --git a/compiler/rustc_macros/src/visitable.rs b/compiler/rustc_macros/src/visitable.rs
new file mode 100644
index 00000000000..a7a82538eab
--- /dev/null
+++ b/compiler/rustc_macros/src/visitable.rs
@@ -0,0 +1,82 @@
+use quote::quote;
+use synstructure::BindingInfo;
+
+pub(super) fn visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    if let syn::Data::Union(_) = s.ast().data {
+        panic!("cannot derive on union")
+    }
+
+    let has_attr = |bind: &BindingInfo<'_>, name| {
+        let mut found = false;
+        bind.ast().attrs.iter().for_each(|attr| {
+            if !attr.path().is_ident("visitable") {
+                return;
+            }
+            let _ = attr.parse_nested_meta(|nested| {
+                if nested.path.is_ident(name) {
+                    found = true;
+                }
+                Ok(())
+            });
+        });
+        found
+    };
+
+    let get_attr = |bind: &BindingInfo<'_>, name: &str| {
+        let mut content = None;
+        bind.ast().attrs.iter().for_each(|attr| {
+            if !attr.path().is_ident("visitable") {
+                return;
+            }
+            let _ = attr.parse_nested_meta(|nested| {
+                if nested.path.is_ident(name) {
+                    let value = nested.value()?;
+                    let value = value.parse()?;
+                    content = Some(value);
+                }
+                Ok(())
+            });
+        });
+        content
+    };
+
+    s.add_bounds(synstructure::AddBounds::Generics);
+    s.bind_with(|_| synstructure::BindStyle::Ref);
+    let ref_visit = s.each(|bind| {
+        let extra = get_attr(bind, "extra").unwrap_or(quote! {});
+        if has_attr(bind, "ignore") {
+            quote! {}
+        } else {
+            quote! { rustc_ast_ir::try_visit!(crate::visit::Visitable::visit(#bind, __visitor, (#extra))) }
+        }
+    });
+
+    s.bind_with(|_| synstructure::BindStyle::RefMut);
+    let mut_visit = s.each(|bind| {
+        let extra = get_attr(bind, "extra").unwrap_or(quote! {});
+        if has_attr(bind, "ignore") {
+            quote! {}
+        } else {
+            quote! { crate::mut_visit::MutVisitable::visit_mut(#bind, __visitor, (#extra)) }
+        }
+    });
+
+    s.gen_impl(quote! {
+        gen impl<'__ast, __V> crate::visit::Walkable<'__ast, __V> for @Self
+            where __V: crate::visit::Visitor<'__ast>,
+        {
+            fn walk_ref(&'__ast self, __visitor: &mut __V) -> __V::Result {
+                match *self { #ref_visit }
+                <__V::Result as rustc_ast_ir::visit::VisitorResult>::output()
+            }
+        }
+
+        gen impl<__V> crate::mut_visit::MutWalkable<__V> for @Self
+            where __V: crate::mut_visit::MutVisitor,
+        {
+            fn walk_mut(&mut self, __visitor: &mut __V) {
+                match *self { #mut_visit }
+            }
+        }
+    })
+}
diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl
index 3bef5ca114b..4d3e879a098 100644
--- a/compiler/rustc_metadata/messages.ftl
+++ b/compiler/rustc_metadata/messages.ftl
@@ -330,3 +330,6 @@ metadata_wasm_import_form =
 
 metadata_whole_archive_needs_static =
     linking modifier `whole-archive` is only compatible with `static` linking kind
+
+metadata_raw_dylib_malformed =
+    link name must be well-formed if link kind is `raw-dylib`
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index 4a3b43167cf..0332dba1077 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -815,3 +815,10 @@ pub struct AsyncDropTypesInDependency {
     pub extern_crate: Symbol,
     pub local_crate: Symbol,
 }
+
+#[derive(Diagnostic)]
+#[diag(metadata_raw_dylib_malformed)]
+pub struct RawDylibMalformed {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 4d276f814ef..ed0f084ea83 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -700,8 +700,21 @@ impl<'tcx> Collector<'tcx> {
             .link_ordinal
             .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
 
+        let name = codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item));
+
+        if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
+            let name = name.as_str();
+            if name.contains('\0') {
+                self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
+            } else if let Some((left, right)) = name.split_once('@')
+                && (left.is_empty() || right.is_empty() || right.contains('@'))
+            {
+                self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
+            }
+        }
+
         DllImport {
-            name: codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item)),
+            name,
             import_name_type,
             calling_convention,
             span,
diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs
index c5ce6efcb81..9d2f0a45237 100644
--- a/compiler/rustc_middle/src/hooks/mod.rs
+++ b/compiler/rustc_middle/src/hooks/mod.rs
@@ -50,10 +50,10 @@ macro_rules! declare_hooks {
 declare_hooks! {
     /// Tries to destructure an `mir::Const` ADT or array into its variant index
     /// and its field values. This should only be used for pretty printing.
-    hook try_destructure_mir_constant_for_user_output(val: mir::ConstValue<'tcx>, ty: Ty<'tcx>) -> Option<mir::DestructuredConstant<'tcx>>;
+    hook try_destructure_mir_constant_for_user_output(val: mir::ConstValue, ty: Ty<'tcx>) -> Option<mir::DestructuredConstant<'tcx>>;
 
     /// Getting a &core::panic::Location referring to a span.
-    hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>;
+    hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue;
 
     /// Returns `true` if this def is a function-like thing that is eligible for
     /// coverage instrumentation under `-Cinstrument-coverage`.
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index fb941977528..96131d47a17 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -9,9 +9,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol};
 use rustc_type_ir::TypeVisitableExt;
 
 use super::interpret::ReportedErrorInfo;
-use crate::mir::interpret::{
-    AllocId, AllocRange, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar, alloc_range,
-};
+use crate::mir::interpret::{AllocId, AllocRange, ErrorHandled, GlobalAlloc, Scalar, alloc_range};
 use crate::mir::{Promoted, pretty_print_const_value};
 use crate::ty::print::{pretty_print_const, with_no_trimmed_paths};
 use crate::ty::{self, ConstKind, GenericArgsRef, ScalarInt, Ty, TyCtxt};
@@ -33,8 +31,8 @@ pub struct ConstAlloc<'tcx> {
 /// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
 /// array length computations, enum discriminants and the pattern matching logic.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
-#[derive(HashStable, Lift)]
-pub enum ConstValue<'tcx> {
+#[derive(HashStable)]
+pub enum ConstValue {
     /// Used for types with `layout::abi::Scalar` ABI.
     ///
     /// Not using the enum `Value` to encode that this must not be `Uninit`.
@@ -52,7 +50,7 @@ pub enum ConstValue<'tcx> {
     Slice {
         /// The allocation storing the slice contents.
         /// This always points to the beginning of the allocation.
-        data: ConstAllocation<'tcx>,
+        alloc_id: AllocId,
         /// The metadata field of the reference.
         /// This is a "target usize", so we use `u64` as in the interpreter.
         meta: u64,
@@ -75,9 +73,9 @@ pub enum ConstValue<'tcx> {
 }
 
 #[cfg(target_pointer_width = "64")]
-rustc_data_structures::static_assert_size!(ConstValue<'_>, 24);
+rustc_data_structures::static_assert_size!(ConstValue, 24);
 
-impl<'tcx> ConstValue<'tcx> {
+impl ConstValue {
     #[inline]
     pub fn try_to_scalar(&self) -> Option<Scalar> {
         match *self {
@@ -98,11 +96,11 @@ impl<'tcx> ConstValue<'tcx> {
         self.try_to_scalar_int()?.try_into().ok()
     }
 
-    pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
+    pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Option<u64> {
         Some(self.try_to_scalar_int()?.to_target_usize(tcx))
     }
 
-    pub fn try_to_bits_for_ty(
+    pub fn try_to_bits_for_ty<'tcx>(
         &self,
         tcx: TyCtxt<'tcx>,
         typing_env: ty::TypingEnv<'tcx>,
@@ -132,12 +130,15 @@ impl<'tcx> ConstValue<'tcx> {
     }
 
     /// Must only be called on constants of type `&str` or `&[u8]`!
-    pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
-        let (data, start, end) = match self {
+    pub fn try_get_slice_bytes_for_diagnostics<'tcx>(
+        &self,
+        tcx: TyCtxt<'tcx>,
+    ) -> Option<&'tcx [u8]> {
+        let (alloc_id, start, len) = match self {
             ConstValue::Scalar(_) | ConstValue::ZeroSized => {
                 bug!("`try_get_slice_bytes` on non-slice constant")
             }
-            &ConstValue::Slice { data, meta } => (data, 0, meta),
+            &ConstValue::Slice { alloc_id, meta } => (alloc_id, 0, meta),
             &ConstValue::Indirect { alloc_id, offset } => {
                 // The reference itself is stored behind an indirection.
                 // Load the reference, and then load the actual slice contents.
@@ -170,26 +171,29 @@ impl<'tcx> ConstValue<'tcx> {
                 // Non-empty slice, must have memory. We know this is a relative pointer.
                 let (inner_prov, offset) =
                     ptr.into_pointer_or_addr().ok()?.prov_and_relative_offset();
-                let data = tcx.global_alloc(inner_prov.alloc_id()).unwrap_memory();
-                (data, offset.bytes(), offset.bytes() + len)
+                (inner_prov.alloc_id(), offset.bytes(), len)
             }
         };
 
+        let data = tcx.global_alloc(alloc_id).unwrap_memory();
+
         // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
         let start = start.try_into().unwrap();
-        let end = end.try_into().unwrap();
+        let end = start + usize::try_from(len).unwrap();
         Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
     }
 
     /// Check if a constant may contain provenance information. This is used by MIR opts.
     /// Can return `true` even if there is no provenance.
-    pub fn may_have_provenance(&self, tcx: TyCtxt<'tcx>, size: Size) -> bool {
+    pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool {
         match *self {
             ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
             ConstValue::Scalar(Scalar::Ptr(..)) => return true,
             // It's hard to find out the part of the allocation we point to;
             // just conservatively check everything.
-            ConstValue::Slice { data, meta: _ } => !data.inner().provenance().ptrs().is_empty(),
+            ConstValue::Slice { alloc_id, meta: _ } => {
+                !tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty()
+            }
             ConstValue::Indirect { alloc_id, offset } => !tcx
                 .global_alloc(alloc_id)
                 .unwrap_memory()
@@ -200,7 +204,7 @@ impl<'tcx> ConstValue<'tcx> {
     }
 
     /// Check if a constant only contains uninitialized bytes.
-    pub fn all_bytes_uninit(&self, tcx: TyCtxt<'tcx>) -> bool {
+    pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool {
         let ConstValue::Indirect { alloc_id, .. } = self else {
             return false;
         };
@@ -247,7 +251,7 @@ pub enum Const<'tcx> {
 
     /// This constant cannot go back into the type system, as it represents
     /// something the type system cannot handle (e.g. pointers).
-    Val(ConstValue<'tcx>, Ty<'tcx>),
+    Val(ConstValue, Ty<'tcx>),
 }
 
 impl<'tcx> Const<'tcx> {
@@ -343,7 +347,7 @@ impl<'tcx> Const<'tcx> {
         tcx: TyCtxt<'tcx>,
         typing_env: ty::TypingEnv<'tcx>,
         span: Span,
-    ) -> Result<ConstValue<'tcx>, ErrorHandled> {
+    ) -> Result<ConstValue, ErrorHandled> {
         match self {
             Const::Ty(_, c) => {
                 if c.has_non_region_param() {
@@ -440,7 +444,7 @@ impl<'tcx> Const<'tcx> {
     }
 
     #[inline]
-    pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
+    pub fn from_value(val: ConstValue, ty: Ty<'tcx>) -> Self {
         Self::Val(val, ty)
     }
 
@@ -487,9 +491,8 @@ impl<'tcx> Const<'tcx> {
     /// taking into account even pointer identity tests.
     pub fn is_deterministic(&self) -> bool {
         // Some constants may generate fresh allocations for pointers they contain,
-        // so using the same constant twice can yield two different results:
-        // - valtrees purposefully generate new allocations
-        // - ConstValue::Slice also generate new allocations
+        // so using the same constant twice can yield two different results.
+        // Notably, valtrees purposefully generate new allocations.
         match self {
             Const::Ty(_, c) => match c.kind() {
                 ty::ConstKind::Param(..) => true,
@@ -507,11 +510,11 @@ impl<'tcx> Const<'tcx> {
                 | ty::ConstKind::Placeholder(..) => bug!(),
             },
             Const::Unevaluated(..) => false,
-            // If the same slice appears twice in the MIR, we cannot guarantee that we will
-            // give the same `AllocId` to the data.
-            Const::Val(ConstValue::Slice { .. }, _) => false,
             Const::Val(
-                ConstValue::ZeroSized | ConstValue::Scalar(_) | ConstValue::Indirect { .. },
+                ConstValue::Slice { .. }
+                | ConstValue::ZeroSized
+                | ConstValue::Scalar(_)
+                | ConstValue::Indirect { .. },
                 _,
             ) => true,
         }
@@ -574,7 +577,7 @@ impl<'tcx> Display for Const<'tcx> {
 /// Const-related utilities
 
 impl<'tcx> TyCtxt<'tcx> {
-    pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> {
+    pub fn span_as_caller_location(self, span: Span) -> ConstValue {
         let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
         let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
         self.const_caller_location(
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 3e68afbfabd..2b0cfb86564 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -137,7 +137,7 @@ impl<'tcx> ValTreeCreationError<'tcx> {
 
 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
 pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>;
-pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
+pub type EvalToConstValueResult<'tcx> = Result<ConstValue, ErrorHandled>;
 pub type EvalToValTreeResult<'tcx> = Result<ValTree<'tcx>, ValTreeCreationError<'tcx>>;
 
 #[cfg(target_pointer_width = "64")]
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 2d7ddd105bd..105736b9e24 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -143,10 +143,8 @@ impl<'tcx> MonoItem<'tcx> {
         };
 
         // Similarly, the executable entrypoint must be instantiated exactly once.
-        if let Some((entry_def_id, _)) = tcx.entry_fn(()) {
-            if instance.def_id() == entry_def_id {
-                return InstantiationMode::GloballyShared { may_conflict: false };
-            }
+        if tcx.is_entrypoint(instance.def_id()) {
+            return InstantiationMode::GloballyShared { may_conflict: false };
         }
 
         // If the function is #[naked] or contains any other attribute that requires exactly-once
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 8e403dfddae..809cdb329f7 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1465,7 +1465,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
                 self.push(&format!("+ user_ty: {user_ty:?}"));
             }
 
-            let fmt_val = |val: ConstValue<'tcx>, ty: Ty<'tcx>| {
+            let fmt_val = |val: ConstValue, ty: Ty<'tcx>| {
                 let tcx = self.tcx;
                 rustc_data_structures::make_display(move |fmt| {
                     pretty_print_const_value_tcx(tcx, val, ty, fmt)
@@ -1562,16 +1562,12 @@ pub fn write_allocations<'tcx>(
         alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id())
     }
 
-    fn alloc_id_from_const_val(val: ConstValue<'_>) -> Option<AllocId> {
+    fn alloc_id_from_const_val(val: ConstValue) -> Option<AllocId> {
         match val {
             ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()),
             ConstValue::Scalar(interpret::Scalar::Int { .. }) => None,
             ConstValue::ZeroSized => None,
-            ConstValue::Slice { .. } => {
-                // `u8`/`str` slices, shouldn't contain pointers that we want to print.
-                None
-            }
-            ConstValue::Indirect { alloc_id, .. } => {
+            ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => {
                 // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
                 // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.
                 Some(alloc_id)
@@ -1885,7 +1881,7 @@ fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Resul
 fn comma_sep<'tcx>(
     tcx: TyCtxt<'tcx>,
     fmt: &mut Formatter<'_>,
-    elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
+    elems: Vec<(ConstValue, Ty<'tcx>)>,
 ) -> fmt::Result {
     let mut first = true;
     for (ct, ty) in elems {
@@ -1900,7 +1896,7 @@ fn comma_sep<'tcx>(
 
 fn pretty_print_const_value_tcx<'tcx>(
     tcx: TyCtxt<'tcx>,
-    ct: ConstValue<'tcx>,
+    ct: ConstValue,
     ty: Ty<'tcx>,
     fmt: &mut Formatter<'_>,
 ) -> fmt::Result {
@@ -1947,7 +1943,7 @@ fn pretty_print_const_value_tcx<'tcx>(
             let ct = tcx.lift(ct).unwrap();
             let ty = tcx.lift(ty).unwrap();
             if let Some(contents) = tcx.try_destructure_mir_constant_for_user_output(ct, ty) {
-                let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
+                let fields: Vec<(ConstValue, Ty<'_>)> = contents.fields.to_vec();
                 match *ty.kind() {
                     ty::Array(..) => {
                         fmt.write_str("[")?;
@@ -2028,7 +2024,7 @@ fn pretty_print_const_value_tcx<'tcx>(
 }
 
 pub(crate) fn pretty_print_const_value<'tcx>(
-    ct: ConstValue<'tcx>,
+    ct: ConstValue,
     ty: Ty<'tcx>,
     fmt: &mut Formatter<'_>,
 ) -> fmt::Result {
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 3fc05f2caf2..a8a95c699d8 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -173,5 +173,5 @@ pub enum AnnotationSource {
 #[derive(Copy, Clone, Debug, HashStable)]
 pub struct DestructuredConstant<'tcx> {
     pub variant: Option<VariantIdx>,
-    pub fields: &'tcx [(ConstValue<'tcx>, Ty<'tcx>)],
+    pub fields: &'tcx [(ConstValue, Ty<'tcx>)],
 }
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index f138c5ca039..dab5900b4ab 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -153,8 +153,8 @@ impl EraseType for Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled> {
     type Result = [u8; size_of::<Result<mir::ConstAlloc<'static>, mir::interpret::ErrorHandled>>()];
 }
 
-impl EraseType for Result<mir::ConstValue<'_>, mir::interpret::ErrorHandled> {
-    type Result = [u8; size_of::<Result<mir::ConstValue<'static>, mir::interpret::ErrorHandled>>()];
+impl EraseType for Result<mir::ConstValue, mir::interpret::ErrorHandled> {
+    type Result = [u8; size_of::<Result<mir::ConstValue, mir::interpret::ErrorHandled>>()];
 }
 
 impl EraseType for EvalToValTreeResult<'_> {
@@ -301,6 +301,7 @@ trivial! {
     rustc_middle::middle::resolve_bound_vars::ResolvedArg,
     rustc_middle::middle::stability::DeprecationEntry,
     rustc_middle::mir::ConstQualifs,
+    rustc_middle::mir::ConstValue,
     rustc_middle::mir::interpret::AllocId,
     rustc_middle::mir::interpret::CtfeProvenance,
     rustc_middle::mir::interpret::ErrorHandled,
@@ -362,7 +363,6 @@ tcx_lifetime! {
     rustc_middle::mir::Const,
     rustc_middle::mir::DestructuredConstant,
     rustc_middle::mir::ConstAlloc,
-    rustc_middle::mir::ConstValue,
     rustc_middle::mir::interpret::GlobalId,
     rustc_middle::mir::interpret::LitToConstInput,
     rustc_middle::mir::interpret::EvalStaticInitializerRawResult,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index ad7f4973e23..b0d579a546f 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1363,7 +1363,7 @@ rustc_queries! {
     }
 
     /// Converts a type-level constant value into a MIR constant value.
-    query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue<'tcx> {
+    query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue {
         desc { "converting type-level constant value to MIR constant value"}
     }
 
@@ -2152,9 +2152,6 @@ rustc_queries! {
         desc { |tcx| "collecting child items of module `{}`", tcx.def_path_str(def_id) }
         separate_provide_extern
     }
-    query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option<CrateNum> {
-        desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id) }
-    }
 
     /// Gets the number of definitions in a foreign crate.
     ///
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 51db92ecd78..66d1335e763 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -5,7 +5,7 @@
 pub mod tls;
 
 use std::assert_matches::debug_assert_matches;
-use std::borrow::Borrow;
+use std::borrow::{Borrow, Cow};
 use std::cmp::Ordering;
 use std::env::VarError;
 use std::ffi::OsStr;
@@ -1625,7 +1625,11 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Allocates a read-only byte or string literal for `mir::interpret` with alignment 1.
     /// Returns the same `AllocId` if called again with the same bytes.
-    pub fn allocate_bytes_dedup(self, bytes: &[u8], salt: usize) -> interpret::AllocId {
+    pub fn allocate_bytes_dedup<'a>(
+        self,
+        bytes: impl Into<Cow<'a, [u8]>>,
+        salt: usize,
+    ) -> interpret::AllocId {
         // Create an allocation that just contains these bytes.
         let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes, ());
         let alloc = self.mk_const_alloc(alloc);
@@ -3373,6 +3377,11 @@ impl<'tcx> TyCtxt<'tcx> {
         self.resolutions(()).module_children.get(&def_id).map_or(&[], |v| &v[..])
     }
 
+    /// Return the crate imported by given use item.
+    pub fn extern_mod_stmt_cnum(self, def_id: LocalDefId) -> Option<CrateNum> {
+        self.resolutions(()).extern_crate_map.get(&def_id).copied()
+    }
+
     pub fn resolver_for_lowering(self) -> &'tcx Steal<(ty::ResolverAstLowering, Arc<ast::Crate>)> {
         self.resolver_for_lowering_raw(()).0
     }
@@ -3410,6 +3419,20 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn do_not_recommend_impl(self, def_id: DefId) -> bool {
         self.get_diagnostic_attr(def_id, sym::do_not_recommend).is_some()
     }
+
+    /// Whether this def is one of the special bin crate entrypoint functions that must have a
+    /// monomorphization and also not be internalized in the bin crate.
+    pub fn is_entrypoint(self, def_id: DefId) -> bool {
+        if self.is_lang_item(def_id, LangItem::Start) {
+            return true;
+        }
+        if let Some((entry_def_id, _)) = self.entry_fn(())
+            && entry_def_id == def_id
+        {
+            return true;
+        }
+        false
+    }
 }
 
 /// Parameter attributes that can only be determined by examining the body of a function instead
@@ -3428,8 +3451,6 @@ pub struct DeducedParamAttrs {
 }
 
 pub fn provide(providers: &mut Providers) {
-    providers.extern_mod_stmt_cnum =
-        |tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned();
     providers.is_panic_runtime =
         |tcx, LocalCrate| contains_name(tcx.hir_krate_attrs(), sym::panic_runtime);
     providers.is_compiler_builtins =
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index ab31d943408..a5fdce93e4b 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -4,6 +4,7 @@
 //! to help with the tedium.
 
 use std::fmt::{self, Debug};
+use std::marker::PhantomData;
 
 use rustc_abi::TyAndLayout;
 use rustc_hir::def::Namespace;
@@ -234,6 +235,7 @@ TrivialLiftImpls! {
     rustc_abi::ExternAbi,
     rustc_abi::Size,
     rustc_hir::Safety,
+    rustc_middle::mir::ConstValue,
     rustc_type_ir::BoundConstness,
     rustc_type_ir::PredicatePolarity,
     // tidy-alphabetical-end
@@ -250,7 +252,7 @@ TrivialTypeTraversalImpls! {
     crate::mir::BlockTailInfo,
     crate::mir::BorrowKind,
     crate::mir::CastKind,
-    crate::mir::ConstValue<'tcx>,
+    crate::mir::ConstValue,
     crate::mir::CoroutineSavedLocal,
     crate::mir::FakeReadCause,
     crate::mir::Local,
@@ -311,6 +313,13 @@ TrivialTypeTraversalAndLiftImpls! {
 ///////////////////////////////////////////////////////////////////////////
 // Lift implementations
 
+impl<'tcx> Lift<TyCtxt<'tcx>> for PhantomData<&()> {
+    type Lifted = PhantomData<&'tcx ()>;
+    fn lift_to_interner(self, _: TyCtxt<'tcx>) -> Option<Self::Lifted> {
+        Some(PhantomData)
+    }
+}
+
 impl<'tcx, T: Lift<TyCtxt<'tcx>>> Lift<TyCtxt<'tcx>> for Option<T> {
     type Lifted = Option<T::Lifted>;
     fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs
index d0d0c21463f..0e0c7a7fa4f 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs
@@ -3,7 +3,7 @@
 use rustc_abi::Size;
 use rustc_ast as ast;
 use rustc_hir::LangItem;
-use rustc_middle::mir::interpret::{Allocation, CTFE_ALLOC_SALT, LitToConstInput, Scalar};
+use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, LitToConstInput, Scalar};
 use rustc_middle::mir::*;
 use rustc_middle::thir::*;
 use rustc_middle::ty::{
@@ -120,17 +120,18 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>
 
     let value = match (lit, lit_ty.kind()) {
         (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
-            let s = s.as_str();
-            let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes(), ());
-            let allocation = tcx.mk_const_alloc(allocation);
-            ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
+            let s = s.as_str().as_bytes();
+            let len = s.len();
+            let allocation = tcx.allocate_bytes_dedup(s, CTFE_ALLOC_SALT);
+            ConstValue::Slice { alloc_id: allocation, meta: len.try_into().unwrap() }
         }
-        (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _))
+        (ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _))
             if matches!(inner_ty.kind(), ty::Slice(_)) =>
         {
-            let allocation = Allocation::from_bytes_byte_aligned_immutable(data.as_byte_str(), ());
-            let allocation = tcx.mk_const_alloc(allocation);
-            ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
+            let data = byte_sym.as_byte_str();
+            let len = data.len();
+            let allocation = tcx.allocate_bytes_dedup(data, CTFE_ALLOC_SALT);
+            ConstValue::Slice { alloc_id: allocation, meta: len.try_into().unwrap() }
         }
         (ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
             let id = tcx.allocate_bytes_dedup(byte_sym.as_byte_str(), CTFE_ALLOC_SALT);
@@ -138,10 +139,10 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>
         }
         (ast::LitKind::CStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) =>
         {
-            let allocation =
-                Allocation::from_bytes_byte_aligned_immutable(byte_sym.as_byte_str(), ());
-            let allocation = tcx.mk_const_alloc(allocation);
-            ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
+            let data = byte_sym.as_byte_str();
+            let len = data.len();
+            let allocation = tcx.allocate_bytes_dedup(data, CTFE_ALLOC_SALT);
+            ConstValue::Slice { alloc_id: allocation, meta: len.try_into().unwrap() }
         }
         (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
             ConstValue::Scalar(Scalar::from_uint(n, Size::from_bytes(1)))
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index 3d5f6f4cf45..855cd2f3bc0 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -1045,11 +1045,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     }
 }
 
-fn parse_float_into_constval<'tcx>(
-    num: Symbol,
-    float_ty: ty::FloatTy,
-    neg: bool,
-) -> Option<ConstValue<'tcx>> {
+fn parse_float_into_constval(num: Symbol, float_ty: ty::FloatTy, neg: bool) -> Option<ConstValue> {
     parse_float_into_scalar(num, float_ty, neg).map(|s| ConstValue::Scalar(s.into()))
 }
 
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index ec76076020e..ddeae093df5 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,7 +1,8 @@
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::mir;
 use rustc_middle::ty::TyCtxt;
-use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
+use rustc_span::source_map::SourceMap;
+use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span};
 use tracing::instrument;
 
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
@@ -83,8 +84,18 @@ pub(super) fn extract_refined_covspans<'tcx>(
     // Discard any span that overlaps with a hole.
     discard_spans_overlapping_holes(&mut covspans, &holes);
 
-    // Perform more refinement steps after holes have been dealt with.
+    // Discard spans that overlap in unwanted ways.
     let mut covspans = remove_unwanted_overlapping_spans(covspans);
+
+    // For all empty spans, either enlarge them to be non-empty, or discard them.
+    let source_map = tcx.sess.source_map();
+    covspans.retain_mut(|covspan| {
+        let Some(span) = ensure_non_empty_span(source_map, covspan.span) else { return false };
+        covspan.span = span;
+        true
+    });
+
+    // Merge covspans that can be merged.
     covspans.dedup_by(|b, a| a.merge_if_eligible(b));
 
     code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
@@ -230,3 +241,26 @@ fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
         // - Both have the same start and span A extends further right
         .then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse())
 }
+
+fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
+    if !span.is_empty() {
+        return Some(span);
+    }
+
+    // The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
+    source_map
+        .span_to_source(span, |src, start, end| try {
+            // Adjusting span endpoints by `BytePos(1)` is normally a bug,
+            // but in this case we have specifically checked that the character
+            // we're skipping over is one of two specific ASCII characters, so
+            // adjusting by exactly 1 byte is correct.
+            if src.as_bytes().get(end).copied() == Some(b'{') {
+                Some(span.with_hi(span.hi() + BytePos(1)))
+            } else if start > 0 && src.as_bytes()[start - 1] == b'}' {
+                Some(span.with_lo(span.lo() - BytePos(1)))
+            } else {
+                None
+            }
+        })
+        .ok()?
+}
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 6657f89ceb5..dc99b67a1e8 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -1542,7 +1542,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 fn op_to_prop_const<'tcx>(
     ecx: &mut InterpCx<'tcx, DummyMachine>,
     op: &OpTy<'tcx>,
-) -> Option<ConstValue<'tcx>> {
+) -> Option<ConstValue> {
     // Do not attempt to propagate unsized locals.
     if op.layout.is_unsized() {
         return None;
diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs
index 86e2bf6cb3c..b03518de00a 100644
--- a/compiler/rustc_mir_transform/src/impossible_predicates.rs
+++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs
@@ -27,7 +27,7 @@
 //! it's usually never invoked in this way.
 
 use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind};
-use rustc_middle::ty::{TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{TyCtxt, TypeFlags, TypeVisitableExt};
 use rustc_trait_selection::traits;
 use tracing::trace;
 
@@ -36,14 +36,23 @@ use crate::pass_manager::MirPass;
 pub(crate) struct ImpossiblePredicates;
 
 impl<'tcx> MirPass<'tcx> for ImpossiblePredicates {
+    #[tracing::instrument(level = "trace", skip(self, tcx, body))]
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        let predicates = tcx
-            .predicates_of(body.source.def_id())
-            .predicates
-            .iter()
-            .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
-        if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
-            trace!("found unsatisfiable predicates for {:?}", body.source);
+        tracing::trace!(def_id = ?body.source.def_id());
+        let predicates = tcx.predicates_of(body.source.def_id()).instantiate_identity(tcx);
+        tracing::trace!(?predicates);
+        let predicates = predicates.predicates.into_iter().filter(|p| {
+            !p.has_type_flags(
+                // Only consider global clauses to simplify.
+                TypeFlags::HAS_FREE_LOCAL_NAMES
+                // Clauses that refer to unevaluated constants as they cause cycles.
+                | TypeFlags::HAS_CT_PROJECTION,
+            )
+        });
+        let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect();
+        tracing::trace!(?predicates);
+        if predicates.references_error() || traits::impossible_predicates(tcx, predicates) {
+            trace!("found unsatisfiable predicates");
             // Clear the body to only contain a single `unreachable` statement.
             let bbs = body.basic_blocks.as_mut();
             bbs.raw.truncate(1);
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 91c8e64ce9a..1bfd83d97ac 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -659,10 +659,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
     }
 
     /// Evaluates a *not yet monomorphized* constant.
-    fn eval_constant(
-        &mut self,
-        constant: &mir::ConstOperand<'tcx>,
-    ) -> Option<mir::ConstValue<'tcx>> {
+    fn eval_constant(&mut self, constant: &mir::ConstOperand<'tcx>) -> Option<mir::ConstValue> {
         let const_ = self.monomorphize(constant.const_);
         // Evaluate the constant. This makes const eval failure a collection-time error (rather than
         // a codegen-time error). rustc stops after collection if there was an error, so this
@@ -1355,19 +1352,15 @@ fn visit_mentioned_item<'tcx>(
 #[instrument(skip(tcx, output), level = "debug")]
 fn collect_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
-    value: mir::ConstValue<'tcx>,
+    value: mir::ConstValue,
     output: &mut MonoItems<'tcx>,
 ) {
     match value {
         mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => {
             collect_alloc(tcx, ptr.provenance.alloc_id(), output)
         }
-        mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
-        mir::ConstValue::Slice { data, meta: _ } => {
-            for &prov in data.inner().provenance().ptrs().values() {
-                collect_alloc(tcx, prov.alloc_id(), output);
-            }
-        }
+        mir::ConstValue::Indirect { alloc_id, .. }
+        | mir::ConstValue::Slice { alloc_id, meta: _ } => collect_alloc(tcx, alloc_id, output),
         _ => {}
     }
 }
@@ -1582,6 +1575,15 @@ impl<'v> RootCollector<'_, 'v> {
             return;
         };
 
+        let main_instance = Instance::mono(self.tcx, main_def_id);
+        if self.tcx.should_codegen_locally(main_instance) {
+            self.output.push(create_fn_mono_item(
+                self.tcx,
+                main_instance,
+                self.tcx.def_span(main_def_id),
+            ));
+        }
+
         let Some(start_def_id) = self.tcx.lang_items().start_fn() else {
             self.tcx.dcx().emit_fatal(errors::StartNotFound);
         };
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index 69851511fb1..ca8228de57e 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -223,11 +223,7 @@ where
         // So even if its mode is LocalCopy, we need to treat it like a root.
         match mono_item.instantiation_mode(cx.tcx) {
             InstantiationMode::GloballyShared { .. } => {}
-            InstantiationMode::LocalCopy => {
-                if !cx.tcx.is_lang_item(mono_item.def_id(), LangItem::Start) {
-                    continue;
-                }
-            }
+            InstantiationMode::LocalCopy => continue,
         }
 
         let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item);
@@ -821,10 +817,9 @@ fn mono_item_visibility<'tcx>(
         | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden,
     };
 
-    // The `start_fn` lang item is actually a monomorphized instance of a
-    // function in the standard library, used for the `main` function. We don't
-    // want to export it so we tag it with `Hidden` visibility but this symbol
-    // is only referenced from the actual `main` symbol which we unfortunately
+    // Both the `start_fn` lang item and `main` itself should not be exported,
+    // so we give them with `Hidden` visibility but these symbols are
+    // only referenced from the actual `main` symbol which we unfortunately
     // don't know anything about during partitioning/collection. As a result we
     // forcibly keep this symbol out of the `internalization_candidates` set.
     //
@@ -834,7 +829,7 @@ fn mono_item_visibility<'tcx>(
     //        from the `main` symbol we'll generate later.
     //
     //        This may be fixable with a new `InstanceKind` perhaps? Unsure!
-    if tcx.is_lang_item(def_id, LangItem::Start) {
+    if tcx.is_entrypoint(def_id) {
         *can_be_internalized = false;
         return Visibility::Hidden;
     }
diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs
index 6de001fc998..947f3df179f 100644
--- a/compiler/rustc_parse/src/lexer/diagnostics.rs
+++ b/compiler/rustc_parse/src/lexer/diagnostics.rs
@@ -126,23 +126,29 @@ pub(super) fn report_suspicious_mismatch_block(
     }
 }
 
-pub(crate) fn make_unclosed_delims_error(
-    unmatched: UnmatchedDelim,
-    psess: &ParseSess,
-) -> Option<Diag<'_>> {
-    // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
-    // `unmatched_delims` only for error recovery in the `Parser`.
-    let found_delim = unmatched.found_delim?;
-    let mut spans = vec![unmatched.found_span];
-    if let Some(sp) = unmatched.unclosed_span {
-        spans.push(sp);
-    };
-    let err = psess.dcx().create_err(MismatchedClosingDelimiter {
-        spans,
-        delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()).to_string(),
-        unmatched: unmatched.found_span,
-        opening_candidate: unmatched.candidate_span,
-        unclosed: unmatched.unclosed_span,
-    });
-    Some(err)
+pub(crate) fn make_errors_for_mismatched_closing_delims<'psess>(
+    unmatcheds: &[UnmatchedDelim],
+    psess: &'psess ParseSess,
+) -> Vec<Diag<'psess>> {
+    unmatcheds
+        .iter()
+        .filter_map(|unmatched| {
+            // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
+            // `unmatched_delims` only for error recovery in the `Parser`.
+            let found_delim = unmatched.found_delim?;
+            let mut spans = vec![unmatched.found_span];
+            if let Some(sp) = unmatched.unclosed_span {
+                spans.push(sp);
+            };
+            let err = psess.dcx().create_err(MismatchedClosingDelimiter {
+                spans,
+                delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind())
+                    .to_string(),
+                unmatched: unmatched.found_span,
+                opening_candidate: unmatched.candidate_span,
+                unclosed: unmatched.unclosed_span,
+            });
+            Some(err)
+        })
+        .collect()
 }
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 60d275bf2b4..85af5a615ae 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -1,4 +1,4 @@
-use diagnostics::make_unclosed_delims_error;
+use diagnostics::make_errors_for_mismatched_closing_delims;
 use rustc_ast::ast::{self, AttrStyle};
 use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
 use rustc_ast::tokenstream::TokenStream;
@@ -71,27 +71,23 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
     };
     let res = lexer.lex_token_trees(/* is_delimited */ false);
 
-    let mut unmatched_delims: Vec<_> = lexer
-        .diag_info
-        .unmatched_delims
-        .into_iter()
-        .filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess))
-        .collect();
+    let mut unmatched_closing_delims: Vec<_> =
+        make_errors_for_mismatched_closing_delims(&lexer.diag_info.unmatched_delims, psess);
 
     match res {
         Ok((_open_spacing, stream)) => {
-            if unmatched_delims.is_empty() {
+            if unmatched_closing_delims.is_empty() {
                 Ok(stream)
             } else {
                 // Return error if there are unmatched delimiters or unclosed delimiters.
-                Err(unmatched_delims)
+                Err(unmatched_closing_delims)
             }
         }
         Err(errs) => {
             // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
             // because the delimiter mismatch is more likely to be the root cause of error
-            unmatched_delims.extend(errs);
-            Err(unmatched_delims)
+            unmatched_closing_delims.extend(errs);
+            Err(unmatched_closing_delims)
         }
     }
 }
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index 64748199f28..634f4c30b26 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -51,45 +51,6 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
         }
     }
 
-    fn eof_err(&mut self) -> Diag<'psess> {
-        let msg = "this file contains an unclosed delimiter";
-        let mut err = self.dcx().struct_span_err(self.token.span, msg);
-
-        let unclosed_delimiter_show_limit = 5;
-        let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_delimiters.len());
-        for &(_, span) in &self.diag_info.open_delimiters[..len] {
-            err.span_label(span, "unclosed delimiter");
-            self.diag_info.unmatched_delims.push(UnmatchedDelim {
-                found_delim: None,
-                found_span: self.token.span,
-                unclosed_span: Some(span),
-                candidate_span: None,
-            });
-        }
-
-        if let Some((_, span)) = self.diag_info.open_delimiters.get(unclosed_delimiter_show_limit)
-            && self.diag_info.open_delimiters.len() >= unclosed_delimiter_show_limit + 2
-        {
-            err.span_label(
-                *span,
-                format!(
-                    "another {} unclosed delimiters begin from here",
-                    self.diag_info.open_delimiters.len() - unclosed_delimiter_show_limit
-                ),
-            );
-        }
-
-        if let Some((delim, _)) = self.diag_info.open_delimiters.last() {
-            report_suspicious_mismatch_block(
-                &mut err,
-                &self.diag_info,
-                self.psess.source_map(),
-                *delim,
-            )
-        }
-        err
-    }
-
     fn lex_token_tree_open_delim(
         &mut self,
         open_delim: Delimiter,
@@ -206,13 +167,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
             } else if let Some(glued) = self.token.glue(&next_tok) {
                 self.token = glued;
             } else {
-                let this_spacing = if next_tok.is_punct() {
-                    Spacing::Joint
-                } else if next_tok == token::Eof {
-                    Spacing::Alone
-                } else {
-                    Spacing::JointHidden
-                };
+                let this_spacing = self.calculate_spacing(&next_tok);
                 break (this_spacing, next_tok);
             }
         };
@@ -223,23 +178,64 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
     // Cut-down version of `bump` used when the token kind is known in advance.
     fn bump_minimal(&mut self) -> Spacing {
         let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor();
-
         let this_spacing = if is_next_tok_preceded_by_whitespace {
             Spacing::Alone
         } else {
-            if next_tok.is_punct() {
-                Spacing::Joint
-            } else if next_tok == token::Eof {
-                Spacing::Alone
-            } else {
-                Spacing::JointHidden
-            }
+            self.calculate_spacing(&next_tok)
         };
-
         self.token = next_tok;
         this_spacing
     }
 
+    fn calculate_spacing(&self, next_tok: &Token) -> Spacing {
+        if next_tok.is_punct() {
+            Spacing::Joint
+        } else if *next_tok == token::Eof {
+            Spacing::Alone
+        } else {
+            Spacing::JointHidden
+        }
+    }
+
+    fn eof_err(&mut self) -> Diag<'psess> {
+        const UNCLOSED_DELIMITER_SHOW_LIMIT: usize = 5;
+        let msg = "this file contains an unclosed delimiter";
+        let mut err = self.dcx().struct_span_err(self.token.span, msg);
+
+        let len = usize::min(UNCLOSED_DELIMITER_SHOW_LIMIT, self.diag_info.open_delimiters.len());
+        for &(_, span) in &self.diag_info.open_delimiters[..len] {
+            err.span_label(span, "unclosed delimiter");
+            self.diag_info.unmatched_delims.push(UnmatchedDelim {
+                found_delim: None,
+                found_span: self.token.span,
+                unclosed_span: Some(span),
+                candidate_span: None,
+            });
+        }
+
+        if let Some((_, span)) = self.diag_info.open_delimiters.get(UNCLOSED_DELIMITER_SHOW_LIMIT)
+            && self.diag_info.open_delimiters.len() >= UNCLOSED_DELIMITER_SHOW_LIMIT + 2
+        {
+            err.span_label(
+                *span,
+                format!(
+                    "another {} unclosed delimiters begin from here",
+                    self.diag_info.open_delimiters.len() - UNCLOSED_DELIMITER_SHOW_LIMIT
+                ),
+            );
+        }
+
+        if let Some((delim, _)) = self.diag_info.open_delimiters.last() {
+            report_suspicious_mismatch_block(
+                &mut err,
+                &self.diag_info,
+                self.psess.source_map(),
+                *delim,
+            )
+        }
+        err
+    }
+
     fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> {
         // An unexpected closing delimiter (i.e., there is no matching opening delimiter).
         let token_str = token_to_string(&self.token);
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index cca621103b5..73a341c3a3d 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -285,6 +285,9 @@ pub fn check_builtin_meta_item(
                 | sym::rustc_do_not_implement_via_object
                 | sym::rustc_coinductive
                 | sym::const_trait
+                | sym::stable
+                | sym::unstable
+                | sym::rustc_allowed_through_unstable_modules
                 | sym::rustc_specialization_trait
                 | sym::rustc_unsafe_specialization_marker
                 | sym::rustc_allow_incoherent_impl
@@ -303,6 +306,8 @@ pub fn check_builtin_meta_item(
                 | sym::cold
                 | sym::target_feature
                 | sym::rustc_allow_const_fn_unstable
+                | sym::macro_use
+                | sym::macro_escape
                 | sym::naked
                 | sym::no_mangle
                 | sym::non_exhaustive
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 96c895e71df..4b524bb2bd2 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -227,6 +227,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => {
                     self.check_link_section(hir_id, *attr_span, span, target)
                 }
+                Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => {
+                    self.check_macro_use(hir_id, sym::macro_use, *span, target)
+                }
+                Attribute::Parsed(AttributeKind::MacroEscape(span)) => {
+                    self.check_macro_use(hir_id, sym::macro_escape, *span, target)
+                }
                 Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
                     self.check_naked(hir_id, *attr_span, span, target)
                 }
@@ -362,9 +368,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
                         [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
                         [sym::link, ..] => self.check_link(hir_id, attr, span, target),
-                        [sym::macro_use, ..] | [sym::macro_escape, ..] => {
-                            self.check_macro_use(hir_id, attr, target)
-                        }
                         [sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod),
                         [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
                         [sym::should_panic, ..] => {
@@ -1255,7 +1258,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             return;
         }
 
-        if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() {
+        if self.tcx.extern_mod_stmt_cnum(hir_id.owner.def_id).is_none() {
             self.tcx.emit_node_span_lint(
                 INVALID_DOC_ATTRIBUTES,
                 hir_id,
@@ -2411,17 +2414,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
-    fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
-        let Some(name) = attr.name() else {
-            return;
-        };
+    fn check_macro_use(&self, hir_id: HirId, name: Symbol, attr_span: Span, target: Target) {
         match target {
             Target::ExternCrate | Target::Mod => {}
             _ => {
                 self.tcx.emit_node_span_lint(
                     UNUSED_ATTRIBUTES,
                     hir_id,
-                    attr.span(),
+                    attr_span,
                     errors::MacroUse { name },
                 );
             }
@@ -2474,7 +2474,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         // Warn on useless empty attributes.
         // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
         let note = if attr.has_any_name(&[
-            sym::macro_use,
             sym::allow,
             sym::expect,
             sym::warn,
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 9dd80bc9964..6fd2b7fc12f 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -26,7 +26,7 @@ use rustc_errors::{MultiSpan, listify};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId};
 use rustc_hir::intravisit::{self, InferKind, Visitor};
-use rustc_hir::{AmbigArg, ForeignItemKind, ItemId, ItemKind, PatKind};
+use rustc_hir::{AmbigArg, ForeignItemId, ItemId, PatKind};
 use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
@@ -599,18 +599,13 @@ impl<'tcx> EmbargoVisitor<'tcx> {
 
             DefKind::Struct | DefKind::Union => {
                 // While structs and unions have type privacy, their fields do not.
-                let item = self.tcx.hir_expect_item(def_id);
-                if let hir::ItemKind::Struct(_, _, ref struct_def)
-                | hir::ItemKind::Union(_, _, ref struct_def) = item.kind
-                {
-                    for field in struct_def.fields() {
-                        let field_vis = self.tcx.local_visibility(field.def_id);
-                        if field_vis.is_accessible_from(module, self.tcx) {
-                            self.reach(field.def_id, macro_ev).ty();
-                        }
+                let struct_def = self.tcx.adt_def(def_id);
+                for field in struct_def.non_enum_variant().fields.iter() {
+                    let def_id = field.did.expect_local();
+                    let field_vis = self.tcx.local_visibility(def_id);
+                    if field_vis.is_accessible_from(module, self.tcx) {
+                        self.reach(def_id, macro_ev).ty();
                     }
-                } else {
-                    bug!("item {:?} with DefKind {:?}", item, def_kind);
                 }
             }
 
@@ -1644,66 +1639,29 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
                 self.check(def_id, item_visibility, effective_vis).generics().predicates();
             }
             DefKind::Enum => {
-                let item = tcx.hir_item(id);
-                if let hir::ItemKind::Enum(_, _, ref def) = item.kind {
-                    self.check_unnameable(item.owner_id.def_id, effective_vis);
-
-                    self.check(item.owner_id.def_id, item_visibility, effective_vis)
-                        .generics()
-                        .predicates();
-
-                    for variant in def.variants {
-                        for field in variant.data.fields() {
-                            self.check(field.def_id, item_visibility, effective_vis).ty();
-                        }
-                    }
-                }
-            }
-            // Subitems of foreign modules have their own publicity.
-            DefKind::ForeignMod => {
-                let item = tcx.hir_item(id);
-                if let hir::ItemKind::ForeignMod { items, .. } = item.kind {
-                    for &foreign_item in items {
-                        let foreign_item = tcx.hir_foreign_item(foreign_item);
-
-                        let ev = self.get(foreign_item.owner_id.def_id);
-                        let vis = tcx.local_visibility(foreign_item.owner_id.def_id);
-
-                        if let ForeignItemKind::Type = foreign_item.kind {
-                            self.check_unnameable(foreign_item.owner_id.def_id, ev);
-                        }
+                self.check_unnameable(def_id, effective_vis);
+                self.check(def_id, item_visibility, effective_vis).generics().predicates();
 
-                        self.check(foreign_item.owner_id.def_id, vis, ev)
-                            .generics()
-                            .predicates()
-                            .ty();
-                    }
+                let adt = tcx.adt_def(id.owner_id);
+                for field in adt.all_fields() {
+                    self.check(field.did.expect_local(), item_visibility, effective_vis).ty();
                 }
             }
             // Subitems of structs and unions have their own publicity.
             DefKind::Struct | DefKind::Union => {
-                let item = tcx.hir_item(id);
-                if let hir::ItemKind::Struct(_, _, ref struct_def)
-                | hir::ItemKind::Union(_, _, ref struct_def) = item.kind
-                {
-                    self.check_unnameable(item.owner_id.def_id, effective_vis);
-                    self.check(item.owner_id.def_id, item_visibility, effective_vis)
-                        .generics()
-                        .predicates();
+                self.check_unnameable(def_id, effective_vis);
+                self.check(def_id, item_visibility, effective_vis).generics().predicates();
 
-                    for field in struct_def.fields() {
-                        let field_visibility = tcx.local_visibility(field.def_id);
-                        let field_ev = self.get(field.def_id);
+                let adt = tcx.adt_def(id.owner_id);
+                for field in adt.all_fields() {
+                    let visibility = min(item_visibility, field.vis.expect_local(), tcx);
+                    let field_ev = self.get(field.did.expect_local());
 
-                        self.check(
-                            field.def_id,
-                            min(item_visibility, field_visibility, tcx),
-                            field_ev,
-                        )
-                        .ty();
-                    }
+                    self.check(field.did.expect_local(), visibility, field_ev).ty();
                 }
             }
+            // Subitems of foreign modules have their own publicity.
+            DefKind::ForeignMod => {}
             // An inherent impl is public when its type is public
             // Subitems of inherent impls have their own publicity.
             // A trait impl is public when both its type and its trait are public
@@ -1763,6 +1721,19 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
             _ => {}
         }
     }
+
+    fn check_foreign_item(&mut self, id: ForeignItemId) {
+        let tcx = self.tcx;
+        let def_id = id.owner_id.def_id;
+        let item_visibility = tcx.local_visibility(def_id);
+        let effective_vis = self.get(def_id);
+
+        if let DefKind::ForeignTy = self.tcx.def_kind(def_id) {
+            self.check_unnameable(def_id, effective_vis);
+        }
+
+        self.check(def_id, item_visibility, effective_vis).generics().predicates().ty();
+    }
 }
 
 pub fn provide(providers: &mut Providers) {
@@ -1791,20 +1762,13 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
         if let Some(body_id) = tcx.hir_maybe_body_owned_by(def_id) {
             visitor.visit_nested_body(body_id.id());
         }
-    }
 
-    for id in module.free_items() {
-        if let ItemKind::Impl(i) = tcx.hir_item(id).kind {
-            if let Some(item) = i.of_trait {
-                let trait_ref = tcx.impl_trait_ref(id.owner_id.def_id).unwrap();
-                let trait_ref = trait_ref.instantiate_identity();
-                visitor.span = item.path.span;
-                let _ = visitor.visit_def_id(
-                    trait_ref.def_id,
-                    "trait",
-                    &trait_ref.print_only_trait_path(),
-                );
-            }
+        if let DefKind::Impl { of_trait: true } = tcx.def_kind(def_id) {
+            let trait_ref = tcx.impl_trait_ref(def_id).unwrap();
+            let trait_ref = trait_ref.instantiate_identity();
+            visitor.span = tcx.hir_expect_item(def_id).expect_impl().of_trait.unwrap().path.span;
+            let _ =
+                visitor.visit_def_id(trait_ref.def_id, "trait", &trait_ref.print_only_trait_path());
         }
     }
 }
@@ -1895,7 +1859,11 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) {
     // Check for private types in public interfaces.
     let mut checker = PrivateItemsInPublicInterfacesChecker { tcx, effective_visibilities };
 
-    for id in tcx.hir_free_items() {
+    let crate_items = tcx.hir_crate_items(());
+    for id in crate_items.free_items() {
         checker.check_item(id);
     }
+    for id in crate_items.foreign_items() {
+        checker.check_foreign_item(id);
+    }
 }
diff --git a/compiler/rustc_public/src/alloc.rs b/compiler/rustc_public/src/alloc.rs
index 75ad31022ff..0c35b3b25df 100644
--- a/compiler/rustc_public/src/alloc.rs
+++ b/compiler/rustc_public/src/alloc.rs
@@ -33,7 +33,7 @@ fn new_empty_allocation(align: Align) -> Allocation {
 #[allow(rustc::usage_of_qualified_ty)]
 pub(crate) fn new_allocation<'tcx>(
     ty: rustc_middle::ty::Ty<'tcx>,
-    const_value: ConstValue<'tcx>,
+    const_value: ConstValue,
     tables: &mut Tables<'tcx, BridgeTys>,
     cx: &CompilerCtxt<'tcx, BridgeTys>,
 ) -> Allocation {
@@ -44,7 +44,7 @@ pub(crate) fn new_allocation<'tcx>(
 #[allow(rustc::usage_of_qualified_ty)]
 pub(crate) fn try_new_allocation<'tcx>(
     ty: rustc_middle::ty::Ty<'tcx>,
-    const_value: ConstValue<'tcx>,
+    const_value: ConstValue,
     tables: &mut Tables<'tcx, BridgeTys>,
     cx: &CompilerCtxt<'tcx, BridgeTys>,
 ) -> Result<Allocation, Error> {
@@ -54,8 +54,8 @@ pub(crate) fn try_new_allocation<'tcx>(
             alloc::try_new_scalar(layout, scalar, cx).map(|alloc| alloc.stable(tables, cx))
         }
         ConstValue::ZeroSized => Ok(new_empty_allocation(layout.align.abi)),
-        ConstValue::Slice { data, meta } => {
-            alloc::try_new_slice(layout, data, meta, cx).map(|alloc| alloc.stable(tables, cx))
+        ConstValue::Slice { alloc_id, meta } => {
+            alloc::try_new_slice(layout, alloc_id, meta, cx).map(|alloc| alloc.stable(tables, cx))
         }
         ConstValue::Indirect { alloc_id, offset } => {
             let alloc = alloc::try_new_indirect(alloc_id, cx);
diff --git a/compiler/rustc_public_bridge/src/alloc.rs b/compiler/rustc_public_bridge/src/alloc.rs
index ecf9004562c..7e6af342546 100644
--- a/compiler/rustc_public_bridge/src/alloc.rs
+++ b/compiler/rustc_public_bridge/src/alloc.rs
@@ -38,11 +38,10 @@ pub fn try_new_scalar<'tcx, B: Bridge>(
 
 pub fn try_new_slice<'tcx, B: Bridge>(
     layout: TyAndLayout<'tcx, Ty<'tcx>>,
-    data: ConstAllocation<'tcx>,
+    alloc_id: AllocId,
     meta: u64,
     cx: &CompilerCtxt<'tcx, B>,
 ) -> Result<Allocation, B::Error> {
-    let alloc_id = cx.tcx.reserve_and_set_memory_alloc(data);
     let ptr = Pointer::new(alloc_id.into(), Size::ZERO);
     let scalar_ptr = Scalar::from_pointer(ptr, &cx.tcx);
     let scalar_meta: Scalar = Scalar::from_target_usize(meta, &cx.tcx);
diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs
index 612e44b56b1..9b3948d232d 100644
--- a/compiler/rustc_public_bridge/src/context/impls.rs
+++ b/compiler/rustc_public_bridge/src/context/impls.rs
@@ -63,7 +63,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> {
         self.tcx.coroutine_movability(def_id)
     }
 
-    pub fn valtree_to_const_val(&self, key: ty::Value<'tcx>) -> ConstValue<'tcx> {
+    pub fn valtree_to_const_val(&self, key: ty::Value<'tcx>) -> ConstValue {
         self.tcx.valtree_to_const_val(key)
     }
 
@@ -675,10 +675,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> {
     }
 
     /// Try to evaluate an instance into a constant.
-    pub fn eval_instance(
-        &self,
-        instance: ty::Instance<'tcx>,
-    ) -> Result<ConstValue<'tcx>, ErrorHandled> {
+    pub fn eval_instance(&self, instance: ty::Instance<'tcx>) -> Result<ConstValue, ErrorHandled> {
         self.tcx.const_eval_instance(
             self.fully_monomorphized(),
             instance,
diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl
index aa818cc9c46..39e9a9cc58a 100644
--- a/compiler/rustc_resolve/messages.ftl
+++ b/compiler/rustc_resolve/messages.ftl
@@ -41,8 +41,6 @@ resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion =
 resolve_attributes_starting_with_rustc_are_reserved =
     attributes starting with `rustc` are reserved for use by the `rustc` compiler
 
-resolve_bad_macro_import = bad macro import
-
 resolve_binding_in_never_pattern =
     never patterns cannot contain variable bindings
     .suggestion = use a wildcard `_` instead
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 737577baa7a..83ec037a975 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -11,11 +11,14 @@ use std::sync::Arc;
 use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind};
 use rustc_ast::{
     self as ast, AssocItem, AssocItemKind, Block, ConstItem, Delegation, Fn, ForeignItem,
-    ForeignItemKind, Impl, Item, ItemKind, MetaItemKind, NodeId, StaticItem, StmtKind, TyAlias,
+    ForeignItemKind, Impl, Item, ItemKind, NodeId, StaticItem, StmtKind, TyAlias,
 };
+use rustc_attr_data_structures::{AttributeKind, MacroUseArgs};
 use rustc_attr_parsing as attr;
+use rustc_attr_parsing::AttributeParser;
 use rustc_expand::base::ResolverExpand;
 use rustc_expand::expand::AstFragment;
+use rustc_hir::Attribute;
 use rustc_hir::def::{self, *};
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
 use rustc_index::bit_set::DenseBitSet;
@@ -25,6 +28,7 @@ use rustc_middle::metadata::ModChild;
 use rustc_middle::ty::{Feed, Visibility};
 use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
 use rustc_span::{Ident, Span, Symbol, kw, sym};
+use thin_vec::ThinVec;
 use tracing::debug;
 
 use crate::Namespace::{MacroNS, TypeNS, ValueNS};
@@ -49,8 +53,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         ns: Namespace,
         binding: NameBinding<'ra>,
     ) {
-        let key = self.new_disambiguated_key(ident, ns);
-        if let Err(old_binding) = self.try_define(parent, key, binding, false) {
+        if let Err(old_binding) = self.try_define(parent, ident, ns, binding, false) {
             self.report_conflict(parent, ident, ns, old_binding, binding);
         }
     }
@@ -442,16 +445,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
 
         self.r.indeterminate_imports.push(import);
         match import.kind {
-            // Don't add unresolved underscore imports to modules
-            ImportKind::Single { target: Ident { name: kw::Underscore, .. }, .. } => {}
             ImportKind::Single { target, type_ns_only, .. } => {
-                self.r.per_ns(|this, ns| {
-                    if !type_ns_only || ns == TypeNS {
-                        let key = BindingKey::new(target, ns);
-                        let mut resolution = this.resolution(current_module, key).borrow_mut();
-                        resolution.single_imports.insert(import);
-                    }
-                });
+                // Don't add underscore imports to `single_imports`
+                // because they cannot define any usable names.
+                if target.name != kw::Underscore {
+                    self.r.per_ns(|this, ns| {
+                        if !type_ns_only || ns == TypeNS {
+                            let key = BindingKey::new(target, ns);
+                            let mut resolution = this.resolution(current_module, key).borrow_mut();
+                            resolution.single_imports.insert(import);
+                        }
+                    });
+                }
             }
             // We don't add prelude imports to the globs since they only affect lexical scopes,
             // which are not relevant to import resolution.
@@ -1019,42 +1024,31 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
     /// Returns `true` if we should consider the underlying `extern crate` to be used.
     fn process_macro_use_imports(&mut self, item: &Item, module: Module<'ra>) -> bool {
         let mut import_all = None;
-        let mut single_imports = Vec::new();
-        for attr in &item.attrs {
-            if attr.has_name(sym::macro_use) {
-                if self.parent_scope.module.parent.is_some() {
-                    self.r.dcx().emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot {
-                        span: item.span,
-                    });
-                }
-                if let ItemKind::ExternCrate(Some(orig_name), _) = item.kind
-                    && orig_name == kw::SelfLower
-                {
-                    self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span: attr.span });
-                }
-                let ill_formed = |span| {
-                    self.r.dcx().emit_err(errors::BadMacroImport { span });
-                };
-                match attr.meta() {
-                    Some(meta) => match meta.kind {
-                        MetaItemKind::Word => {
-                            import_all = Some(meta.span);
-                            break;
-                        }
-                        MetaItemKind::List(meta_item_inners) => {
-                            for meta_item_inner in meta_item_inners {
-                                match meta_item_inner.ident() {
-                                    Some(ident) if meta_item_inner.is_word() => {
-                                        single_imports.push(ident)
-                                    }
-                                    _ => ill_formed(meta_item_inner.span()),
-                                }
-                            }
-                        }
-                        MetaItemKind::NameValue(..) => ill_formed(meta.span),
-                    },
-                    None => ill_formed(attr.span),
-                }
+        let mut single_imports = ThinVec::new();
+        if let Some(Attribute::Parsed(AttributeKind::MacroUse { span, arguments })) =
+            AttributeParser::parse_limited(
+                self.r.tcx.sess,
+                &item.attrs,
+                sym::macro_use,
+                item.span,
+                item.id,
+                None,
+            )
+        {
+            if self.parent_scope.module.parent.is_some() {
+                self.r
+                    .dcx()
+                    .emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot { span: item.span });
+            }
+            if let ItemKind::ExternCrate(Some(orig_name), _) = item.kind
+                && orig_name == kw::SelfLower
+            {
+                self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span });
+            }
+
+            match arguments {
+                MacroUseArgs::UseAll => import_all = Some(span),
+                MacroUseArgs::UseSpecific(imports) => single_imports = imports,
             }
         }
 
@@ -1408,9 +1402,12 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             let parent = self.parent_scope.module;
             let expansion = self.parent_scope.expansion;
             self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
-        } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) {
+        } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob)
+            && ident.name != kw::Underscore
+        {
+            // Don't add underscore names, they cannot be looked up anyway.
             let impl_def_id = self.r.tcx.local_parent(local_def_id);
-            let key = BindingKey::new(ident.normalize_to_macros_2_0(), ns);
+            let key = BindingKey::new(ident, ns);
             self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key);
         }
 
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index b34bcb38f84..d6b1e4de6ea 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -816,13 +816,6 @@ pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot {
 }
 
 #[derive(Diagnostic)]
-#[diag(resolve_bad_macro_import, code = E0466)]
-pub(crate) struct BadMacroImport {
-    #[primary_span]
-    pub(crate) span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(resolve_extern_crate_self_requires_renaming)]
 pub(crate) struct ExternCrateSelfRequiresRenaming {
     #[primary_span]
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 0a4c25b0eb0..783c5005cca 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -25,7 +25,7 @@ use rustc_span::{Ident, Span, Symbol, kw, sym};
 use smallvec::SmallVec;
 use tracing::debug;
 
-use crate::Namespace::*;
+use crate::Namespace::{self, *};
 use crate::diagnostics::{DiagMode, Suggestion, import_candidates};
 use crate::errors::{
     CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate,
@@ -338,13 +338,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
     pub(crate) fn try_define(
         &mut self,
         module: Module<'ra>,
-        key: BindingKey,
+        ident: Ident,
+        ns: Namespace,
         binding: NameBinding<'ra>,
         warn_ambiguity: bool,
     ) -> Result<(), NameBinding<'ra>> {
         let res = binding.res();
-        self.check_reserved_macro_name(key.ident, res);
+        self.check_reserved_macro_name(ident, res);
         self.set_binding_parent_module(binding, module);
+        // Even if underscore names cannot be looked up, we still need to add them to modules,
+        // because they can be fetched by glob imports from those modules, and bring traits
+        // into scope both directly and through glob imports.
+        let key = BindingKey::new_disambiguated(ident, ns, || {
+            module.underscore_disambiguator.update(|d| d + 1);
+            module.underscore_disambiguator.get()
+        });
         self.update_resolution(module, key, warn_ambiguity, |this, resolution| {
             if let Some(old_binding) = resolution.best_binding() {
                 if res == Res::Err && old_binding.res() != Res::Err {
@@ -383,7 +391,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     (old_glob @ true, false) | (old_glob @ false, true) => {
                         let (glob_binding, non_glob_binding) =
                             if old_glob { (old_binding, binding) } else { (binding, old_binding) };
-                        if key.ns == MacroNS
+                        if ns == MacroNS
                             && non_glob_binding.expansion != LocalExpnId::ROOT
                             && glob_binding.res() != non_glob_binding.res()
                         {
@@ -489,10 +497,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             };
             if self.is_accessible_from(binding.vis, scope) {
                 let imported_binding = self.import(binding, *import);
-                let key = BindingKey { ident, ..key };
                 let _ = self.try_define(
                     import.parent_scope.module,
-                    key,
+                    ident,
+                    key.ns,
                     imported_binding,
                     warn_ambiguity,
                 );
@@ -514,11 +522,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             let dummy_binding = self.dummy_binding;
             let dummy_binding = self.import(dummy_binding, import);
             self.per_ns(|this, ns| {
-                let key = BindingKey::new(target, ns);
-                let _ = this.try_define(import.parent_scope.module, key, dummy_binding, false);
-                this.update_resolution(import.parent_scope.module, key, false, |_, resolution| {
-                    resolution.single_imports.swap_remove(&import);
-                })
+                let module = import.parent_scope.module;
+                let _ = this.try_define(module, target, ns, dummy_binding, false);
+                // Don't remove underscores from `single_imports`, they were never added.
+                if target.name != kw::Underscore {
+                    let key = BindingKey::new(target, ns);
+                    this.update_resolution(module, key, false, |_, resolution| {
+                        resolution.single_imports.swap_remove(&import);
+                    })
+                }
             });
             self.record_use(target, dummy_binding, Used::Other);
         } else if import.imported_module.get().is_none() {
@@ -895,7 +907,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         PendingBinding::Ready(Some(imported_binding))
                     }
                     Err(Determinacy::Determined) => {
-                        // Don't update the resolution for underscores, because it was never added.
+                        // Don't remove underscores from `single_imports`, they were never added.
                         if target.name != kw::Underscore {
                             let key = BindingKey::new(target, ns);
                             this.update_resolution(parent, key, false, |_, resolution| {
@@ -1510,7 +1522,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     .is_some_and(|binding| binding.warn_ambiguity_recursive());
                 let _ = self.try_define(
                     import.parent_scope.module,
-                    key,
+                    key.ident,
+                    key.ns,
                     imported_binding,
                     warn_ambiguity,
                 );
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 0d41a822e8a..08f1f61ea86 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -532,15 +532,26 @@ struct BindingKey {
     /// identifier.
     ident: Ident,
     ns: Namespace,
-    /// 0 if ident is not `_`, otherwise a value that's unique to the specific
-    /// `_` in the expanded AST that introduced this binding.
+    /// When we add an underscore binding (with ident `_`) to some module, this field has
+    /// a non-zero value that uniquely identifies this binding in that module.
+    /// For non-underscore bindings this field is zero.
+    /// When a key is constructed for name lookup (as opposed to name definition), this field is
+    /// also zero, even for underscore names, so for underscores the lookup will never succeed.
     disambiguator: u32,
 }
 
 impl BindingKey {
     fn new(ident: Ident, ns: Namespace) -> Self {
-        let ident = ident.normalize_to_macros_2_0();
-        BindingKey { ident, ns, disambiguator: 0 }
+        BindingKey { ident: ident.normalize_to_macros_2_0(), ns, disambiguator: 0 }
+    }
+
+    fn new_disambiguated(
+        ident: Ident,
+        ns: Namespace,
+        disambiguator: impl FnOnce() -> u32,
+    ) -> BindingKey {
+        let disambiguator = if ident.name == kw::Underscore { disambiguator() } else { 0 };
+        BindingKey { ident: ident.normalize_to_macros_2_0(), ns, disambiguator }
     }
 }
 
@@ -568,6 +579,8 @@ struct ModuleData<'ra> {
     lazy_resolutions: Resolutions<'ra>,
     /// True if this is a module from other crate that needs to be populated on access.
     populate_on_access: Cell<bool>,
+    /// Used to disambiguate underscore items (`const _: T = ...`) in the module.
+    underscore_disambiguator: Cell<u32>,
 
     /// Macro invocations that can expand into items in this module.
     unexpanded_invocations: RefCell<FxHashSet<LocalExpnId>>,
@@ -628,6 +641,7 @@ impl<'ra> ModuleData<'ra> {
             kind,
             lazy_resolutions: Default::default(),
             populate_on_access: Cell::new(is_foreign),
+            underscore_disambiguator: Cell::new(0),
             unexpanded_invocations: Default::default(),
             no_implicit_prelude,
             glob_importers: RefCell::new(Vec::new()),
@@ -1087,8 +1101,6 @@ pub struct Resolver<'ra, 'tcx> {
     extern_module_map: RefCell<FxIndexMap<DefId, Module<'ra>>>,
     binding_parent_modules: FxHashMap<NameBinding<'ra>, Module<'ra>>,
 
-    underscore_disambiguator: u32,
-
     /// Maps glob imports to the names of items actually imported.
     glob_map: FxIndexMap<LocalDefId, FxIndexSet<Symbol>>,
     glob_error: Option<ErrorGuaranteed>,
@@ -1500,7 +1512,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             extern_crate_map: Default::default(),
             module_children: Default::default(),
             trait_map: NodeMap::default(),
-            underscore_disambiguator: 0,
             empty_module,
             local_module_map,
             extern_module_map: Default::default(),
@@ -1881,17 +1892,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         import_ids
     }
 
-    fn new_disambiguated_key(&mut self, ident: Ident, ns: Namespace) -> BindingKey {
-        let ident = ident.normalize_to_macros_2_0();
-        let disambiguator = if ident.name == kw::Underscore {
-            self.underscore_disambiguator += 1;
-            self.underscore_disambiguator
-        } else {
-            0
-        };
-        BindingKey { ident, ns, disambiguator }
-    }
-
     fn resolutions(&mut self, module: Module<'ra>) -> &'ra Resolutions<'ra> {
         if module.populate_on_access.get() {
             module.populate_on_access.set(false);
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 77ef7f56c09..f0225daa09d 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -1,6 +1,7 @@
 //! A bunch of methods and structures more or less related to resolving macros and
 //! interface provided by `Resolver` to macro expander.
 
+use std::any::Any;
 use std::cell::Cell;
 use std::mem;
 use std::sync::Arc;
@@ -13,10 +14,10 @@ use rustc_expand::base::{
     Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension,
     SyntaxExtensionKind,
 };
-use rustc_expand::compile_declarative_macro;
 use rustc_expand::expand::{
     AstFragment, AstFragmentKind, Invocation, InvocationKind, SupportsMacroExpansion,
 };
+use rustc_expand::{MacroRulesMacroExpander, compile_declarative_macro};
 use rustc_hir::def::{self, DefKind, Namespace, NonMacroAttrKind};
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
 use rustc_middle::middle::stability;
@@ -357,8 +358,12 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
             let SyntaxExtensionKind::LegacyBang(ref ext) = m.ext.kind else {
                 continue;
             };
+            let ext: &dyn Any = ext.as_ref();
+            let Some(m) = ext.downcast_ref::<MacroRulesMacroExpander>() else {
+                continue;
+            };
             for arm_i in unused_arms.iter() {
-                if let Some((ident, rule_span)) = ext.get_unused_rule(arm_i) {
+                if let Some((ident, rule_span)) = m.get_unused_rule(arm_i) {
                     self.lint_buffer.buffer_lint(
                         UNUSED_MACRO_RULES,
                         node_id,
@@ -530,7 +535,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
         target_trait.for_each_child(self, |this, ident, ns, _binding| {
             // FIXME: Adjust hygiene for idents from globs, like for glob imports.
             if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id)
-                && overriding_keys.contains(&BindingKey::new(ident.normalize_to_macros_2_0(), ns))
+                && overriding_keys.contains(&BindingKey::new(ident, ns))
             {
                 // The name is overridden, do not produce it from the glob delegation.
             } else {
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index 24e15ded94f..6450f63472c 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -509,9 +509,8 @@ fn collect_link_data<'input, F: BrokenLinkCallback<'input>>(
     display_text.map(String::into_boxed_str)
 }
 
-/// Returns a tuple containing a span encompassing all the document fragments and a boolean that is
-/// `true` if any of the fragments are from a macro expansion.
-pub fn span_of_fragments_with_expansion(fragments: &[DocFragment]) -> Option<(Span, bool)> {
+/// Returns a span encompassing all the document fragments.
+pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
     let (first_fragment, last_fragment) = match fragments {
         [] => return None,
         [first, .., last] => (first, last),
@@ -520,15 +519,7 @@ pub fn span_of_fragments_with_expansion(fragments: &[DocFragment]) -> Option<(Sp
     if first_fragment.span == DUMMY_SP {
         return None;
     }
-    Some((
-        first_fragment.span.to(last_fragment.span),
-        fragments.iter().any(|frag| frag.from_expansion),
-    ))
-}
-
-/// Returns a span encompassing all the document fragments.
-pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
-    span_of_fragments_with_expansion(fragments).map(|(sp, _)| sp)
+    Some(first_fragment.span.to(last_fragment.span))
 }
 
 /// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.
@@ -686,7 +677,7 @@ pub fn source_span_for_markdown_range_inner(
         }
     }
 
-    let (span, _) = span_of_fragments_with_expansion(fragments)?;
+    let span = span_of_fragments(fragments)?;
     let src_span = span.from_inner(InnerSpan::new(
         md_range.start + start_bytes,
         md_range.end + start_bytes + end_bytes,
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 7bea8685724..8f624e0fb2f 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -343,12 +343,12 @@ impl LinkSelfContained {
         if let Some(component_to_enable) = component.strip_prefix('+') {
             self.explicitly_set = None;
             self.enabled_components
-                .insert(LinkSelfContainedComponents::from_str(component_to_enable)?);
+                .insert(LinkSelfContainedComponents::from_str(component_to_enable).ok()?);
             Some(())
         } else if let Some(component_to_disable) = component.strip_prefix('-') {
             self.explicitly_set = None;
             self.disabled_components
-                .insert(LinkSelfContainedComponents::from_str(component_to_disable)?);
+                .insert(LinkSelfContainedComponents::from_str(component_to_disable).ok()?);
             Some(())
         } else {
             None
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index b33e3815ea4..5f1973b31a1 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1296,7 +1296,7 @@ pub mod parse {
     }
 
     pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavorCli>, v: Option<&str>) -> bool {
-        match v.and_then(LinkerFlavorCli::from_str) {
+        match v.and_then(|v| LinkerFlavorCli::from_str(v).ok()) {
             Some(lf) => *slot = Some(lf),
             _ => return false,
         }
diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml
index 0121c752dbd..56932c24922 100644
--- a/compiler/rustc_target/Cargo.toml
+++ b/compiler/rustc_target/Cargo.toml
@@ -12,7 +12,10 @@ rustc_fs_util = { path = "../rustc_fs_util" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
+serde = "1.0.219"
+serde_derive = "1.0.219"
 serde_json = "1.0.59"
+serde_path_to_error = "0.1.17"
 tracing = "0.1"
 # tidy-alphabetical-end
 
diff --git a/compiler/rustc_target/src/json.rs b/compiler/rustc_target/src/json.rs
index 4fcc477921b..896609bdbe3 100644
--- a/compiler/rustc_target/src/json.rs
+++ b/compiler/rustc_target/src/json.rs
@@ -114,3 +114,18 @@ impl ToJson for rustc_abi::CanonAbi {
         self.to_string().to_json()
     }
 }
+
+macro_rules! serde_deserialize_from_str {
+    ($ty:ty) => {
+        impl<'de> serde::Deserialize<'de> for $ty {
+            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+            where
+                D: serde::Deserializer<'de>,
+            {
+                let s = String::deserialize(deserializer)?;
+                FromStr::from_str(&s).map_err(serde::de::Error::custom)
+            }
+        }
+    };
+}
+pub(crate) use serde_deserialize_from_str;
diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs
index 6c716f87125..d27c1929aef 100644
--- a/compiler/rustc_target/src/spec/json.rs
+++ b/compiler/rustc_target/src/spec/json.rs
@@ -1,60 +1,47 @@
-use std::borrow::Cow;
 use std::collections::BTreeMap;
 use std::str::FromStr;
 
-use rustc_abi::{Align, AlignFromBytesError, ExternAbi};
-use serde_json::Value;
+use rustc_abi::{Align, AlignFromBytesError};
 
-use super::{Target, TargetKind, TargetOptions, TargetWarnings};
+use super::crt_objects::CrtObjects;
+use super::{
+    BinaryFormat, CodeModel, DebuginfoKind, FloatAbi, FramePointer, LinkArgsCli,
+    LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFlavorCli, LldFlavor,
+    MergeFunctions, PanicStrategy, RelocModel, RelroLevel, RustcAbi, SanitizerSet,
+    SmallDataThresholdSupport, SplitDebuginfo, StackProbeType, StaticCow, SymbolVisibility, Target,
+    TargetKind, TargetOptions, TargetWarnings, TlsModel,
+};
 use crate::json::{Json, ToJson};
 use crate::spec::AbiMap;
 
 impl Target {
     /// Loads a target descriptor from a JSON object.
-    pub fn from_json(obj: Json) -> Result<(Target, TargetWarnings), String> {
-        // While ugly, this code must remain this way to retain
-        // compatibility with existing JSON fields and the internal
-        // expected naming of the Target and TargetOptions structs.
-        // To ensure compatibility is retained, the built-in targets
-        // are round-tripped through this code to catch cases where
-        // the JSON parser is not updated to match the structs.
-
-        let mut obj = match obj {
-            Value::Object(obj) => obj,
-            _ => return Err("Expected JSON object for target")?,
-        };
+    pub fn from_json(json: &str) -> Result<(Target, TargetWarnings), String> {
+        let json_deserializer = &mut serde_json::Deserializer::from_str(json);
 
-        let mut get_req_field = |name: &str| {
-            obj.remove(name)
-                .and_then(|j| j.as_str().map(str::to_string))
-                .ok_or_else(|| format!("Field {name} in target specification is required"))
-        };
+        let json: TargetSpecJson =
+            serde_path_to_error::deserialize(json_deserializer).map_err(|err| err.to_string())?;
 
         let mut base = Target {
-            llvm_target: get_req_field("llvm-target")?.into(),
+            llvm_target: json.llvm_target,
             metadata: Default::default(),
-            pointer_width: get_req_field("target-pointer-width")?
-                .parse::<u32>()
-                .map_err(|_| "target-pointer-width must be an integer".to_string())?,
-            data_layout: get_req_field("data-layout")?.into(),
-            arch: get_req_field("arch")?.into(),
+            pointer_width: json
+                .target_pointer_width
+                .parse()
+                .map_err(|err| format!("invalid target-pointer-width: {err}"))?,
+            data_layout: json.data_layout,
+            arch: json.arch,
             options: Default::default(),
         };
 
         // FIXME: This doesn't properly validate anything and just ignores the data if it's invalid.
         // That's okay for now, the only use of this is when generating docs, which we don't do for
         // custom targets.
-        if let Some(Json::Object(mut metadata)) = obj.remove("metadata") {
-            base.metadata.description = metadata
-                .remove("description")
-                .and_then(|desc| desc.as_str().map(|desc| desc.to_owned().into()));
-            base.metadata.tier = metadata
-                .remove("tier")
-                .and_then(|tier| tier.as_u64())
-                .filter(|tier| (1..=3).contains(tier));
-            base.metadata.host_tools =
-                metadata.remove("host_tools").and_then(|host| host.as_bool());
-            base.metadata.std = metadata.remove("std").and_then(|host| host.as_bool());
+        if let Some(metadata) = json.metadata {
+            base.metadata.description = metadata.description;
+            base.metadata.tier = metadata.tier.filter(|tier| (1..=3).contains(tier));
+            base.metadata.host_tools = metadata.host_tools;
+            base.metadata.std = metadata.std;
         }
 
         let alignment_error = |field_name: &str, error: AlignFromBytesError| -> String {
@@ -65,640 +52,188 @@ impl Target {
             format!("`{}` bits is not a valid value for {field_name}: {msg}", error.align() * 8)
         };
 
-        let mut incorrect_type = vec![];
-
-        macro_rules! key {
-            ($key_name:ident) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(s) = obj.remove(&name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) {
-                    base.$key_name = s;
-                }
-            } );
-            ($key_name:ident = $json_name:expr) => ( {
-                let name = $json_name;
-                if let Some(s) = obj.remove(name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) {
-                    base.$key_name = s;
-                }
-            } );
-            ($key_name:ident = $json_name:expr, u64 as $int_ty:ty) => ( {
-                let name = $json_name;
-                if let Some(s) = obj.remove(name).and_then(|b| b.as_u64()) {
-                    base.$key_name = s as $int_ty;
-                }
-            } );
-            ($key_name:ident, bool) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(s) = obj.remove(&name).and_then(|b| b.as_bool()) {
-                    base.$key_name = s;
-                }
-            } );
-            ($key_name:ident = $json_name:expr, bool) => ( {
-                let name = $json_name;
-                if let Some(s) = obj.remove(name).and_then(|b| b.as_bool()) {
-                    base.$key_name = s;
-                }
-            } );
-            ($key_name:ident, u32) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) {
-                    if s < 1 || s > 5 {
-                        return Err("Not a valid DWARF version number".into());
-                    }
-                    base.$key_name = s as u32;
-                }
-            } );
-            ($key_name:ident, Option<bool>) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(s) = obj.remove(&name).and_then(|b| b.as_bool()) {
-                    base.$key_name = Some(s);
-                }
-            } );
-            ($key_name:ident, Option<u64>) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) {
-                    base.$key_name = Some(s);
-                }
-            } );
-            ($key_name:ident, Option<StaticCow<str>>) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(s) = obj.remove(&name).and_then(|b| Some(b.as_str()?.to_string())) {
-                    base.$key_name = Some(s.into());
-                }
-            } );
-            ($key_name:ident, Option<Align>) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(b) = obj.remove(&name).and_then(|b| b.as_u64()) {
-                    match Align::from_bits(b) {
-                        Ok(align) => base.$key_name = Some(align),
-                        Err(e) => return Err(alignment_error(&name, e)),
-                    }
-                }
-            } );
-            ($key_name:ident, BinaryFormat) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|f| f.as_str().and_then(|s| {
-                    match s.parse::<super::BinaryFormat>() {
-                        Ok(binary_format) => base.$key_name = binary_format,
-                        _ => return Some(Err(format!(
-                            "'{s}' is not a valid value for binary_format. \
-                            Use 'coff', 'elf', 'mach-o', 'wasm' or 'xcoff' "
-                        ))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, MergeFunctions) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::MergeFunctions>() {
-                        Ok(mergefunc) => base.$key_name = mergefunc,
-                        _ => return Some(Err(format!("'{}' is not a valid value for \
-                                                      merge-functions. Use 'disabled', \
-                                                      'trampolines', or 'aliases'.",
-                                                      s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, FloatAbi) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::FloatAbi>() {
-                        Ok(float_abi) => base.$key_name = Some(float_abi),
-                        _ => return Some(Err(format!("'{}' is not a valid value for \
-                                                      llvm-floatabi. Use 'soft' or 'hard'.",
-                                                      s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, RustcAbi) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::RustcAbi>() {
-                        Ok(rustc_abi) => base.$key_name = Some(rustc_abi),
-                        _ => return Some(Err(format!(
-                            "'{s}' is not a valid value for rustc-abi. \
-                            Use 'x86-softfloat' or leave the field unset."
-                        ))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, RelocModel) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::RelocModel>() {
-                        Ok(relocation_model) => base.$key_name = relocation_model,
-                        _ => return Some(Err(format!("'{}' is not a valid relocation model. \
-                                                      Run `rustc --print relocation-models` to \
-                                                      see the list of supported values.", s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, CodeModel) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::CodeModel>() {
-                        Ok(code_model) => base.$key_name = Some(code_model),
-                        _ => return Some(Err(format!("'{}' is not a valid code model. \
-                                                      Run `rustc --print code-models` to \
-                                                      see the list of supported values.", s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, TlsModel) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::TlsModel>() {
-                        Ok(tls_model) => base.$key_name = tls_model,
-                        _ => return Some(Err(format!("'{}' is not a valid TLS model. \
-                                                      Run `rustc --print tls-models` to \
-                                                      see the list of supported values.", s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, SmallDataThresholdSupport) => ( {
-                obj.remove("small-data-threshold-support").and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::SmallDataThresholdSupport>() {
-                        Ok(support) => base.small_data_threshold_support = support,
-                        _ => return Some(Err(format!("'{s}' is not a valid value for small-data-threshold-support."))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, PanicStrategy) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s {
-                        "unwind" => base.$key_name = super::PanicStrategy::Unwind,
-                        "abort" => base.$key_name = super::PanicStrategy::Abort,
-                        _ => return Some(Err(format!("'{}' is not a valid value for \
-                                                      panic-strategy. Use 'unwind' or 'abort'.",
-                                                     s))),
-                }
-                Some(Ok(()))
-            })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, RelroLevel) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::RelroLevel>() {
-                        Ok(level) => base.$key_name = level,
-                        _ => return Some(Err(format!("'{}' is not a valid value for \
-                                                      relro-level. Use 'full', 'partial, or 'off'.",
-                                                      s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, Option<SymbolVisibility>) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::SymbolVisibility>() {
-                        Ok(level) => base.$key_name = Some(level),
-                        _ => return Some(Err(format!("'{}' is not a valid value for \
-                                                      symbol-visibility. Use 'hidden', 'protected, or 'interposable'.",
-                                                      s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, DebuginfoKind) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::DebuginfoKind>() {
-                        Ok(level) => base.$key_name = level,
-                        _ => return Some(Err(
-                            format!("'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \
-                                  'dwarf-dsym' or 'pdb'.")
-                        )),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, SplitDebuginfo) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::SplitDebuginfo>() {
-                        Ok(level) => base.$key_name = level,
-                        _ => return Some(Err(format!("'{}' is not a valid value for \
-                                                      split-debuginfo. Use 'off' or 'dsymutil'.",
-                                                      s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, list) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(j) = obj.remove(&name) {
-                    if let Some(v) = j.as_array() {
-                        base.$key_name = v.iter()
-                            .map(|a| a.as_str().unwrap().to_string().into())
-                            .collect();
-                    } else {
-                        incorrect_type.push(name)
-                    }
-                }
-            } );
-            ($key_name:ident, opt_list) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(j) = obj.remove(&name) {
-                    if let Some(v) = j.as_array() {
-                        base.$key_name = Some(v.iter()
-                            .map(|a| a.as_str().unwrap().to_string().into())
-                            .collect());
-                    } else {
-                        incorrect_type.push(name)
-                    }
+        macro_rules! forward {
+            ($name:ident) => {
+                if let Some($name) = json.$name {
+                    base.$name = $name;
                 }
-            } );
-            ($key_name:ident, fallible_list) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|j| {
-                    if let Some(v) = j.as_array() {
-                        match v.iter().map(|a| FromStr::from_str(a.as_str().unwrap())).collect() {
-                            Ok(l) => { base.$key_name = l },
-                            // FIXME: `fallible_list` can't re-use the `key!` macro for list
-                            // elements and the error messages from that macro, so it has a bad
-                            // generic message instead
-                            Err(_) => return Some(Err(
-                                format!("`{:?}` is not a valid value for `{}`", j, name)
-                            )),
-                        }
-                    } else {
-                        incorrect_type.push(name)
-                    }
-                    Some(Ok(()))
-                }).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, optional) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(o) = obj.remove(&name) {
-                    base.$key_name = o
-                        .as_str()
-                        .map(|s| s.to_string().into());
-                }
-            } );
-            ($key_name:ident = $json_name:expr, LldFlavor) => ( {
-                let name = $json_name;
-                obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
-                    if let Some(flavor) = super::LldFlavor::from_str(&s) {
-                        base.$key_name = flavor;
-                    } else {
-                        return Some(Err(format!(
-                            "'{}' is not a valid value for lld-flavor. \
-                             Use 'darwin', 'gnu', 'link' or 'wasm'.",
-                            s)))
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident = $json_name:expr, LinkerFlavorCli) => ( {
-                let name = $json_name;
-                obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
-                    match super::LinkerFlavorCli::from_str(s) {
-                        Some(linker_flavor) => base.$key_name = linker_flavor,
-                        _ => return Some(Err(format!("'{}' is not a valid value for linker-flavor. \
-                                                      Use {}", s, super::LinkerFlavorCli::one_of()))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, StackProbeType) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                obj.remove(&name).and_then(|o| match super::StackProbeType::from_json(&o) {
-                    Ok(v) => {
-                        base.$key_name = v;
-                        Some(Ok(()))
-                    },
-                    Err(s) => Some(Err(
-                        format!("`{:?}` is not a valid value for `{}`: {}", o, name, s)
-                    )),
-                }).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident, SanitizerSet) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(o) = obj.remove(&name) {
-                    if let Some(a) = o.as_array() {
-                        for s in a {
-                            use super::SanitizerSet;
-                            base.$key_name |= match s.as_str() {
-                                Some("address") => SanitizerSet::ADDRESS,
-                                Some("cfi") => SanitizerSet::CFI,
-                                Some("dataflow") => SanitizerSet::DATAFLOW,
-                                Some("kcfi") => SanitizerSet::KCFI,
-                                Some("kernel-address") => SanitizerSet::KERNELADDRESS,
-                                Some("leak") => SanitizerSet::LEAK,
-                                Some("memory") => SanitizerSet::MEMORY,
-                                Some("memtag") => SanitizerSet::MEMTAG,
-                                Some("safestack") => SanitizerSet::SAFESTACK,
-                                Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
-                                Some("thread") => SanitizerSet::THREAD,
-                                Some("hwaddress") => SanitizerSet::HWADDRESS,
-                                Some(s) => return Err(format!("unknown sanitizer {}", s)),
-                                _ => return Err(format!("not a string: {:?}", s)),
-                            };
-                        }
-                    } else {
-                        incorrect_type.push(name)
-                    }
-                }
-                Ok::<(), String>(())
-            } );
-            ($key_name:ident, link_self_contained_components) => ( {
-                // Skeleton of what needs to be parsed:
-                //
-                // ```
-                // $name: {
-                //     "components": [
-                //         <array of strings>
-                //     ]
-                // }
-                // ```
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(o) = obj.remove(&name) {
-                    if let Some(o) = o.as_object() {
-                        let component_array = o.get("components")
-                            .ok_or_else(|| format!("{name}: expected a \
-                                JSON object with a `components` field."))?;
-                        let component_array = component_array.as_array()
-                            .ok_or_else(|| format!("{name}.components: expected a JSON array"))?;
-                        let mut components = super::LinkSelfContainedComponents::empty();
-                        for s in component_array {
-                            components |= match s.as_str() {
-                                Some(s) => {
-                                    super::LinkSelfContainedComponents::from_str(s)
-                                        .ok_or_else(|| format!("unknown \
-                                        `-Clink-self-contained` component: {s}"))?
-                                },
-                                _ => return Err(format!("not a string: {:?}", s)),
-                            };
-                        }
-                        base.$key_name = super::LinkSelfContainedDefault::WithComponents(components);
-                    } else {
-                        incorrect_type.push(name)
-                    }
-                }
-                Ok::<(), String>(())
-            } );
-            ($key_name:ident = $json_name:expr, link_self_contained_backwards_compatible) => ( {
-                let name = $json_name;
-                obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
-                    match s.parse::<super::LinkSelfContainedDefault>() {
-                        Ok(lsc_default) => base.$key_name = lsc_default,
-                        _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \
-                                                      Use 'false', 'true', 'musl' or 'mingw'", s))),
-                    }
-                    Some(Ok(()))
-                })).unwrap_or(Ok(()))
-            } );
-            ($key_name:ident = $json_name:expr, link_objects) => ( {
-                let name = $json_name;
-                if let Some(val) = obj.remove(name) {
-                    let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
-                        JSON object with fields per CRT object kind.", name))?;
-                    let mut args = super::CrtObjects::new();
-                    for (k, v) in obj {
-                        let kind = super::LinkOutputKind::from_str(&k).ok_or_else(|| {
-                            format!("{}: '{}' is not a valid value for CRT object kind. \
-                                     Use '(dynamic,static)-(nopic,pic)-exe' or \
-                                     '(dynamic,static)-dylib' or 'wasi-reactor-exe'", name, k)
-                        })?;
-
-                        let v = v.as_array().ok_or_else(||
-                            format!("{}.{}: expected a JSON array", name, k)
-                        )?.iter().enumerate()
-                            .map(|(i,s)| {
-                                let s = s.as_str().ok_or_else(||
-                                    format!("{}.{}[{}]: expected a JSON string", name, k, i))?;
-                                Ok(s.to_string().into())
-                            })
-                            .collect::<Result<Vec<_>, String>>()?;
-
-                        args.insert(kind, v);
-                    }
-                    base.$key_name = args;
-                }
-            } );
-            ($key_name:ident = $json_name:expr, link_args) => ( {
-                let name = $json_name;
-                if let Some(val) = obj.remove(name) {
-                    let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
-                        JSON object with fields per linker-flavor.", name))?;
-                    let mut args = super::LinkArgsCli::new();
-                    for (k, v) in obj {
-                        let flavor = super::LinkerFlavorCli::from_str(&k).ok_or_else(|| {
-                            format!("{}: '{}' is not a valid value for linker-flavor. \
-                                     Use 'em', 'gcc', 'ld' or 'msvc'", name, k)
-                        })?;
-
-                        let v = v.as_array().ok_or_else(||
-                            format!("{}.{}: expected a JSON array", name, k)
-                        )?.iter().enumerate()
-                            .map(|(i,s)| {
-                                let s = s.as_str().ok_or_else(||
-                                    format!("{}.{}[{}]: expected a JSON string", name, k, i))?;
-                                Ok(s.to_string().into())
-                            })
-                            .collect::<Result<Vec<_>, String>>()?;
-
-                        args.insert(flavor, v);
-                    }
-                    base.$key_name = args;
-                }
-            } );
-            ($key_name:ident, env) => ( {
-                let name = (stringify!($key_name)).replace("_", "-");
-                if let Some(o) = obj.remove(&name) {
-                    if let Some(a) = o.as_array() {
-                        for o in a {
-                            if let Some(s) = o.as_str() {
-                                if let [k, v] = *s.split('=').collect::<Vec<_>>() {
-                                    base.$key_name
-                                        .to_mut()
-                                        .push((k.to_string().into(), v.to_string().into()))
-                                }
-                            }
-                        }
-                    } else {
-                        incorrect_type.push(name)
-                    }
+            };
+        }
+        macro_rules! forward_opt {
+            ($name:ident) => {
+                if let Some($name) = json.$name {
+                    base.$name = Some($name);
                 }
-            } );
-            ($key_name:ident, target_families) => ( {
-                if let Some(value) = obj.remove("target-family") {
-                    if let Some(v) = value.as_array() {
-                        base.$key_name = v.iter()
-                            .map(|a| a.as_str().unwrap().to_string().into())
-                            .collect();
-                    } else if let Some(v) = value.as_str() {
-                        base.$key_name = vec![v.to_string().into()].into();
-                    }
+            };
+        }
+
+        if let Some(target_endian) = json.target_endian {
+            base.endian = target_endian.0;
+        }
+
+        forward!(frame_pointer);
+        forward!(c_int_width);
+        forward_opt!(c_enum_min_bits); // if None, matches c_int_width
+        forward!(os);
+        forward!(env);
+        forward!(abi);
+        forward!(vendor);
+        forward_opt!(linker);
+        forward!(linker_flavor_json);
+        forward!(lld_flavor_json);
+        forward!(linker_is_gnu_json);
+        forward!(pre_link_objects);
+        forward!(post_link_objects);
+        forward!(pre_link_objects_self_contained);
+        forward!(post_link_objects_self_contained);
+
+        // Deserializes the backwards-compatible variants of `-Clink-self-contained`
+        if let Some(link_self_contained) = json.link_self_contained_backwards_compatible {
+            base.link_self_contained = link_self_contained;
+        }
+        // Deserializes the components variant of `-Clink-self-contained`
+        if let Some(link_self_contained) = json.link_self_contained {
+            let components = link_self_contained
+                .components
+                .into_iter()
+                .fold(LinkSelfContainedComponents::empty(), |a, b| a | b);
+            base.link_self_contained = LinkSelfContainedDefault::WithComponents(components);
+        }
+
+        forward!(pre_link_args_json);
+        forward!(late_link_args_json);
+        forward!(late_link_args_dynamic_json);
+        forward!(late_link_args_static_json);
+        forward!(post_link_args_json);
+        forward_opt!(link_script);
+
+        if let Some(link_env) = json.link_env {
+            for s in link_env {
+                if let [k, v] = *s.split('=').collect::<Vec<_>>() {
+                    base.link_env.to_mut().push((k.to_string().into(), v.to_string().into()))
+                } else {
+                    return Err(format!("link-env value '{s}' must be of the pattern 'KEY=VALUE'"));
                 }
-            } );
+            }
         }
 
-        if let Some(j) = obj.remove("target-endian") {
-            if let Some(s) = j.as_str() {
-                base.endian = s.parse()?;
-            } else {
-                incorrect_type.push("target-endian".into())
+        forward!(link_env_remove);
+        forward!(asm_args);
+        forward!(cpu);
+        forward!(need_explicit_cpu);
+        forward!(features);
+        forward!(dynamic_linking);
+        forward_opt!(direct_access_external_data);
+        forward!(dll_tls_export);
+        forward!(only_cdylib);
+        forward!(executables);
+        forward!(relocation_model);
+        forward_opt!(code_model);
+        forward!(tls_model);
+        forward!(disable_redzone);
+        forward!(function_sections);
+        forward!(dll_prefix);
+        forward!(dll_suffix);
+        forward!(exe_suffix);
+        forward!(staticlib_prefix);
+        forward!(staticlib_suffix);
+
+        if let Some(target_family) = json.target_family {
+            match target_family {
+                TargetFamiliesJson::Array(families) => base.families = families,
+                TargetFamiliesJson::String(family) => base.families = vec![family].into(),
             }
         }
 
-        if let Some(fp) = obj.remove("frame-pointer") {
-            if let Some(s) = fp.as_str() {
-                base.frame_pointer = s
-                    .parse()
-                    .map_err(|()| format!("'{s}' is not a valid value for frame-pointer"))?;
-            } else {
-                incorrect_type.push("frame-pointer".into())
+        forward!(abi_return_struct_as_int);
+        forward!(is_like_aix);
+        forward!(is_like_darwin);
+        forward!(is_like_solaris);
+        forward!(is_like_windows);
+        forward!(is_like_msvc);
+        forward!(is_like_wasm);
+        forward!(is_like_android);
+        forward!(binary_format);
+        forward!(default_dwarf_version);
+        forward!(allows_weak_linkage);
+        forward!(has_rpath);
+        forward!(no_default_libraries);
+        forward!(position_independent_executables);
+        forward!(static_position_independent_executables);
+        forward!(plt_by_default);
+        forward!(relro_level);
+        forward!(archive_format);
+        forward!(allow_asm);
+        forward!(main_needs_argc_argv);
+        forward!(has_thread_local);
+        forward!(obj_is_bitcode);
+        forward!(bitcode_llvm_cmdline);
+        forward_opt!(max_atomic_width);
+        forward_opt!(min_atomic_width);
+        forward!(atomic_cas);
+        forward!(panic_strategy);
+        forward!(crt_static_allows_dylibs);
+        forward!(crt_static_default);
+        forward!(crt_static_respected);
+        forward!(stack_probes);
+
+        if let Some(min_global_align) = json.min_global_align {
+            match Align::from_bits(min_global_align) {
+                Ok(align) => base.min_global_align = Some(align),
+                Err(e) => return Err(alignment_error("min-global-align", e)),
             }
         }
 
-        key!(c_int_width = "target-c-int-width", u64 as u16);
-        key!(c_enum_min_bits, Option<u64>); // if None, matches c_int_width
-        key!(os);
-        key!(env);
-        key!(abi);
-        key!(vendor);
-        key!(linker, optional);
-        key!(linker_flavor_json = "linker-flavor", LinkerFlavorCli)?;
-        key!(lld_flavor_json = "lld-flavor", LldFlavor)?;
-        key!(linker_is_gnu_json = "linker-is-gnu", bool);
-        key!(pre_link_objects = "pre-link-objects", link_objects);
-        key!(post_link_objects = "post-link-objects", link_objects);
-        key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects);
-        key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects);
-        // Deserializes the backwards-compatible variants of `-Clink-self-contained`
-        key!(
-            link_self_contained = "crt-objects-fallback",
-            link_self_contained_backwards_compatible
-        )?;
-        // Deserializes the components variant of `-Clink-self-contained`
-        key!(link_self_contained, link_self_contained_components)?;
-        key!(pre_link_args_json = "pre-link-args", link_args);
-        key!(late_link_args_json = "late-link-args", link_args);
-        key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args);
-        key!(late_link_args_static_json = "late-link-args-static", link_args);
-        key!(post_link_args_json = "post-link-args", link_args);
-        key!(link_script, optional);
-        key!(link_env, env);
-        key!(link_env_remove, list);
-        key!(asm_args, list);
-        key!(cpu);
-        key!(need_explicit_cpu, bool);
-        key!(features);
-        key!(dynamic_linking, bool);
-        key!(direct_access_external_data, Option<bool>);
-        key!(dll_tls_export, bool);
-        key!(only_cdylib, bool);
-        key!(executables, bool);
-        key!(relocation_model, RelocModel)?;
-        key!(code_model, CodeModel)?;
-        key!(tls_model, TlsModel)?;
-        key!(disable_redzone, bool);
-        key!(function_sections, bool);
-        key!(dll_prefix);
-        key!(dll_suffix);
-        key!(exe_suffix);
-        key!(staticlib_prefix);
-        key!(staticlib_suffix);
-        key!(families, target_families);
-        key!(abi_return_struct_as_int, bool);
-        key!(is_like_aix, bool);
-        key!(is_like_darwin, bool);
-        key!(is_like_solaris, bool);
-        key!(is_like_windows, bool);
-        key!(is_like_msvc, bool);
-        key!(is_like_wasm, bool);
-        key!(is_like_android, bool);
-        key!(binary_format, BinaryFormat)?;
-        key!(default_dwarf_version, u32);
-        key!(allows_weak_linkage, bool);
-        key!(has_rpath, bool);
-        key!(no_default_libraries, bool);
-        key!(position_independent_executables, bool);
-        key!(static_position_independent_executables, bool);
-        key!(plt_by_default, bool);
-        key!(relro_level, RelroLevel)?;
-        key!(archive_format);
-        key!(allow_asm, bool);
-        key!(main_needs_argc_argv, bool);
-        key!(has_thread_local, bool);
-        key!(obj_is_bitcode, bool);
-        key!(bitcode_llvm_cmdline);
-        key!(max_atomic_width, Option<u64>);
-        key!(min_atomic_width, Option<u64>);
-        key!(atomic_cas, bool);
-        key!(panic_strategy, PanicStrategy)?;
-        key!(crt_static_allows_dylibs, bool);
-        key!(crt_static_default, bool);
-        key!(crt_static_respected, bool);
-        key!(stack_probes, StackProbeType)?;
-        key!(min_global_align, Option<Align>);
-        key!(default_codegen_units, Option<u64>);
-        key!(default_codegen_backend, Option<StaticCow<str>>);
-        key!(trap_unreachable, bool);
-        key!(requires_lto, bool);
-        key!(singlethread, bool);
-        key!(no_builtins, bool);
-        key!(default_visibility, Option<SymbolVisibility>)?;
-        key!(emit_debug_gdb_scripts, bool);
-        key!(requires_uwtable, bool);
-        key!(default_uwtable, bool);
-        key!(simd_types_indirect, bool);
-        key!(limit_rdylib_exports, bool);
-        key!(override_export_symbols, opt_list);
-        key!(merge_functions, MergeFunctions)?;
-        key!(mcount = "target-mcount");
-        key!(llvm_mcount_intrinsic, optional);
-        key!(llvm_abiname);
-        key!(llvm_floatabi, FloatAbi)?;
-        key!(rustc_abi, RustcAbi)?;
-        key!(relax_elf_relocations, bool);
-        key!(llvm_args, list);
-        key!(use_ctors_section, bool);
-        key!(eh_frame_header, bool);
-        key!(has_thumb_interworking, bool);
-        key!(debuginfo_kind, DebuginfoKind)?;
-        key!(split_debuginfo, SplitDebuginfo)?;
-        key!(supported_split_debuginfo, fallible_list)?;
-        key!(supported_sanitizers, SanitizerSet)?;
-        key!(generate_arange_section, bool);
-        key!(supports_stack_protector, bool);
-        key!(small_data_threshold_support, SmallDataThresholdSupport)?;
-        key!(entry_name);
-        key!(supports_xray, bool);
+        forward_opt!(default_codegen_units);
+        forward_opt!(default_codegen_backend);
+        forward!(trap_unreachable);
+        forward!(requires_lto);
+        forward!(singlethread);
+        forward!(no_builtins);
+        forward_opt!(default_visibility);
+        forward!(emit_debug_gdb_scripts);
+        forward!(requires_uwtable);
+        forward!(default_uwtable);
+        forward!(simd_types_indirect);
+        forward!(limit_rdylib_exports);
+        forward_opt!(override_export_symbols);
+        forward!(merge_functions);
+        forward!(mcount);
+        forward_opt!(llvm_mcount_intrinsic);
+        forward!(llvm_abiname);
+        forward_opt!(llvm_floatabi);
+        forward_opt!(rustc_abi);
+        forward!(relax_elf_relocations);
+        forward!(llvm_args);
+        forward!(use_ctors_section);
+        forward!(eh_frame_header);
+        forward!(has_thumb_interworking);
+        forward!(debuginfo_kind);
+        forward!(split_debuginfo);
+        forward!(supported_split_debuginfo);
+
+        if let Some(supported_sanitizers) = json.supported_sanitizers {
+            base.supported_sanitizers =
+                supported_sanitizers.into_iter().fold(SanitizerSet::empty(), |a, b| a | b);
+        }
+
+        forward!(generate_arange_section);
+        forward!(supports_stack_protector);
+        forward!(small_data_threshold_support);
+        forward!(entry_name);
+        forward!(supports_xray);
 
         // we're going to run `update_from_cli`, but that won't change the target's AbiMap
         // FIXME: better factor the Target definition so we enforce this on a type level
         let abi_map = AbiMap::from_target(&base);
-
-        if let Some(abi_str) = obj.remove("entry-abi") {
-            if let Json::String(abi_str) = abi_str {
-                match abi_str.parse::<ExternAbi>() {
-                    Ok(abi) => base.options.entry_abi = abi_map.canonize_abi(abi, false).unwrap(),
-                    Err(_) => return Err(format!("{abi_str} is not a valid ExternAbi")),
-                }
-            } else {
-                incorrect_type.push("entry-abi".to_owned())
-            }
+        if let Some(entry_abi) = json.entry_abi {
+            base.options.entry_abi = abi_map.canonize_abi(entry_abi.0, false).unwrap();
         }
 
         base.update_from_cli();
         base.check_consistency(TargetKind::Json)?;
 
-        // Each field should have been read using `Json::remove` so any keys remaining are unused.
-        let remaining_keys = obj.keys();
-        Ok((
-            base,
-            TargetWarnings { unused_fields: remaining_keys.cloned().collect(), incorrect_type },
-        ))
+        Ok((base, TargetWarnings { unused_fields: vec![] }))
     }
 }
 
@@ -877,3 +412,189 @@ impl ToJson for Target {
         Json::Object(d)
     }
 }
+
+#[derive(serde_derive::Deserialize)]
+struct LinkSelfContainedComponentsWrapper {
+    components: Vec<LinkSelfContainedComponents>,
+}
+
+#[derive(serde_derive::Deserialize)]
+#[serde(untagged)]
+enum TargetFamiliesJson {
+    Array(StaticCow<[StaticCow<str>]>),
+    String(StaticCow<str>),
+}
+
+/// `Endian` is in `rustc_abi`, which doesn't have access to the macro and serde.
+struct EndianWrapper(rustc_abi::Endian);
+impl FromStr for EndianWrapper {
+    type Err = String;
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        rustc_abi::Endian::from_str(s).map(Self)
+    }
+}
+crate::json::serde_deserialize_from_str!(EndianWrapper);
+
+/// `ExternAbi` is in `rustc_abi`, which doesn't have access to the macro and serde.
+struct ExternAbiWrapper(rustc_abi::ExternAbi);
+impl FromStr for ExternAbiWrapper {
+    type Err = String;
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        rustc_abi::ExternAbi::from_str(s)
+            .map(Self)
+            .map_err(|_| format!("{s} is not a valid extern ABI"))
+    }
+}
+crate::json::serde_deserialize_from_str!(ExternAbiWrapper);
+
+#[derive(serde_derive::Deserialize)]
+struct TargetSpecJsonMetadata {
+    description: Option<StaticCow<str>>,
+    tier: Option<u64>,
+    host_tools: Option<bool>,
+    std: Option<bool>,
+}
+
+#[derive(serde_derive::Deserialize)]
+#[serde(rename_all = "kebab-case")]
+// Ensure that all unexpected fields get turned into errors.
+// This helps users stay up to date when the schema changes instead of silently
+// ignoring their old values.
+#[serde(deny_unknown_fields)]
+struct TargetSpecJson {
+    llvm_target: StaticCow<str>,
+    target_pointer_width: String,
+    data_layout: StaticCow<str>,
+    arch: StaticCow<str>,
+
+    metadata: Option<TargetSpecJsonMetadata>,
+
+    // options:
+    target_endian: Option<EndianWrapper>,
+    frame_pointer: Option<FramePointer>,
+    #[serde(rename = "target-c-int-width")]
+    c_int_width: Option<u16>,
+    c_enum_min_bits: Option<u64>,
+    os: Option<StaticCow<str>>,
+    env: Option<StaticCow<str>>,
+    abi: Option<StaticCow<str>>,
+    vendor: Option<StaticCow<str>>,
+    linker: Option<StaticCow<str>>,
+    #[serde(rename = "linker-flavor")]
+    linker_flavor_json: Option<LinkerFlavorCli>,
+    #[serde(rename = "lld-flavor")]
+    lld_flavor_json: Option<LldFlavor>,
+    #[serde(rename = "linker-is-gnu")]
+    linker_is_gnu_json: Option<bool>,
+    #[serde(rename = "pre-link-objects")]
+    pre_link_objects: Option<CrtObjects>,
+    #[serde(rename = "post-link-objects")]
+    post_link_objects: Option<CrtObjects>,
+    #[serde(rename = "pre-link-objects-fallback")]
+    pre_link_objects_self_contained: Option<CrtObjects>,
+    #[serde(rename = "post-link-objects-fallback")]
+    post_link_objects_self_contained: Option<CrtObjects>,
+    #[serde(rename = "crt-objects-fallback")]
+    link_self_contained_backwards_compatible: Option<LinkSelfContainedDefault>,
+    link_self_contained: Option<LinkSelfContainedComponentsWrapper>,
+    #[serde(rename = "pre-link-args")]
+    pre_link_args_json: Option<LinkArgsCli>,
+    #[serde(rename = "late-link-args")]
+    late_link_args_json: Option<LinkArgsCli>,
+    #[serde(rename = "late-link-args-dynamic")]
+    late_link_args_dynamic_json: Option<LinkArgsCli>,
+    #[serde(rename = "late-link-args-static")]
+    late_link_args_static_json: Option<LinkArgsCli>,
+    #[serde(rename = "post-link-args")]
+    post_link_args_json: Option<LinkArgsCli>,
+    link_script: Option<StaticCow<str>>,
+    link_env: Option<Vec<StaticCow<str>>>,
+    link_env_remove: Option<StaticCow<[StaticCow<str>]>>,
+    asm_args: Option<StaticCow<[StaticCow<str>]>>,
+    cpu: Option<StaticCow<str>>,
+    need_explicit_cpu: Option<bool>,
+    features: Option<StaticCow<str>>,
+    dynamic_linking: Option<bool>,
+    direct_access_external_data: Option<bool>,
+    dll_tls_export: Option<bool>,
+    only_cdylib: Option<bool>,
+    executables: Option<bool>,
+    relocation_model: Option<RelocModel>,
+    code_model: Option<CodeModel>,
+    tls_model: Option<TlsModel>,
+    disable_redzone: Option<bool>,
+    function_sections: Option<bool>,
+    dll_prefix: Option<StaticCow<str>>,
+    dll_suffix: Option<StaticCow<str>>,
+    exe_suffix: Option<StaticCow<str>>,
+    staticlib_prefix: Option<StaticCow<str>>,
+    staticlib_suffix: Option<StaticCow<str>>,
+    target_family: Option<TargetFamiliesJson>,
+    abi_return_struct_as_int: Option<bool>,
+    is_like_aix: Option<bool>,
+    is_like_darwin: Option<bool>,
+    is_like_solaris: Option<bool>,
+    is_like_windows: Option<bool>,
+    is_like_msvc: Option<bool>,
+    is_like_wasm: Option<bool>,
+    is_like_android: Option<bool>,
+    binary_format: Option<BinaryFormat>,
+    default_dwarf_version: Option<u32>,
+    allows_weak_linkage: Option<bool>,
+    has_rpath: Option<bool>,
+    no_default_libraries: Option<bool>,
+    position_independent_executables: Option<bool>,
+    static_position_independent_executables: Option<bool>,
+    plt_by_default: Option<bool>,
+    relro_level: Option<RelroLevel>,
+    archive_format: Option<StaticCow<str>>,
+    allow_asm: Option<bool>,
+    main_needs_argc_argv: Option<bool>,
+    has_thread_local: Option<bool>,
+    obj_is_bitcode: Option<bool>,
+    bitcode_llvm_cmdline: Option<StaticCow<str>>,
+    max_atomic_width: Option<u64>,
+    min_atomic_width: Option<u64>,
+    atomic_cas: Option<bool>,
+    panic_strategy: Option<PanicStrategy>,
+    crt_static_allows_dylibs: Option<bool>,
+    crt_static_default: Option<bool>,
+    crt_static_respected: Option<bool>,
+    stack_probes: Option<StackProbeType>,
+    min_global_align: Option<u64>,
+    default_codegen_units: Option<u64>,
+    default_codegen_backend: Option<StaticCow<str>>,
+    trap_unreachable: Option<bool>,
+    requires_lto: Option<bool>,
+    singlethread: Option<bool>,
+    no_builtins: Option<bool>,
+    default_visibility: Option<SymbolVisibility>,
+    emit_debug_gdb_scripts: Option<bool>,
+    requires_uwtable: Option<bool>,
+    default_uwtable: Option<bool>,
+    simd_types_indirect: Option<bool>,
+    limit_rdylib_exports: Option<bool>,
+    override_export_symbols: Option<StaticCow<[StaticCow<str>]>>,
+    merge_functions: Option<MergeFunctions>,
+    #[serde(rename = "target-mcount")]
+    mcount: Option<StaticCow<str>>,
+    llvm_mcount_intrinsic: Option<StaticCow<str>>,
+    llvm_abiname: Option<StaticCow<str>>,
+    llvm_floatabi: Option<FloatAbi>,
+    rustc_abi: Option<RustcAbi>,
+    relax_elf_relocations: Option<bool>,
+    llvm_args: Option<StaticCow<[StaticCow<str>]>>,
+    use_ctors_section: Option<bool>,
+    eh_frame_header: Option<bool>,
+    has_thumb_interworking: Option<bool>,
+    debuginfo_kind: Option<DebuginfoKind>,
+    split_debuginfo: Option<SplitDebuginfo>,
+    supported_split_debuginfo: Option<StaticCow<[SplitDebuginfo]>>,
+    supported_sanitizers: Option<Vec<SanitizerSet>>,
+    generate_arange_section: Option<bool>,
+    supports_stack_protector: Option<bool>,
+    small_data_threshold_support: Option<SmallDataThresholdSupport>,
+    entry_name: Option<StaticCow<str>>,
+    supports_xray: Option<bool>,
+    entry_abi: Option<ExternAbiWrapper>,
+}
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 4bc0d88a910..c64cd9a51b7 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -37,6 +37,7 @@
 //!
 //! [JSON]: https://json.org
 
+use core::result::Result;
 use std::borrow::Cow;
 use std::collections::BTreeMap;
 use std::hash::{Hash, Hasher};
@@ -198,18 +199,29 @@ impl LldFlavor {
             LldFlavor::Link => "link",
         }
     }
+}
 
-    fn from_str(s: &str) -> Option<Self> {
-        Some(match s {
+impl FromStr for LldFlavor {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(match s {
             "darwin" => LldFlavor::Ld64,
             "gnu" => LldFlavor::Ld,
             "link" => LldFlavor::Link,
             "wasm" => LldFlavor::Wasm,
-            _ => return None,
+            _ => {
+                return Err(
+                    "invalid value for lld flavor: '{s}', expected one of 'darwin', 'gnu', 'link', 'wasm'"
+                        .into(),
+                );
+            }
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(LldFlavor);
+
 impl ToJson for LldFlavor {
     fn to_json(&self) -> Json {
         self.as_str().to_json()
@@ -494,19 +506,23 @@ macro_rules! linker_flavor_cli_impls {
                 concat!("one of: ", $($string, " ",)*)
             }
 
-            pub fn from_str(s: &str) -> Option<LinkerFlavorCli> {
-                Some(match s {
-                    $($string => $($flavor)*,)*
-                    _ => return None,
-                })
-            }
-
             pub fn desc(self) -> &'static str {
                 match self {
                     $($($flavor)* => $string,)*
                 }
             }
         }
+
+        impl FromStr for LinkerFlavorCli {
+            type Err = String;
+
+            fn from_str(s: &str) -> Result<LinkerFlavorCli, Self::Err> {
+                Ok(match s {
+                    $($string => $($flavor)*,)*
+                    _ => return Err(format!("invalid linker flavor, allowed values: {}", Self::one_of())),
+                })
+            }
+        }
     )
 }
 
@@ -540,6 +556,8 @@ linker_flavor_cli_impls! {
     (LinkerFlavorCli::Em) "em"
 }
 
+crate::json::serde_deserialize_from_str!(LinkerFlavorCli);
+
 impl ToJson for LinkerFlavorCli {
     fn to_json(&self) -> Json {
         self.desc().to_json()
@@ -573,19 +591,26 @@ pub enum LinkSelfContainedDefault {
 
 /// Parses a backwards-compatible `-Clink-self-contained` option string, without components.
 impl FromStr for LinkSelfContainedDefault {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<LinkSelfContainedDefault, ()> {
+    fn from_str(s: &str) -> Result<LinkSelfContainedDefault, Self::Err> {
         Ok(match s {
             "false" => LinkSelfContainedDefault::False,
             "true" | "wasm" => LinkSelfContainedDefault::True,
             "musl" => LinkSelfContainedDefault::InferredForMusl,
             "mingw" => LinkSelfContainedDefault::InferredForMingw,
-            _ => return Err(()),
+            _ => {
+                return Err(format!(
+                    "'{s}' is not a valid `-Clink-self-contained` default. \
+                        Use 'false', 'true', 'wasm', 'musl' or 'mingw'",
+                ));
+            }
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(LinkSelfContainedDefault);
+
 impl ToJson for LinkSelfContainedDefault {
     fn to_json(&self) -> Json {
         match *self {
@@ -652,19 +677,6 @@ bitflags::bitflags! {
 rustc_data_structures::external_bitflags_debug! { LinkSelfContainedComponents }
 
 impl LinkSelfContainedComponents {
-    /// Parses a single `-Clink-self-contained` well-known component, not a set of flags.
-    pub fn from_str(s: &str) -> Option<LinkSelfContainedComponents> {
-        Some(match s {
-            "crto" => LinkSelfContainedComponents::CRT_OBJECTS,
-            "libc" => LinkSelfContainedComponents::LIBC,
-            "unwind" => LinkSelfContainedComponents::UNWIND,
-            "linker" => LinkSelfContainedComponents::LINKER,
-            "sanitizers" => LinkSelfContainedComponents::SANITIZERS,
-            "mingw" => LinkSelfContainedComponents::MINGW,
-            _ => return None,
-        })
-    }
-
     /// Return the component's name.
     ///
     /// Returns `None` if the bitflags aren't a singular component (but a mix of multiple flags).
@@ -708,6 +720,29 @@ impl LinkSelfContainedComponents {
     }
 }
 
+impl FromStr for LinkSelfContainedComponents {
+    type Err = String;
+
+    /// Parses a single `-Clink-self-contained` well-known component, not a set of flags.
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(match s {
+            "crto" => LinkSelfContainedComponents::CRT_OBJECTS,
+            "libc" => LinkSelfContainedComponents::LIBC,
+            "unwind" => LinkSelfContainedComponents::UNWIND,
+            "linker" => LinkSelfContainedComponents::LINKER,
+            "sanitizers" => LinkSelfContainedComponents::SANITIZERS,
+            "mingw" => LinkSelfContainedComponents::MINGW,
+            _ => {
+                return Err(format!(
+                    "'{s}' is not a valid link-self-contained component, expected 'crto', 'libc', 'unwind', 'linker', 'sanitizers', 'mingw'"
+                ));
+            }
+        })
+    }
+}
+
+crate::json::serde_deserialize_from_str!(LinkSelfContainedComponents);
+
 impl ToJson for LinkSelfContainedComponents {
     fn to_json(&self) -> Json {
         let components: Vec<_> = Self::all_components()
@@ -821,6 +856,25 @@ impl PanicStrategy {
     }
 }
 
+impl FromStr for PanicStrategy {
+    type Err = String;
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(match s {
+            "unwind" => PanicStrategy::Unwind,
+            "abort" => PanicStrategy::Abort,
+            _ => {
+                return Err(format!(
+                    "'{}' is not a valid value for \
+                    panic-strategy. Use 'unwind' or 'abort'.",
+                    s
+                ));
+            }
+        })
+    }
+}
+
+crate::json::serde_deserialize_from_str!(PanicStrategy);
+
 impl ToJson for PanicStrategy {
     fn to_json(&self) -> Json {
         match *self {
@@ -867,18 +921,24 @@ impl SymbolVisibility {
 }
 
 impl FromStr for SymbolVisibility {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<SymbolVisibility, ()> {
+    fn from_str(s: &str) -> Result<SymbolVisibility, Self::Err> {
         match s {
             "hidden" => Ok(SymbolVisibility::Hidden),
             "protected" => Ok(SymbolVisibility::Protected),
             "interposable" => Ok(SymbolVisibility::Interposable),
-            _ => Err(()),
+            _ => Err(format!(
+                "'{}' is not a valid value for \
+                    symbol-visibility. Use 'hidden', 'protected, or 'interposable'.",
+                s
+            )),
         }
     }
 }
 
+crate::json::serde_deserialize_from_str!(SymbolVisibility);
+
 impl ToJson for SymbolVisibility {
     fn to_json(&self) -> Json {
         match *self {
@@ -890,19 +950,25 @@ impl ToJson for SymbolVisibility {
 }
 
 impl FromStr for RelroLevel {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<RelroLevel, ()> {
+    fn from_str(s: &str) -> Result<RelroLevel, Self::Err> {
         match s {
             "full" => Ok(RelroLevel::Full),
             "partial" => Ok(RelroLevel::Partial),
             "off" => Ok(RelroLevel::Off),
             "none" => Ok(RelroLevel::None),
-            _ => Err(()),
+            _ => Err(format!(
+                "'{}' is not a valid value for \
+                        relro-level. Use 'full', 'partial, 'off', or 'none'.",
+                s
+            )),
         }
     }
 }
 
+crate::json::serde_deserialize_from_str!(RelroLevel);
+
 impl ToJson for RelroLevel {
     fn to_json(&self) -> Json {
         match *self {
@@ -923,7 +989,7 @@ pub enum SmallDataThresholdSupport {
 }
 
 impl FromStr for SmallDataThresholdSupport {
-    type Err = ();
+    type Err = String;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         if s == "none" {
@@ -935,11 +1001,13 @@ impl FromStr for SmallDataThresholdSupport {
         } else if let Some(arg) = s.strip_prefix("llvm-arg=") {
             Ok(Self::LlvmArg(arg.to_string().into()))
         } else {
-            Err(())
+            Err(format!("'{s}' is not a valid value for small-data-threshold-support."))
         }
     }
 }
 
+crate::json::serde_deserialize_from_str!(SmallDataThresholdSupport);
+
 impl ToJson for SmallDataThresholdSupport {
     fn to_json(&self) -> Value {
         match self {
@@ -969,18 +1037,25 @@ impl MergeFunctions {
 }
 
 impl FromStr for MergeFunctions {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<MergeFunctions, ()> {
+    fn from_str(s: &str) -> Result<MergeFunctions, Self::Err> {
         match s {
             "disabled" => Ok(MergeFunctions::Disabled),
             "trampolines" => Ok(MergeFunctions::Trampolines),
             "aliases" => Ok(MergeFunctions::Aliases),
-            _ => Err(()),
+            _ => Err(format!(
+                "'{}' is not a valid value for \
+                    merge-functions. Use 'disabled', \
+                    'trampolines', or 'aliases'.",
+                s
+            )),
         }
     }
 }
 
+crate::json::serde_deserialize_from_str!(MergeFunctions);
+
 impl ToJson for MergeFunctions {
     fn to_json(&self) -> Json {
         match *self {
@@ -1040,9 +1115,9 @@ impl RelocModel {
 }
 
 impl FromStr for RelocModel {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<RelocModel, ()> {
+    fn from_str(s: &str) -> Result<RelocModel, Self::Err> {
         Ok(match s {
             "static" => RelocModel::Static,
             "pic" => RelocModel::Pic,
@@ -1051,11 +1126,19 @@ impl FromStr for RelocModel {
             "ropi" => RelocModel::Ropi,
             "rwpi" => RelocModel::Rwpi,
             "ropi-rwpi" => RelocModel::RopiRwpi,
-            _ => return Err(()),
+            _ => {
+                return Err(format!(
+                    "invalid relocation model '{s}'.
+                        Run `rustc --print relocation-models` to \
+                        see the list of supported values.'"
+                ));
+            }
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(RelocModel);
+
 impl ToJson for RelocModel {
     fn to_json(&self) -> Json {
         self.desc().to_json()
@@ -1072,20 +1155,28 @@ pub enum CodeModel {
 }
 
 impl FromStr for CodeModel {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<CodeModel, ()> {
+    fn from_str(s: &str) -> Result<CodeModel, Self::Err> {
         Ok(match s {
             "tiny" => CodeModel::Tiny,
             "small" => CodeModel::Small,
             "kernel" => CodeModel::Kernel,
             "medium" => CodeModel::Medium,
             "large" => CodeModel::Large,
-            _ => return Err(()),
+            _ => {
+                return Err(format!(
+                    "'{s}' is not a valid code model. \
+                        Run `rustc --print code-models` to \
+                        see the list of supported values."
+                ));
+            }
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(CodeModel);
+
 impl ToJson for CodeModel {
     fn to_json(&self) -> Json {
         match *self {
@@ -1107,17 +1198,25 @@ pub enum FloatAbi {
 }
 
 impl FromStr for FloatAbi {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<FloatAbi, ()> {
+    fn from_str(s: &str) -> Result<FloatAbi, Self::Err> {
         Ok(match s {
             "soft" => FloatAbi::Soft,
             "hard" => FloatAbi::Hard,
-            _ => return Err(()),
+            _ => {
+                return Err(format!(
+                    "'{}' is not a valid value for \
+                        llvm-floatabi. Use 'soft' or 'hard'.",
+                    s
+                ));
+            }
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(FloatAbi);
+
 impl ToJson for FloatAbi {
     fn to_json(&self) -> Json {
         match *self {
@@ -1138,17 +1237,24 @@ pub enum RustcAbi {
 }
 
 impl FromStr for RustcAbi {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<RustcAbi, ()> {
+    fn from_str(s: &str) -> Result<RustcAbi, Self::Err> {
         Ok(match s {
             "x86-sse2" => RustcAbi::X86Sse2,
             "x86-softfloat" => RustcAbi::X86Softfloat,
-            _ => return Err(()),
+            _ => {
+                return Err(format!(
+                    "'{s}' is not a valid value for rustc-abi. \
+                        Use 'x86-softfloat' or leave the field unset."
+                ));
+            }
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(RustcAbi);
+
 impl ToJson for RustcAbi {
     fn to_json(&self) -> Json {
         match *self {
@@ -1169,9 +1275,9 @@ pub enum TlsModel {
 }
 
 impl FromStr for TlsModel {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<TlsModel, ()> {
+    fn from_str(s: &str) -> Result<TlsModel, Self::Err> {
         Ok(match s {
             // Note the difference "general" vs "global" difference. The model name is "general",
             // but the user-facing option name is "global" for consistency with other compilers.
@@ -1180,11 +1286,19 @@ impl FromStr for TlsModel {
             "initial-exec" => TlsModel::InitialExec,
             "local-exec" => TlsModel::LocalExec,
             "emulated" => TlsModel::Emulated,
-            _ => return Err(()),
+            _ => {
+                return Err(format!(
+                    "'{s}' is not a valid TLS model. \
+                        Run `rustc --print tls-models` to \
+                        see the list of supported values."
+                ));
+            }
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(TlsModel);
+
 impl ToJson for TlsModel {
     fn to_json(&self) -> Json {
         match *self {
@@ -1230,19 +1344,6 @@ impl LinkOutputKind {
         }
     }
 
-    pub(super) fn from_str(s: &str) -> Option<LinkOutputKind> {
-        Some(match s {
-            "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe,
-            "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe,
-            "static-nopic-exe" => LinkOutputKind::StaticNoPicExe,
-            "static-pic-exe" => LinkOutputKind::StaticPicExe,
-            "dynamic-dylib" => LinkOutputKind::DynamicDylib,
-            "static-dylib" => LinkOutputKind::StaticDylib,
-            "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe,
-            _ => return None,
-        })
-    }
-
     pub fn can_link_dylib(self) -> bool {
         match self {
             LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => false,
@@ -1255,6 +1356,31 @@ impl LinkOutputKind {
     }
 }
 
+impl FromStr for LinkOutputKind {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<LinkOutputKind, Self::Err> {
+        Ok(match s {
+            "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe,
+            "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe,
+            "static-nopic-exe" => LinkOutputKind::StaticNoPicExe,
+            "static-pic-exe" => LinkOutputKind::StaticPicExe,
+            "dynamic-dylib" => LinkOutputKind::DynamicDylib,
+            "static-dylib" => LinkOutputKind::StaticDylib,
+            "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe,
+            _ => {
+                return Err(format!(
+                    "invalid value for CRT object kind. \
+                        Use '(dynamic,static)-(nopic,pic)-exe' or \
+                        '(dynamic,static)-dylib' or 'wasi-reactor-exe'"
+                ));
+            }
+        })
+    }
+}
+
+crate::json::serde_deserialize_from_str!(LinkOutputKind);
+
 impl fmt::Display for LinkOutputKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.write_str(self.as_str())
@@ -1290,18 +1416,25 @@ impl DebuginfoKind {
 }
 
 impl FromStr for DebuginfoKind {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<Self, ()> {
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
         Ok(match s {
             "dwarf" => DebuginfoKind::Dwarf,
             "dwarf-dsym" => DebuginfoKind::DwarfDsym,
             "pdb" => DebuginfoKind::Pdb,
-            _ => return Err(()),
+            _ => {
+                return Err(format!(
+                    "'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \
+                        'dwarf-dsym' or 'pdb'."
+                ));
+            }
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(DebuginfoKind);
+
 impl ToJson for DebuginfoKind {
     fn to_json(&self) -> Json {
         self.as_str().to_json()
@@ -1354,18 +1487,25 @@ impl SplitDebuginfo {
 }
 
 impl FromStr for SplitDebuginfo {
-    type Err = ();
+    type Err = String;
 
-    fn from_str(s: &str) -> Result<Self, ()> {
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
         Ok(match s {
             "off" => SplitDebuginfo::Off,
             "unpacked" => SplitDebuginfo::Unpacked,
             "packed" => SplitDebuginfo::Packed,
-            _ => return Err(()),
+            _ => {
+                return Err(format!(
+                    "'{s}' is not a valid value for \
+                        split-debuginfo. Use 'off', 'unpacked', or 'packed'.",
+                ));
+            }
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(SplitDebuginfo);
+
 impl ToJson for SplitDebuginfo {
     fn to_json(&self) -> Json {
         self.as_str().to_json()
@@ -1378,7 +1518,9 @@ impl fmt::Display for SplitDebuginfo {
     }
 }
 
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize)]
+#[serde(tag = "kind")]
+#[serde(rename_all = "kebab-case")]
 pub enum StackProbeType {
     /// Don't emit any stack probes.
     None,
@@ -1390,44 +1532,10 @@ pub enum StackProbeType {
     Call,
     /// Use inline option for LLVM versions later than specified in `min_llvm_version_for_inline`
     /// and call `__rust_probestack` otherwise.
-    InlineOrCall { min_llvm_version_for_inline: (u32, u32, u32) },
-}
-
-impl StackProbeType {
-    fn from_json(json: &Json) -> Result<Self, String> {
-        let object = json.as_object().ok_or_else(|| "expected a JSON object")?;
-        let kind = object
-            .get("kind")
-            .and_then(|o| o.as_str())
-            .ok_or_else(|| "expected `kind` to be a string")?;
-        match kind {
-            "none" => Ok(StackProbeType::None),
-            "inline" => Ok(StackProbeType::Inline),
-            "call" => Ok(StackProbeType::Call),
-            "inline-or-call" => {
-                let min_version = object
-                    .get("min-llvm-version-for-inline")
-                    .and_then(|o| o.as_array())
-                    .ok_or_else(|| "expected `min-llvm-version-for-inline` to be an array")?;
-                let mut iter = min_version.into_iter().map(|v| {
-                    let int = v.as_u64().ok_or_else(
-                        || "expected `min-llvm-version-for-inline` values to be integers",
-                    )?;
-                    u32::try_from(int)
-                        .map_err(|_| "`min-llvm-version-for-inline` values don't convert to u32")
-                });
-                let min_llvm_version_for_inline = (
-                    iter.next().unwrap_or(Ok(11))?,
-                    iter.next().unwrap_or(Ok(0))?,
-                    iter.next().unwrap_or(Ok(0))?,
-                );
-                Ok(StackProbeType::InlineOrCall { min_llvm_version_for_inline })
-            }
-            _ => Err(String::from(
-                "`kind` expected to be one of `none`, `inline`, `call` or `inline-or-call`",
-            )),
-        }
-    }
+    InlineOrCall {
+        #[serde(rename = "min-llvm-version-for-inline")]
+        min_llvm_version_for_inline: (u32, u32, u32),
+    },
 }
 
 impl ToJson for StackProbeType {
@@ -1549,6 +1657,29 @@ impl fmt::Display for SanitizerSet {
     }
 }
 
+impl FromStr for SanitizerSet {
+    type Err = String;
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(match s {
+            "address" => SanitizerSet::ADDRESS,
+            "cfi" => SanitizerSet::CFI,
+            "dataflow" => SanitizerSet::DATAFLOW,
+            "kcfi" => SanitizerSet::KCFI,
+            "kernel-address" => SanitizerSet::KERNELADDRESS,
+            "leak" => SanitizerSet::LEAK,
+            "memory" => SanitizerSet::MEMORY,
+            "memtag" => SanitizerSet::MEMTAG,
+            "safestack" => SanitizerSet::SAFESTACK,
+            "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
+            "thread" => SanitizerSet::THREAD,
+            "hwaddress" => SanitizerSet::HWADDRESS,
+            s => return Err(format!("unknown sanitizer {s}")),
+        })
+    }
+}
+
+crate::json::serde_deserialize_from_str!(SanitizerSet);
+
 impl ToJson for SanitizerSet {
     fn to_json(&self) -> Json {
         self.into_iter()
@@ -1587,17 +1718,19 @@ impl FramePointer {
 }
 
 impl FromStr for FramePointer {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, ()> {
+    type Err = String;
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
         Ok(match s {
             "always" => Self::Always,
             "non-leaf" => Self::NonLeaf,
             "may-omit" => Self::MayOmit,
-            _ => return Err(()),
+            _ => return Err(format!("'{s}' is not a valid value for frame-pointer")),
         })
     }
 }
 
+crate::json::serde_deserialize_from_str!(FramePointer);
+
 impl ToJson for FramePointer {
     fn to_json(&self) -> Json {
         match *self {
@@ -1685,7 +1818,7 @@ impl BinaryFormat {
 }
 
 impl FromStr for BinaryFormat {
-    type Err = ();
+    type Err = String;
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match s {
             "coff" => Ok(Self::Coff),
@@ -1693,11 +1826,16 @@ impl FromStr for BinaryFormat {
             "mach-o" => Ok(Self::MachO),
             "wasm" => Ok(Self::Wasm),
             "xcoff" => Ok(Self::Xcoff),
-            _ => Err(()),
+            _ => Err(format!(
+                "'{s}' is not a valid value for binary_format. \
+                    Use 'coff', 'elf', 'mach-o', 'wasm' or 'xcoff' "
+            )),
         }
     }
 }
 
+crate::json::serde_deserialize_from_str!(BinaryFormat);
+
 impl ToJson for BinaryFormat {
     fn to_json(&self) -> Json {
         match self {
@@ -2130,12 +2268,11 @@ pub(crate) use cvs;
 #[derive(Debug, PartialEq)]
 pub struct TargetWarnings {
     unused_fields: Vec<String>,
-    incorrect_type: Vec<String>,
 }
 
 impl TargetWarnings {
     pub fn empty() -> Self {
-        Self { unused_fields: Vec::new(), incorrect_type: Vec::new() }
+        Self { unused_fields: Vec::new() }
     }
 
     pub fn warning_messages(&self) -> Vec<String> {
@@ -2146,12 +2283,6 @@ impl TargetWarnings {
                 self.unused_fields.join(", ")
             ));
         }
-        if !self.incorrect_type.is_empty() {
-            warnings.push(format!(
-                "target json file contains fields whose value doesn't have the correct json type: {}",
-                self.incorrect_type.join(", ")
-            ));
-        }
         warnings
     }
 }
@@ -3325,7 +3456,8 @@ impl Target {
     /// Test target self-consistency and JSON encoding/decoding roundtrip.
     #[cfg(test)]
     fn test_target(mut self) {
-        let recycled_target = Target::from_json(self.to_json()).map(|(j, _)| j);
+        let recycled_target =
+            Target::from_json(&serde_json::to_string(&self.to_json()).unwrap()).map(|(j, _)| j);
         self.update_to_cli();
         self.check_consistency(TargetKind::Builtin).unwrap();
         assert_eq!(recycled_target, Ok(self));
@@ -3373,8 +3505,7 @@ impl Target {
 
         fn load_file(path: &Path) -> Result<(Target, TargetWarnings), String> {
             let contents = fs::read_to_string(path).map_err(|e| e.to_string())?;
-            let obj = serde_json::from_str(&contents).map_err(|e| e.to_string())?;
-            Target::from_json(obj)
+            Target::from_json(&contents)
         }
 
         match *target_tuple {
@@ -3422,10 +3553,7 @@ impl Target {
                     Err(format!("could not find specification for target {target_tuple:?}"))
                 }
             }
-            TargetTuple::TargetJson { ref contents, .. } => {
-                let obj = serde_json::from_str(contents).map_err(|e| e.to_string())?;
-                Target::from_json(obj)
-            }
+            TargetTuple::TargetJson { ref contents, .. } => Target::from_json(contents),
         }
     }
 
diff --git a/compiler/rustc_target/src/tests.rs b/compiler/rustc_target/src/tests.rs
index 76375170db6..ee847a84007 100644
--- a/compiler/rustc_target/src/tests.rs
+++ b/compiler/rustc_target/src/tests.rs
@@ -2,8 +2,7 @@ use crate::spec::Target;
 
 #[test]
 fn report_unused_fields() {
-    let json = serde_json::from_str(
-        r#"
+    let json = r#"
     {
         "arch": "powerpc64",
         "data-layout": "e-m:e-i64:64-n32:64",
@@ -11,47 +10,8 @@ fn report_unused_fields() {
         "target-pointer-width": "64",
         "code-mode": "foo"
     }
-    "#,
-    )
-    .unwrap();
-    let warnings = Target::from_json(json).unwrap().1;
-    assert_eq!(warnings.warning_messages().len(), 1);
-    assert!(warnings.warning_messages().join("\n").contains("code-mode"));
-}
-
-#[test]
-fn report_incorrect_json_type() {
-    let json = serde_json::from_str(
-        r#"
-    {
-        "arch": "powerpc64",
-        "data-layout": "e-m:e-i64:64-n32:64",
-        "llvm-target": "powerpc64le-elf",
-        "target-pointer-width": "64",
-        "link-env-remove": "foo"
-    }
-    "#,
-    )
-    .unwrap();
-    let warnings = Target::from_json(json).unwrap().1;
-    assert_eq!(warnings.warning_messages().len(), 1);
-    assert!(warnings.warning_messages().join("\n").contains("link-env-remove"));
-}
-
-#[test]
-fn no_warnings_for_valid_target() {
-    let json = serde_json::from_str(
-        r#"
-    {
-        "arch": "powerpc64",
-        "data-layout": "e-m:e-i64:64-n32:64",
-        "llvm-target": "powerpc64le-elf",
-        "target-pointer-width": "64",
-        "link-env-remove": ["foo"]
-    }
-    "#,
-    )
-    .unwrap();
-    let warnings = Target::from_json(json).unwrap().1;
-    assert_eq!(warnings.warning_messages().len(), 0);
+    "#;
+    let result = Target::from_json(json);
+    eprintln!("{result:#?}");
+    assert!(result.is_err());
 }