about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock6
-rw-r--r--Cargo.toml27
-rw-r--r--compiler/rustc_abi/Cargo.toml4
-rw-r--r--compiler/rustc_abi/src/extern_abi.rs25
-rw-r--r--compiler/rustc_abi/src/lib.rs2
-rw-r--r--compiler/rustc_ast/Cargo.toml10
-rw-r--r--compiler/rustc_ast/src/ast.rs6
-rw-r--r--compiler/rustc_ast_lowering/Cargo.toml4
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs6
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/pat.rs9
-rw-r--r--compiler/rustc_ast_passes/Cargo.toml4
-rw-r--r--compiler/rustc_ast_passes/messages.ftl2
-rw-r--r--compiler/rustc_ast_pretty/Cargo.toml4
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs2
-rw-r--r--compiler/rustc_attr_parsing/Cargo.toml2
-rw-r--r--compiler/rustc_borrowck/Cargo.toml8
-rw-r--r--compiler/rustc_borrowck/src/consumers.rs11
-rw-r--r--compiler/rustc_borrowck/src/lib.rs117
-rw-r--r--compiler/rustc_borrowck/src/nll.rs67
-rw-r--r--compiler/rustc_borrowck/src/polonius/dump.rs87
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs162
-rw-r--r--compiler/rustc_borrowck/src/region_infer/values.rs2
-rw-r--r--compiler/rustc_borrowck/src/root_cx.rs302
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs1
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs28
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs2
-rw-r--r--compiler/rustc_builtin_macros/Cargo.toml4
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs5
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml14
-rw-r--r--compiler/rustc_codegen_llvm/src/back/archive.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/va_arg.rs9
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml16
-rw-r--r--compiler/rustc_codegen_ssa/src/back/archive.rs24
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs3
-rw-r--r--compiler/rustc_const_eval/Cargo.toml6
-rw-r--r--compiler/rustc_data_structures/Cargo.toml18
-rw-r--r--compiler/rustc_data_structures/src/frozen.rs2
-rw-r--r--compiler/rustc_driver_impl/Cargo.toml6
-rw-r--r--compiler/rustc_error_messages/Cargo.toml2
-rw-r--r--compiler/rustc_errors/Cargo.toml4
-rw-r--r--compiler/rustc_expand/Cargo.toml4
-rw-r--r--compiler/rustc_feature/Cargo.toml2
-rw-r--r--compiler/rustc_feature/src/accepted.rs3
-rw-r--r--compiler/rustc_feature/src/unstable.rs3
-rw-r--r--compiler/rustc_fluent_macro/Cargo.toml4
-rw-r--r--compiler/rustc_fs_util/Cargo.toml2
-rw-r--r--compiler/rustc_hir/Cargo.toml8
-rw-r--r--compiler/rustc_hir/src/hir.rs8
-rw-r--r--compiler/rustc_hir_analysis/Cargo.toml4
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs59
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs4
-rw-r--r--compiler/rustc_hir_typeck/Cargo.toml4
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs12
-rw-r--r--compiler/rustc_incremental/Cargo.toml4
-rw-r--r--compiler/rustc_index_macros/Cargo.toml4
-rw-r--r--compiler/rustc_infer/Cargo.toml4
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs4
-rw-r--r--compiler/rustc_interface/Cargo.toml2
-rw-r--r--compiler/rustc_lexer/Cargo.toml2
-rw-r--r--compiler/rustc_lexer/src/lib.rs34
-rw-r--r--compiler/rustc_lint/Cargo.toml2
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs11
-rw-r--r--compiler/rustc_llvm/Cargo.toml2
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp99
-rw-r--r--compiler/rustc_log/Cargo.toml2
-rw-r--r--compiler/rustc_macros/Cargo.toml4
-rw-r--r--compiler/rustc_metadata/Cargo.toml10
-rw-r--r--compiler/rustc_middle/Cargo.toml12
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs4
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs682
-rw-r--r--compiler/rustc_middle/src/query/mod.rs2
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs8
-rw-r--r--compiler/rustc_mir_build/Cargo.toml6
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/into.rs8
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/mod.rs18
-rw-r--r--compiler/rustc_mir_dataflow/Cargo.toml4
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/graphviz.rs13
-rw-r--r--compiler/rustc_mir_transform/Cargo.toml6
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs21
-rw-r--r--compiler/rustc_mir_transform/src/coroutine/by_move_body.rs7
-rw-r--r--compiler/rustc_mir_transform/src/coroutine/drop.rs12
-rw-r--r--compiler/rustc_mir_transform/src/coverage/expansion.rs127
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs1
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs142
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs34
-rw-r--r--compiler/rustc_mir_transform/src/coverage/unexpand.rs48
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs20
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs1
-rw-r--r--compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs9
-rw-r--r--compiler/rustc_mir_transform/src/pass_manager.rs40
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs14
-rw-r--r--compiler/rustc_monomorphize/Cargo.toml4
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs15
-rw-r--r--compiler/rustc_next_trait_solver/Cargo.toml4
-rw-r--r--compiler/rustc_parse/Cargo.toml8
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs2
-rw-r--r--compiler/rustc_parse_format/Cargo.toml2
-rw-r--r--compiler/rustc_passes/Cargo.toml2
-rw-r--r--compiler/rustc_passes/src/liveness.rs2
-rw-r--r--compiler/rustc_pattern_analysis/Cargo.toml6
-rw-r--r--compiler/rustc_privacy/Cargo.toml2
-rw-r--r--compiler/rustc_proc_macro/Cargo.toml2
-rw-r--r--compiler/rustc_public/Cargo.toml4
-rw-r--r--compiler/rustc_query_impl/Cargo.toml4
-rw-r--r--compiler/rustc_query_system/Cargo.toml2
-rw-r--r--compiler/rustc_resolve/Cargo.toml10
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs9
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs2
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs5
-rw-r--r--compiler/rustc_resolve/src/late.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs93
-rw-r--r--compiler/rustc_sanitizers/Cargo.toml4
-rw-r--r--compiler/rustc_serialize/Cargo.toml6
-rw-r--r--compiler/rustc_session/Cargo.toml8
-rw-r--r--compiler/rustc_span/Cargo.toml8
-rw-r--r--compiler/rustc_symbol_mangling/Cargo.toml4
-rw-r--r--compiler/rustc_target/Cargo.toml6
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs4
-rw-r--r--compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs2
-rw-r--r--compiler/rustc_thread_pool/Cargo.toml4
-rw-r--r--compiler/rustc_trait_selection/Cargo.toml6
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs45
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs23
-rw-r--r--compiler/rustc_traits/Cargo.toml2
-rw-r--r--compiler/rustc_transmute/Cargo.toml4
-rw-r--r--compiler/rustc_ty_utils/Cargo.toml4
-rw-r--r--compiler/rustc_ty_utils/src/nested_bodies.rs4
-rw-r--r--compiler/rustc_type_ir/Cargo.toml12
-rw-r--r--compiler/rustc_type_ir_macros/Cargo.toml4
-rw-r--r--library/core/src/ops/range.rs32
-rw-r--r--library/coretests/tests/floats/f128.rs64
-rw-r--r--library/coretests/tests/floats/f16.rs60
-rw-r--r--library/coretests/tests/floats/f32.rs64
-rw-r--r--library/coretests/tests/floats/f64.rs58
-rw-r--r--library/coretests/tests/floats/mod.rs102
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--library/std/src/sys/pal/unix/os.rs20
-rw-r--r--library/std/tests/floats/f32.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs6
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs8
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs752
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs37
-rw-r--r--src/bootstrap/src/core/builder/mod.rs2
-rw-r--r--src/bootstrap/src/core/builder/tests.rs227
-rw-r--r--src/bootstrap/src/core/config/config.rs10
-rw-r--r--src/bootstrap/src/lib.rs29
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rw-r--r--src/ci/docker/host-x86_64/tidy/Dockerfile2
-rw-r--r--src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md10
-rw-r--r--src/librustdoc/Cargo.toml10
-rw-r--r--src/librustdoc/html/render/search_index.rs255
-rw-r--r--src/librustdoc/html/static/js/rustdoc.d.ts5
-rw-r--r--src/librustdoc/html/static/js/search.js79
-rw-r--r--src/tools/build-manifest/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_lints/src/equatable_if_let.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs2
-rw-r--r--src/tools/collect-license-metadata/Cargo.toml2
-rw-r--r--src/tools/compiletest/Cargo.toml8
-rw-r--r--src/tools/compiletest/src/executor.rs13
-rw-r--r--src/tools/compiletest/src/lib.rs3
-rw-r--r--src/tools/compiletest/src/panic_hook.rs136
-rw-r--r--src/tools/coverage-dump/Cargo.toml4
-rw-r--r--src/tools/features-status-dump/Cargo.toml2
-rw-r--r--src/tools/generate-copyright/Cargo.toml2
-rw-r--r--src/tools/jsondocck/Cargo.toml2
-rw-r--r--src/tools/jsondoclint/Cargo.toml4
-rw-r--r--src/tools/lint-docs/Cargo.toml4
-rw-r--r--src/tools/lint-docs/src/lib.rs42
-rw-r--r--src/tools/lint-docs/src/main.rs13
-rw-r--r--src/tools/llvm-bitcode-linker/Cargo.toml4
-rw-r--r--src/tools/nix-dev-shell/shell.nix3
-rw-r--r--src/tools/opt-dist/Cargo.toml4
-rw-r--r--src/tools/run-make-support/Cargo.toml4
-rw-r--r--src/tools/rustfmt/src/patterns.rs2
-rw-r--r--src/tools/tidy/Cargo.toml2
-rw-r--r--tests/codegen-llvm/async-fn-debug-awaitee-field.rs8
-rw-r--r--tests/codegen-llvm/cffi/c-variadic-ffi.rs2
-rw-r--r--tests/codegen-units/item-collection/async-fn-impl.rs10
-rw-r--r--tests/codegen-units/item-collection/opaque-return-impls.rs89
-rw-r--r--tests/coverage/async.cov-map8
-rw-r--r--tests/coverage/async.coverage4
-rw-r--r--tests/coverage/async.rs2
-rw-r--r--tests/coverage/closure.cov-map35
-rw-r--r--tests/coverage/closure.coverage6
-rw-r--r--tests/coverage/macro_in_closure.cov-map7
-rw-r--r--tests/coverage/rustfmt-skip.cov-map12
-rw-r--r--tests/coverage/rustfmt-skip.coverage18
-rw-r--r--tests/coverage/rustfmt-skip.rs17
-rw-r--r--tests/mir-opt/building/loop_match_diverges.break_to_block_unit.built.after.mir63
-rw-r--r--tests/mir-opt/building/loop_match_diverges.infinite_a.built.after.mir51
-rw-r--r--tests/mir-opt/building/loop_match_diverges.rs68
-rw-r--r--tests/mir-opt/building/loop_match_diverges.simple.built.after.mir187
-rw-r--r--tests/ui/abi/unsupported-varargs-fnptr.rs2
-rw-r--r--tests/ui/abi/unsupported-varargs-fnptr.stderr2
-rw-r--r--tests/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr5
-rw-r--r--tests/ui/async-await/future-sizes/async-awaiting-fut.rs4
-rw-r--r--tests/ui/async-await/future-sizes/async-awaiting-fut.stdout37
-rw-r--r--tests/ui/async-await/future-sizes/large-arg.rs4
-rw-r--r--tests/ui/async-await/future-sizes/large-arg.stdout42
-rw-r--r--tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs16
-rw-r--r--tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr33
-rw-r--r--tests/ui/c-variadic/issue-86053-1.rs2
-rw-r--r--tests/ui/c-variadic/issue-86053-1.stderr2
-rw-r--r--tests/ui/c-variadic/same-program-multiple-abis-arm.rs1
-rw-r--r--tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs1
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs2
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr2
-rw-r--r--tests/ui/feature-gates/feature-gate-c_variadic.rs6
-rw-r--r--tests/ui/feature-gates/feature-gate-c_variadic.stderr18
-rw-r--r--tests/ui/frontmatter/frontmatter-whitespace-1.rs2
-rw-r--r--tests/ui/frontmatter/frontmatter-whitespace-1.stderr18
-rw-r--r--tests/ui/frontmatter/frontmatter-whitespace-2.rs5
-rw-r--r--tests/ui/frontmatter/frontmatter-whitespace-2.stderr38
-rw-r--r--tests/ui/frontmatter/multifrontmatter-2.rs6
-rw-r--r--tests/ui/frontmatter/multifrontmatter-2.stderr22
-rw-r--r--tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr8
-rw-r--r--tests/ui/impl-trait/non-defining-uses/as-projection-term.rs4
-rw-r--r--tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr17
-rw-r--r--tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs11
-rw-r--r--tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr17
-rw-r--r--tests/ui/impl-trait/non-defining-uses/recursive-call.rs30
-rw-r--r--tests/ui/loop-match/diverges.rs44
-rw-r--r--tests/ui/mir/issue-83499-input-output-iteration-ice.rs2
-rw-r--r--tests/ui/mir/issue-83499-input-output-iteration-ice.stderr2
-rw-r--r--tests/ui/parser/variadic-ffi-semantic-restrictions.rs42
-rw-r--r--tests/ui/parser/variadic-ffi-semantic-restrictions.stderr42
-rw-r--r--tests/ui/print_type_sizes/async.rs4
-rw-r--r--tests/ui/print_type_sizes/async.stdout33
-rw-r--r--tests/ui/privacy/private-field-ty-err.stderr5
-rw-r--r--tests/ui/stats/input-stats.stderr20
-rw-r--r--tests/ui/suggestions/negative-literal-infered-to-unsigned.rs13
-rw-r--r--tests/ui/suggestions/negative-literal-infered-to-unsigned.stderr25
-rw-r--r--tests/ui/type-alias-impl-trait/split-paths-may-dangle.rs11
-rw-r--r--tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.rs14
-rw-r--r--tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.stderr14
248 files changed, 3989 insertions, 2261 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 8b9c243879c..a8df53f3a41 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -158,11 +158,11 @@ checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
 
 [[package]]
 name = "ar_archive_writer"
-version = "0.4.2"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01667f6f40216b9a0b2945e05fed5f1ad0ab6470e69cb9378001e37b1c0668e4"
+checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b"
 dependencies = [
- "object 0.36.7",
+ "object 0.37.3",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 2c5044f6a35..67c7a9d67ed 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -59,33 +59,6 @@ exclude = [
   "obj",
 ]
 
-[workspace.dependencies]
-# tidy-alphabetical-start
-bitflags = "2.9.3"
-derive-where = "1.6.0"
-either = "1.15.0"
-indexmap = "2.10.0"
-itertools = "0.12.1"
-# FIXME: Remove this pin once this rustix issue is resolved
-# https://github.com/bytecodealliance/rustix/issues/1496
-libc = "=0.2.174"
-measureme = "12.0.3"
-memchr = "2.7.5"
-odht = { version = "0.3.1", features = ["nightly"] }
-polonius-engine = "0.13.0"
-proc-macro2 = "1.0.101"
-quote = "1.0.40"
-rustc-demangle = "0.1.26"
-rustc-hash = "2.1.1"
-rustc-literal-escaper = "0.0.5"
-rustc_apfloat = "0.2.3"
-scoped-tls = "1.0.1"
-serde_json = "1.0.142"
-tempfile = "3.20.0"
-thin-vec = "0.2.14"
-tracing = "0.1.37"
-# tidy-alphabetical-end
-
 [profile.release.package.rustc_thread_pool]
 # The rustc fork of Rayon has deadlock detection code which intermittently
 # causes overflows in the CI (see https://github.com/rust-lang/rust/issues/90227)
diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml
index 3de6d186b8a..83d96d8d04d 100644
--- a/compiler/rustc_abi/Cargo.toml
+++ b/compiler/rustc_abi/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
+bitflags = "2.4.1"
 rand = { version = "0.9.0", default-features = false, optional = true }
 rand_xoshiro = { version = "0.7.0", optional = true }
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
@@ -15,7 +15,7 @@ rustc_index = { path = "../rustc_index", default-features = false }
 rustc_macros = { path = "../rustc_macros", optional = true }
 rustc_serialize = { path = "../rustc_serialize", optional = true }
 rustc_span = { path = "../rustc_span", optional = true }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
index 41d744e1946..e3b2b1eff72 100644
--- a/compiler/rustc_abi/src/extern_abi.rs
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -6,6 +6,8 @@ use std::hash::{Hash, Hasher};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
 #[cfg(feature = "nightly")]
 use rustc_macros::{Decodable, Encodable};
+#[cfg(feature = "nightly")]
+use rustc_span::Symbol;
 
 use crate::AbiFromStrErr;
 
@@ -226,6 +228,13 @@ impl StableOrd for ExternAbi {
 #[cfg(feature = "nightly")]
 rustc_error_messages::into_diag_arg_using_display!(ExternAbi);
 
+#[cfg(feature = "nightly")]
+pub enum CVariadicStatus {
+    NotSupported,
+    Stable,
+    Unstable { feature: Symbol },
+}
+
 impl ExternAbi {
     /// An ABI "like Rust"
     ///
@@ -238,23 +247,33 @@ impl ExternAbi {
         matches!(self, Rust | RustCall | RustCold)
     }
 
-    pub fn supports_varargs(self) -> bool {
+    /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*
+    /// of such functions via `extern` blocks; there's a separate check during AST construction
+    /// guarding *definitions* of variadic functions.
+    #[cfg(feature = "nightly")]
+    pub fn supports_c_variadic(self) -> CVariadicStatus {
         // * C and Cdecl obviously support varargs.
         // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
         // * EfiApi is based on Win64 or C, so it also supports it.
+        // * System automatically falls back to C when used with variadics, therefore supports it.
         //
         // * Stdcall does not, because it would be impossible for the callee to clean
         //   up the arguments. (callee doesn't know how many arguments are there)
         // * Same for Fastcall, Vectorcall and Thiscall.
         // * Other calling conventions are related to hardware or the compiler itself.
+        //
+        // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.
         match self {
             Self::C { .. }
             | Self::Cdecl { .. }
             | Self::Aapcs { .. }
             | Self::Win64 { .. }
             | Self::SysV64 { .. }
-            | Self::EfiApi => true,
-            _ => false,
+            | Self::EfiApi => CVariadicStatus::Stable,
+            Self::System { .. } => {
+                CVariadicStatus::Unstable { feature: rustc_span::sym::extern_system_varargs }
+            }
+            _ => CVariadicStatus::NotSupported,
         }
     }
 }
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index a1a0de5aaf8..369874521e5 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -63,6 +63,8 @@ mod tests;
 
 pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
 pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
+#[cfg(feature = "nightly")]
+pub use extern_abi::CVariadicStatus;
 pub use extern_abi::{ExternAbi, all_names};
 #[cfg(feature = "nightly")]
 pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml
index 7f948a65220..155e14a3796 100644
--- a/compiler/rustc_ast/Cargo.toml
+++ b/compiler/rustc_ast/Cargo.toml
@@ -5,9 +5,9 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
-memchr.workspace = true
-rustc-literal-escaper.workspace = true
+bitflags = "2.4.1"
+memchr = "2.7.4"
+rustc-literal-escaper = "0.0.5"
 rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_index = { path = "../rustc_index" }
@@ -15,6 +15,6 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index de3e0e0c87f..802a6fa3249 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -937,7 +937,7 @@ pub enum PatKind {
 #[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Walkable)]
 pub enum PatFieldsRest {
     /// `module::StructName { field, ..}`
-    Rest,
+    Rest(Span),
     /// `module::StructName { field, syntax error }`
     Recovered(ErrorGuaranteed),
     /// `module::StructName { field }`
@@ -4051,8 +4051,8 @@ mod size_asserts {
     static_assert_size!(Local, 96);
     static_assert_size!(MetaItemLit, 40);
     static_assert_size!(Param, 40);
-    static_assert_size!(Pat, 72);
-    static_assert_size!(PatKind, 48);
+    static_assert_size!(Pat, 80);
+    static_assert_size!(PatKind, 56);
     static_assert_size!(Path, 24);
     static_assert_size!(PathSegment, 24);
     static_assert_size!(Stmt, 32);
diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml
index 317742a3bb8..6ac258155fe 100644
--- a/compiler/rustc_ast_lowering/Cargo.toml
+++ b/compiler/rustc_ast_lowering/Cargo.toml
@@ -24,6 +24,6 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index cbd17d66b75..3674814b796 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1434,10 +1434,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         self.dcx().emit_err(FunctionalRecordUpdateDestructuringAssignment {
                             span: e.span,
                         });
-                        true
+                        Some(self.lower_span(e.span))
                     }
-                    StructRest::Rest(_) => true,
-                    StructRest::None => false,
+                    StructRest::Rest(span) => Some(self.lower_span(*span)),
+                    StructRest::None => None,
                 };
                 let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted);
                 return self.pat_without_dbm(lhs.span, struct_pat);
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 70595391b85..137207bde1f 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -2508,7 +2508,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         fields: &'hir [hir::PatField<'hir>],
     ) -> &'hir hir::Pat<'hir> {
         let qpath = hir::QPath::LangItem(lang_item, self.lower_span(span));
-        self.pat(span, hir::PatKind::Struct(qpath, fields, false))
+        self.pat(span, hir::PatKind::Struct(qpath, fields, None))
     }
 
     fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, HirId) {
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index b8f86247875..ed159f37051 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -106,10 +106,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         break hir::PatKind::Struct(
                             qpath,
                             fs,
-                            matches!(
-                                etc,
-                                ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_)
-                            ),
+                            match etc {
+                                ast::PatFieldsRest::Rest(sp) => Some(self.lower_span(*sp)),
+                                ast::PatFieldsRest::Recovered(_) => Some(Span::default()),
+                                _ => None,
+                            },
                         );
                     }
                     PatKind::Tuple(pats) => {
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
index 6d2bcbed22b..3e04f8b11ec 100644
--- a/compiler/rustc_ast_passes/Cargo.toml
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools.workspace = true
+itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
@@ -18,5 +18,5 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-thin-vec.workspace = true
+thin-vec = "0.2.12"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index a95f1443968..e5f1fcdc4b4 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -57,7 +57,7 @@ ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetim
     .label = {ast_passes_auto_super_lifetime}
     .suggestion = remove the super traits or lifetime bounds
 
-ast_passes_bad_c_variadic = only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+ast_passes_bad_c_variadic = defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 
 ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block
     .cannot_have = cannot have a body
diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml
index 0c7e55305b4..b704040be96 100644
--- a/compiler/rustc_ast_pretty/Cargo.toml
+++ b/compiler/rustc_ast_pretty/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools.workspace = true
+itertools = "0.12"
 rustc_ast = { path = "../rustc_ast" }
 rustc_lexer = { path = "../rustc_lexer" }
 rustc_span = { path = "../rustc_span" }
@@ -13,5 +13,5 @@ rustc_span = { path = "../rustc_span" }
 
 [dev-dependencies]
 # tidy-alphabetical-start
-thin-vec.workspace = true
+thin-vec = "0.2.12"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index a056ce3e29d..41b520b04c9 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1769,7 +1769,7 @@ impl<'a> State<'a> {
                     },
                     |f| f.pat.span,
                 );
-                if let ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) = etc {
+                if let ast::PatFieldsRest::Rest(_) | ast::PatFieldsRest::Recovered(_) = etc {
                     if !fields.is_empty() {
                         self.word_space(",");
                     }
diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml
index 8bfde43fd33..79193f394fe 100644
--- a/compiler/rustc_attr_parsing/Cargo.toml
+++ b/compiler/rustc_attr_parsing/Cargo.toml
@@ -18,5 +18,5 @@ rustc_parse = { path = "../rustc_parse" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-thin-vec.workspace = true
+thin-vec = "0.2.12"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml
index 5f9dc41766b..9e7d55180a2 100644
--- a/compiler/rustc_borrowck/Cargo.toml
+++ b/compiler/rustc_borrowck/Cargo.toml
@@ -5,9 +5,9 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-either.workspace = true
-itertools.workspace = true
-polonius-engine.workspace = true
+either = "1.5.0"
+itertools = "0.12"
+polonius-engine = "0.13.0"
 rustc_abi = { path = "../rustc_abi" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
@@ -25,5 +25,5 @@ rustc_span = { path = "../rustc_span" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_traits = { path = "../rustc_traits" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index 1c4ff5a6779..54897371410 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -17,7 +17,7 @@ pub use super::polonius::legacy::{
     RichLocation, RustcFacts,
 };
 pub use super::region_infer::RegionInferenceContext;
-use crate::{BorrowCheckRootCtxt, do_mir_borrowck};
+use crate::BorrowCheckRootCtxt;
 
 /// Struct used during mir borrowck to collect bodies with facts for a typeck root and all
 /// its nested bodies.
@@ -127,13 +127,6 @@ pub fn get_bodies_with_borrowck_facts(
 ) -> FxHashMap<LocalDefId, BodyWithBorrowckFacts<'_>> {
     let mut root_cx =
         BorrowCheckRootCtxt::new(tcx, root_def_id, Some(BorrowckConsumer::new(options)));
-
-    // See comment in `rustc_borrowck::mir_borrowck`
-    let nested_bodies = tcx.nested_bodies_within(root_def_id);
-    for def_id in nested_bodies {
-        root_cx.get_or_insert_nested(def_id);
-    }
-
-    do_mir_borrowck(&mut root_cx, root_def_id);
+    root_cx.do_mir_borrowck();
     root_cx.consumer.unwrap().bodies
 }
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index ce78ae203a4..5d2dda8b0e7 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -22,8 +22,10 @@ use std::ops::{ControlFlow, Deref};
 use std::rc::Rc;
 
 use borrow_set::LocalsStateAtExit;
+use polonius_engine::AllFacts;
 use root_cx::BorrowCheckRootCtxt;
 use rustc_abi::FieldIdx;
+use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::graph::dominators::Dominators;
 use rustc_errors::LintDiagnostic;
@@ -32,6 +34,7 @@ use rustc_hir::CRATE_HIR_ID;
 use rustc_hir::def_id::LocalDefId;
 use rustc_index::bit_set::MixedBitSet;
 use rustc_index::{IndexSlice, IndexVec};
+use rustc_infer::infer::outlives::env::RegionBoundPairs;
 use rustc_infer::infer::{
     InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
 };
@@ -53,7 +56,7 @@ use smallvec::SmallVec;
 use tracing::{debug, instrument};
 
 use crate::borrow_set::{BorrowData, BorrowSet};
-use crate::consumers::BodyWithBorrowckFacts;
+use crate::consumers::{BodyWithBorrowckFacts, RustcFacts};
 use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
 use crate::diagnostics::{
     AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
@@ -61,15 +64,17 @@ use crate::diagnostics::{
 use crate::path_utils::*;
 use crate::place_ext::PlaceExt;
 use crate::places_conflict::{PlaceConflictBias, places_conflict};
-use crate::polonius::PoloniusDiagnosticsContext;
 use crate::polonius::legacy::{
     PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
 };
+use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
 use crate::prefixes::PrefixSet;
 use crate::region_infer::RegionInferenceContext;
+use crate::region_infer::opaque_types::DeferredOpaqueTypeError;
 use crate::renumber::RegionCtxt;
 use crate::session_diagnostics::VarNeedNotMut;
-use crate::type_check::MirTypeckResults;
+use crate::type_check::free_region_relations::UniversalRegionRelations;
+use crate::type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults};
 
 mod borrow_set;
 mod borrowck_errors;
@@ -129,18 +134,7 @@ fn mir_borrowck(
         Ok(tcx.arena.alloc(opaque_types))
     } else {
         let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None);
-        // We need to manually borrowck all nested bodies from the HIR as
-        // we do not generate MIR for dead code. Not doing so causes us to
-        // never check closures in dead code.
-        let nested_bodies = tcx.nested_bodies_within(def);
-        for def_id in nested_bodies {
-            root_cx.get_or_insert_nested(def_id);
-        }
-
-        let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
-            do_mir_borrowck(&mut root_cx, def);
-        debug_assert!(closure_requirements.is_none());
-        debug_assert!(used_mut_upvars.is_empty());
+        root_cx.do_mir_borrowck();
         root_cx.finalize()
     }
 }
@@ -153,6 +147,8 @@ struct PropagatedBorrowCheckResults<'tcx> {
     used_mut_upvars: SmallVec<[FieldIdx; 8]>,
 }
 
+type DeferredClosureRequirements<'tcx> = Vec<(LocalDefId, ty::GenericArgsRef<'tcx>, Locations)>;
+
 /// After we borrow check a closure, we are left with various
 /// requirements that we have inferred between the free regions that
 /// appear in the closure's signature or on its field types. These
@@ -291,14 +287,31 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
     }
 }
 
-/// Perform the actual borrow checking.
-///
-/// For nested bodies this should only be called through `root_cx.get_or_insert_nested`.
-#[instrument(skip(root_cx), level = "debug")]
-fn do_mir_borrowck<'tcx>(
+struct CollectRegionConstraintsResult<'tcx> {
+    infcx: BorrowckInferCtxt<'tcx>,
+    body_owned: Body<'tcx>,
+    promoted: IndexVec<Promoted, Body<'tcx>>,
+    move_data: MoveData<'tcx>,
+    borrow_set: BorrowSet<'tcx>,
+    location_table: PoloniusLocationTable,
+    location_map: Rc<DenseLocationMap>,
+    universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
+    region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
+    known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
+    constraints: MirTypeckRegionConstraints<'tcx>,
+    deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
+    deferred_opaque_type_errors: Vec<DeferredOpaqueTypeError<'tcx>>,
+    polonius_facts: Option<AllFacts<RustcFacts>>,
+    polonius_context: Option<PoloniusContext>,
+}
+
+/// Start borrow checking by collecting the region constraints for
+/// the current body. This initializes the relevant data structures
+/// and then type checks the MIR body.
+fn borrowck_collect_region_constraints<'tcx>(
     root_cx: &mut BorrowCheckRootCtxt<'tcx>,
     def: LocalDefId,
-) -> PropagatedBorrowCheckResults<'tcx> {
+) -> CollectRegionConstraintsResult<'tcx> {
     let tcx = root_cx.tcx;
     let infcx = BorrowckInferCtxt::new(tcx, def, root_cx.root_def_id());
     let (input_body, promoted) = tcx.mir_promoted(def);
@@ -334,10 +347,11 @@ fn do_mir_borrowck<'tcx>(
 
     // Run the MIR type-checker.
     let MirTypeckResults {
-        mut constraints,
+        constraints,
         universal_region_relations,
         region_bound_pairs,
         known_type_outlives_obligations,
+        deferred_closure_requirements,
         polonius_context,
     } = type_check::type_check(
         root_cx,
@@ -352,16 +366,53 @@ fn do_mir_borrowck<'tcx>(
         Rc::clone(&location_map),
     );
 
-    let opaque_type_errors = region_infer::opaque_types::handle_opaque_type_uses(
-        root_cx,
-        &infcx,
-        &body,
-        &universal_region_relations,
-        &region_bound_pairs,
-        &known_type_outlives_obligations,
-        &location_map,
-        &mut constraints,
-    );
+    CollectRegionConstraintsResult {
+        infcx,
+        body_owned,
+        promoted,
+        move_data,
+        borrow_set,
+        location_table,
+        location_map,
+        universal_region_relations,
+        region_bound_pairs,
+        known_type_outlives_obligations,
+        constraints,
+        deferred_closure_requirements,
+        deferred_opaque_type_errors: Default::default(),
+        polonius_facts,
+        polonius_context,
+    }
+}
+
+/// Using the region constraints computed by [borrowck_collect_region_constraints]
+/// and the additional constraints from [BorrowCheckRootCtxt::handle_opaque_type_uses],
+/// compute the region graph and actually check for any borrowck errors.
+fn borrowck_check_region_constraints<'tcx>(
+    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+    CollectRegionConstraintsResult {
+        infcx,
+        body_owned,
+        promoted,
+        move_data,
+        borrow_set,
+        location_table,
+        location_map,
+        universal_region_relations,
+        region_bound_pairs: _,
+        known_type_outlives_obligations: _,
+        constraints,
+        deferred_closure_requirements,
+        deferred_opaque_type_errors,
+        polonius_facts,
+        polonius_context,
+    }: CollectRegionConstraintsResult<'tcx>,
+) -> PropagatedBorrowCheckResults<'tcx> {
+    assert!(!infcx.has_opaque_types_in_storage());
+    assert!(deferred_closure_requirements.is_empty());
+    let tcx = root_cx.tcx;
+    let body = &body_owned;
+    let def = body.source.def_id().expect_local();
 
     // Compute non-lexical lifetimes using the constraints computed
     // by typechecking the MIR body.
@@ -481,7 +532,7 @@ fn do_mir_borrowck<'tcx>(
 
     // Compute and report region errors, if any.
     if nll_errors.is_empty() {
-        mbcx.report_opaque_type_errors(opaque_type_errors);
+        mbcx.report_opaque_type_errors(deferred_opaque_type_errors);
     } else {
         mbcx.report_region_errors(nll_errors);
     }
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 8608a8a3a66..5537d90e297 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -8,8 +8,8 @@ use std::str::FromStr;
 use polonius_engine::{Algorithm, AllFacts, Output};
 use rustc_data_structures::frozen::Frozen;
 use rustc_index::IndexSlice;
-use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
-use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
+use rustc_middle::mir::pretty::PrettyPrintMirOptions;
+use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_mir_dataflow::move_paths::MoveData;
@@ -68,11 +68,45 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
     // Replace all remaining regions with fresh inference variables.
     renumber::renumber_mir(infcx, body, promoted);
 
-    dump_mir(infcx.tcx, false, "renumber", &0, body, |_, _| Ok(()));
+    if let Some(dumper) = MirDumper::new(infcx.tcx, "renumber", body) {
+        dumper.dump_mir(body);
+    }
 
     universal_regions
 }
 
+/// Computes the closure requirements given the current inference state.
+///
+/// This is intended to be used by before [BorrowCheckRootCtxt::handle_opaque_type_uses]
+/// because applying member constraints may rely on closure requirements.
+/// This is frequently the case of async functions where pretty much everything
+/// happens inside of the inner async block but the opaque only gets constrained
+/// in the parent function.
+pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>(
+    infcx: &BorrowckInferCtxt<'tcx>,
+    body: &Body<'tcx>,
+    location_map: Rc<DenseLocationMap>,
+    universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
+    constraints: &MirTypeckRegionConstraints<'tcx>,
+) -> Option<ClosureRegionRequirements<'tcx>> {
+    // FIXME(#146079): we shouldn't have to clone all this stuff here.
+    // Computing the region graph should take at least some of it by reference/`Rc`.
+    let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
+        constraints.clone(),
+        &universal_region_relations,
+        infcx,
+    );
+    let mut regioncx = RegionInferenceContext::new(
+        &infcx,
+        lowered_constraints,
+        universal_region_relations.clone(),
+        location_map,
+    );
+
+    let (closure_region_requirements, _nll_errors) = regioncx.solve(infcx, body, None);
+    closure_region_requirements
+}
+
 /// Computes the (non-lexical) regions from the input MIR.
 ///
 /// This may result in errors being reported.
@@ -175,9 +209,7 @@ pub(super) fn dump_nll_mir<'tcx>(
     borrow_set: &BorrowSet<'tcx>,
 ) {
     let tcx = infcx.tcx;
-    if !dump_enabled(tcx, "nll", body.source.def_id()) {
-        return;
-    }
+    let Some(dumper) = MirDumper::new(tcx, "nll", body) else { return };
 
     // We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
     // #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
@@ -188,27 +220,24 @@ pub(super) fn dump_nll_mir<'tcx>(
             MirIncludeSpans::On | MirIncludeSpans::Nll
         ),
     };
-    dump_mir_with_options(
-        tcx,
-        false,
-        "nll",
-        &0,
-        body,
-        |pass_where, out| {
-            emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
-        },
-        options,
-    );
+
+    let extra_data = &|pass_where, out: &mut dyn std::io::Write| {
+        emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
+    };
+
+    let dumper = dumper.set_extra_data(extra_data).set_options(options);
+
+    dumper.dump_mir(body);
 
     // Also dump the region constraint graph as a graphviz file.
     let _: io::Result<()> = try {
-        let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?;
+        let mut file = dumper.create_dump_file("regioncx.all.dot", body)?;
         regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?;
     };
 
     // Also dump the region constraint SCC graph as a graphviz file.
     let _: io::Result<()> = try {
-        let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
+        let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?;
         regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?;
     };
 }
diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs
index 6b13b5ad081..62f9ae17347 100644
--- a/compiler/rustc_borrowck/src/polonius/dump.rs
+++ b/compiler/rustc_borrowck/src/polonius/dump.rs
@@ -2,9 +2,7 @@ use std::io;
 
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_index::IndexVec;
-use rustc_middle::mir::pretty::{
-    PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer,
-};
+use rustc_middle::mir::pretty::{MirDumper, PassWhere, PrettyPrintMirOptions};
 use rustc_middle::mir::{Body, Location};
 use rustc_middle::ty::{RegionVid, TyCtxt};
 use rustc_mir_dataflow::points::PointIndex;
@@ -33,22 +31,41 @@ pub(crate) fn dump_polonius_mir<'tcx>(
         return;
     }
 
-    if !dump_enabled(tcx, "polonius", body.source.def_id()) {
-        return;
-    }
+    let Some(dumper) = MirDumper::new(tcx, "polonius", body) else { return };
 
     let polonius_diagnostics =
         polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`");
 
+    let extra_data = &|pass_where, out: &mut dyn io::Write| {
+        emit_polonius_mir(
+            tcx,
+            regioncx,
+            closure_region_requirements,
+            borrow_set,
+            &polonius_diagnostics.localized_outlives_constraints,
+            pass_where,
+            out,
+        )
+    };
+    // We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
+    // mir-include-spans` on the CLI still has priority.
+    let options = PrettyPrintMirOptions {
+        include_extra_comments: matches!(
+            tcx.sess.opts.unstable_opts.mir_include_spans,
+            MirIncludeSpans::On | MirIncludeSpans::Nll
+        ),
+    };
+
+    let dumper = dumper.set_extra_data(extra_data).set_options(options);
+
     let _: io::Result<()> = try {
-        let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?;
+        let mut file = dumper.create_dump_file("html", body)?;
         emit_polonius_dump(
-            tcx,
+            &dumper,
             body,
             regioncx,
             borrow_set,
             &polonius_diagnostics.localized_outlives_constraints,
-            closure_region_requirements,
             &mut file,
         )?;
     };
@@ -61,12 +78,11 @@ pub(crate) fn dump_polonius_mir<'tcx>(
 /// - a mermaid graph of the NLL regions and the constraints between them
 /// - a mermaid graph of the NLL SCCs and the constraints between them
 fn emit_polonius_dump<'tcx>(
-    tcx: TyCtxt<'tcx>,
+    dumper: &MirDumper<'_, '_, 'tcx>,
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     borrow_set: &BorrowSet<'tcx>,
     localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
-    closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
     out: &mut dyn io::Write,
 ) -> io::Result<()> {
     // Prepare the HTML dump file prologue.
@@ -79,15 +95,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "<div>")?;
     writeln!(out, "Raw MIR dump")?;
     writeln!(out, "<pre><code>")?;
-    emit_html_mir(
-        tcx,
-        body,
-        regioncx,
-        borrow_set,
-        &localized_outlives_constraints,
-        closure_region_requirements,
-        out,
-    )?;
+    emit_html_mir(dumper, body, out)?;
     writeln!(out, "</code></pre>")?;
     writeln!(out, "</div>")?;
 
@@ -116,7 +124,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "<div>")?;
     writeln!(out, "NLL regions")?;
     writeln!(out, "<pre class='mermaid'>")?;
-    emit_mermaid_nll_regions(tcx, regioncx, out)?;
+    emit_mermaid_nll_regions(dumper.tcx(), regioncx, out)?;
     writeln!(out, "</pre>")?;
     writeln!(out, "</div>")?;
 
@@ -124,7 +132,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "<div>")?;
     writeln!(out, "NLL SCCs")?;
     writeln!(out, "<pre class='mermaid'>")?;
-    emit_mermaid_nll_sccs(tcx, regioncx, out)?;
+    emit_mermaid_nll_sccs(dumper.tcx(), regioncx, out)?;
     writeln!(out, "</pre>")?;
     writeln!(out, "</div>")?;
 
@@ -149,45 +157,14 @@ fn emit_polonius_dump<'tcx>(
 
 /// Emits the polonius MIR, as escaped HTML.
 fn emit_html_mir<'tcx>(
-    tcx: TyCtxt<'tcx>,
+    dumper: &MirDumper<'_, '_, 'tcx>,
     body: &Body<'tcx>,
-    regioncx: &RegionInferenceContext<'tcx>,
-    borrow_set: &BorrowSet<'tcx>,
-    localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
-    closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
     out: &mut dyn io::Write,
 ) -> io::Result<()> {
     // Buffer the regular MIR dump to be able to escape it.
     let mut buffer = Vec::new();
 
-    // We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
-    // mir-include-spans` on the CLI still has priority.
-    let options = PrettyPrintMirOptions {
-        include_extra_comments: matches!(
-            tcx.sess.opts.unstable_opts.mir_include_spans,
-            MirIncludeSpans::On | MirIncludeSpans::Nll
-        ),
-    };
-
-    dump_mir_to_writer(
-        tcx,
-        "polonius",
-        &0,
-        body,
-        &mut buffer,
-        |pass_where, out| {
-            emit_polonius_mir(
-                tcx,
-                regioncx,
-                closure_region_requirements,
-                borrow_set,
-                localized_outlives_constraints,
-                pass_where,
-                out,
-            )
-        },
-        options,
-    )?;
+    dumper.dump_mir_to_writer(body, &mut buffer)?;
 
     // Escape the handful of characters that need it. We don't need to be particularly efficient:
     // we're actually writing into a buffered writer already. Note that MIR dumps are valid UTF-8.
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
index bee82e17835..72615cb33b3 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
@@ -3,16 +3,16 @@ use std::rc::Rc;
 
 use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_infer::infer::outlives::env::RegionBoundPairs;
 use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, OpaqueTypeStorageEntries};
 use rustc_infer::traits::ObligationCause;
 use rustc_macros::extension;
-use rustc_middle::mir::{Body, ConstraintCategory};
+use rustc_middle::mir::{Body, ConcreteOpaqueTypes, ConstraintCategory};
 use rustc_middle::ty::{
-    self, DefiningScopeKind, FallibleTypeFolder, GenericArg, GenericArgsRef, OpaqueHiddenType,
-    OpaqueTypeKey, Region, RegionVid, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
-    TypeVisitableExt, fold_regions,
+    self, DefiningScopeKind, EarlyBinder, FallibleTypeFolder, GenericArg, GenericArgsRef,
+    OpaqueHiddenType, OpaqueTypeKey, Region, RegionVid, Ty, TyCtxt, TypeFoldable,
+    TypeSuperFoldable, TypeVisitableExt, fold_regions,
 };
 use rustc_mir_dataflow::points::DenseLocationMap;
 use rustc_span::Span;
@@ -24,13 +24,13 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
 use tracing::{debug, instrument};
 
 use super::reverse_sccs::ReverseSccGraph;
+use crate::BorrowckInferCtxt;
 use crate::consumers::RegionInferenceContext;
 use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
 use crate::type_check::canonical::fully_perform_op_raw;
 use crate::type_check::free_region_relations::UniversalRegionRelations;
 use crate::type_check::{Locations, MirTypeckRegionConstraints};
 use crate::universal_regions::{RegionClassification, UniversalRegions};
-use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt};
 
 mod member_constraints;
 mod region_ctxt;
@@ -58,78 +58,32 @@ pub(crate) enum DeferredOpaqueTypeError<'tcx> {
     },
 }
 
-/// This looks at all uses of opaque types in their defining scope inside
-/// of this function.
+/// We eagerly map all regions to NLL vars here, as we need to make sure we've
+/// introduced nll vars for all used placeholders.
 ///
-/// It first uses all defining uses to compute the actual concrete type of each
-/// opaque type definition.
-///
-/// We then apply this inferred type to actually check all uses of the opaque.
-pub(crate) fn handle_opaque_type_uses<'tcx>(
-    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+/// We need to resolve inference vars as even though we're in MIR typeck, we may still
+/// encounter inference variables, e.g. when checking user types.
+pub(crate) fn clone_and_resolve_opaque_types<'tcx>(
     infcx: &BorrowckInferCtxt<'tcx>,
-    body: &Body<'tcx>,
     universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
-    region_bound_pairs: &RegionBoundPairs<'tcx>,
-    known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
-    location_map: &Rc<DenseLocationMap>,
     constraints: &mut MirTypeckRegionConstraints<'tcx>,
-) -> Vec<DeferredOpaqueTypeError<'tcx>> {
-    let tcx = infcx.tcx;
+) -> (OpaqueTypeStorageEntries, Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)>) {
     let opaque_types = infcx.clone_opaque_types();
-    if opaque_types.is_empty() {
-        return Vec::new();
-    }
-
-    // We need to eagerly map all regions to NLL vars here, as we need to make sure we've
-    // introduced nll vars for all used placeholders.
-    //
-    // We need to resolve inference vars as even though we're in MIR typeck, we may still
-    // encounter inference variables, e.g. when checking user types.
     let opaque_types_storage_num_entries = infcx.inner.borrow_mut().opaque_types().num_entries();
     let opaque_types = opaque_types
         .into_iter()
         .map(|entry| {
-            fold_regions(tcx, infcx.resolve_vars_if_possible(entry), |r, _| {
+            fold_regions(infcx.tcx, infcx.resolve_vars_if_possible(entry), |r, _| {
                 let vid = if let ty::RePlaceholder(placeholder) = r.kind() {
                     constraints.placeholder_region(infcx, placeholder).as_var()
                 } else {
                     universal_region_relations.universal_regions.to_region_vid(r)
                 };
-                Region::new_var(tcx, vid)
+                Region::new_var(infcx.tcx, vid)
             })
         })
         .collect::<Vec<_>>();
-
-    debug!(?opaque_types);
-
-    let errors = compute_concrete_opaque_types(
-        root_cx,
-        infcx,
-        constraints,
-        universal_region_relations,
-        Rc::clone(location_map),
-        &opaque_types,
-    );
-
-    if !errors.is_empty() {
-        return errors;
-    }
-
-    let errors = apply_computed_concrete_opaque_types(
-        root_cx,
-        infcx,
-        body,
-        &universal_region_relations.universal_regions,
-        region_bound_pairs,
-        known_type_outlives_obligations,
-        constraints,
-        &opaque_types,
-    );
-
-    detect_opaque_types_added_while_handling_opaque_types(infcx, opaque_types_storage_num_entries);
-
-    errors
+    (opaque_types_storage_num_entries, opaque_types)
 }
 
 /// Maps an NLL var to a deterministically chosen equal universal region.
@@ -172,6 +126,42 @@ fn nll_var_to_universal_region<'tcx>(
     }
 }
 
+/// Collect all defining uses of opaque types inside of this typeck root. This
+/// expects the hidden type to be mapped to the definition parameters of the opaque
+/// and errors if we end up with distinct hidden types.
+fn add_concrete_opaque_type<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
+    def_id: LocalDefId,
+    hidden_ty: OpaqueHiddenType<'tcx>,
+) {
+    // Sometimes two opaque types are the same only after we remap the generic parameters
+    // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
+    // `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
+    // only know that once we convert the generic parameters to those of the opaque type.
+    if let Some(prev) = concrete_opaque_types.0.get_mut(&def_id) {
+        if prev.ty != hidden_ty.ty {
+            let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| {
+                let (Ok(e) | Err(e)) = prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit());
+                e
+            });
+            prev.ty = Ty::new_error(tcx, guar);
+        }
+        // Pick a better span if there is one.
+        // FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
+        prev.span = prev.span.substitute_dummy(hidden_ty.span);
+    } else {
+        concrete_opaque_types.0.insert(def_id, hidden_ty);
+    }
+}
+
+fn get_concrete_opaque_type<'tcx>(
+    concrete_opaque_types: &ConcreteOpaqueTypes<'tcx>,
+    def_id: LocalDefId,
+) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> {
+    concrete_opaque_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty))
+}
+
 #[derive(Debug)]
 struct DefiningUse<'tcx> {
     /// The opaque type using non NLL vars. This uses the actual
@@ -193,12 +183,12 @@ struct DefiningUse<'tcx> {
 ///
 /// It also means that this whole function is not really soundness critical as we
 /// recheck all uses of the opaques regardless.
-fn compute_concrete_opaque_types<'tcx>(
-    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+pub(crate) fn compute_concrete_opaque_types<'tcx>(
     infcx: &BorrowckInferCtxt<'tcx>,
-    constraints: &MirTypeckRegionConstraints<'tcx>,
     universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
+    constraints: &MirTypeckRegionConstraints<'tcx>,
     location_map: Rc<DenseLocationMap>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
     opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
 ) -> Vec<DeferredOpaqueTypeError<'tcx>> {
     let mut errors = Vec::new();
@@ -211,7 +201,8 @@ fn compute_concrete_opaque_types<'tcx>(
     // We start by checking each use of an opaque type during type check and
     // check whether the generic arguments of the opaque type are fully
     // universal, if so, it's a defining use.
-    let defining_uses = collect_defining_uses(root_cx, &mut rcx, opaque_types, &mut errors);
+    let defining_uses =
+        collect_defining_uses(&mut rcx, concrete_opaque_types, opaque_types, &mut errors);
 
     // We now compute and apply member constraints for all regions in the hidden
     // types of each defining use. This mutates the region values of the `rcx` which
@@ -221,14 +212,19 @@ fn compute_concrete_opaque_types<'tcx>(
     // After applying member constraints, we now check whether all member regions ended
     // up equal to one of their choice regions and compute the actual concrete type of
     // the opaque type definition. This is stored in the `root_cx`.
-    compute_concrete_types_from_defining_uses(root_cx, &rcx, &defining_uses, &mut errors);
+    compute_concrete_types_from_defining_uses(
+        &rcx,
+        concrete_opaque_types,
+        &defining_uses,
+        &mut errors,
+    );
     errors
 }
 
 #[instrument(level = "debug", skip_all, ret)]
 fn collect_defining_uses<'tcx>(
-    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
     rcx: &mut RegionCtxt<'_, 'tcx>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
     opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
     errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
 ) -> Vec<DefiningUse<'tcx>> {
@@ -248,7 +244,9 @@ fn collect_defining_uses<'tcx>(
             // with `TypingMode::Borrowck`.
             if infcx.tcx.use_typing_mode_borrowck() {
                 match err {
-                    NonDefiningUseReason::Tainted(guar) => root_cx.add_concrete_opaque_type(
+                    NonDefiningUseReason::Tainted(guar) => add_concrete_opaque_type(
+                        infcx.tcx,
+                        concrete_opaque_types,
                         opaque_type_key.def_id,
                         OpaqueHiddenType::new_error(infcx.tcx, guar),
                     ),
@@ -280,8 +278,8 @@ fn collect_defining_uses<'tcx>(
 }
 
 fn compute_concrete_types_from_defining_uses<'tcx>(
-    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
     rcx: &RegionCtxt<'_, 'tcx>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
     defining_uses: &[DefiningUse<'tcx>],
     errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
 ) {
@@ -360,7 +358,9 @@ fn compute_concrete_types_from_defining_uses<'tcx>(
                 },
             ));
         }
-        root_cx.add_concrete_opaque_type(
+        add_concrete_opaque_type(
+            tcx,
+            concrete_opaque_types,
             opaque_type_key.def_id,
             OpaqueHiddenType { span: hidden_type.span, ty },
         );
@@ -489,20 +489,20 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for ToArgRegionsFolder<'_, 'tcx> {
 ///
 /// It does this by equating the hidden type of each use with the instantiated final
 /// hidden type of the opaque.
-fn apply_computed_concrete_opaque_types<'tcx>(
-    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+pub(crate) fn apply_computed_concrete_opaque_types<'tcx>(
     infcx: &BorrowckInferCtxt<'tcx>,
     body: &Body<'tcx>,
     universal_regions: &UniversalRegions<'tcx>,
     region_bound_pairs: &RegionBoundPairs<'tcx>,
     known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
     constraints: &mut MirTypeckRegionConstraints<'tcx>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
     opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
 ) -> Vec<DeferredOpaqueTypeError<'tcx>> {
     let tcx = infcx.tcx;
     let mut errors = Vec::new();
     for &(key, hidden_type) in opaque_types {
-        let Some(expected) = root_cx.get_concrete_opaque_type(key.def_id) else {
+        let Some(expected) = get_concrete_opaque_type(concrete_opaque_types, key.def_id) else {
             assert!(tcx.use_typing_mode_borrowck(), "non-defining use in defining scope");
             errors.push(DeferredOpaqueTypeError::NonDefiningUseInDefiningScope {
                 span: hidden_type.span,
@@ -512,7 +512,12 @@ fn apply_computed_concrete_opaque_types<'tcx>(
                 hidden_type.span,
                 "non-defining use in the defining scope with no defining uses",
             );
-            root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar));
+            add_concrete_opaque_type(
+                tcx,
+                concrete_opaque_types,
+                key.def_id,
+                OpaqueHiddenType::new_error(tcx, guar),
+            );
             continue;
         };
 
@@ -552,7 +557,12 @@ fn apply_computed_concrete_opaque_types<'tcx>(
                 "equating opaque types",
             ),
         ) {
-            root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar));
+            add_concrete_opaque_type(
+                tcx,
+                concrete_opaque_types,
+                key.def_id,
+                OpaqueHiddenType::new_error(tcx, guar),
+            );
         }
     }
     errors
@@ -565,7 +575,7 @@ fn apply_computed_concrete_opaque_types<'tcx>(
 /// an ICE we can properly handle this, but we haven't encountered any such test yet.
 ///
 /// See the related comment in `FnCtxt::detect_opaque_types_added_during_writeback`.
-fn detect_opaque_types_added_while_handling_opaque_types<'tcx>(
+pub(crate) fn detect_opaque_types_added_while_handling_opaque_types<'tcx>(
     infcx: &InferCtxt<'tcx>,
     opaque_types_storage_num_entries: OpaqueTypeStorageEntries,
 ) {
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs
index f1427218cdb..eb611fa3475 100644
--- a/compiler/rustc_borrowck/src/region_infer/values.rs
+++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -37,6 +37,7 @@ pub(crate) enum RegionElement {
 
 /// Records the CFG locations where each region is live. When we initially compute liveness, we use
 /// an interval matrix storing liveness ranges for each region-vid.
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct LivenessValues {
     /// The map from locations to points.
     location_map: Rc<DenseLocationMap>,
@@ -194,6 +195,7 @@ impl LivenessValues {
 /// rustc to the internal `PlaceholderIndex` values that are used in
 /// NLL.
 #[derive(Debug, Default)]
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct PlaceholderIndices {
     indices: FxIndexSet<ty::PlaceholderRegion>,
 }
diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs
index 4e90ae391bb..cd4e9683f2d 100644
--- a/compiler/rustc_borrowck/src/root_cx.rs
+++ b/compiler/rustc_borrowck/src/root_cx.rs
@@ -1,13 +1,26 @@
+use std::mem;
+use std::rc::Rc;
+
 use rustc_abi::FieldIdx;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_hir::def_id::LocalDefId;
-use rustc_middle::bug;
-use rustc_middle::ty::{EarlyBinder, OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::mir::ConstraintCategory;
+use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::ErrorGuaranteed;
 use smallvec::SmallVec;
 
 use crate::consumers::BorrowckConsumer;
-use crate::{ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults};
+use crate::nll::compute_closure_requirements_modulo_opaques;
+use crate::region_infer::opaque_types::{
+    apply_computed_concrete_opaque_types, clone_and_resolve_opaque_types,
+    compute_concrete_opaque_types, detect_opaque_types_added_while_handling_opaque_types,
+};
+use crate::type_check::{Locations, constraint_conversion};
+use crate::{
+    ClosureRegionRequirements, CollectRegionConstraintsResult, ConcreteOpaqueTypes,
+    PropagatedBorrowCheckResults, borrowck_check_region_constraints,
+    borrowck_collect_region_constraints,
+};
 
 /// The shared context used by both the root as well as all its nested
 /// items.
@@ -15,7 +28,12 @@ pub(super) struct BorrowCheckRootCtxt<'tcx> {
     pub tcx: TyCtxt<'tcx>,
     root_def_id: LocalDefId,
     concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
-    nested_bodies: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
+    /// The region constraints computed by [borrowck_collect_region_constraints]. This uses
+    /// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before
+    /// their parents.
+    collect_region_constraints_results:
+        FxIndexMap<LocalDefId, CollectRegionConstraintsResult<'tcx>>,
+    propagated_borrowck_results: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
     tainted_by_errors: Option<ErrorGuaranteed>,
     /// This should be `None` during normal compilation. See [`crate::consumers`] for more
     /// information on how this is used.
@@ -32,7 +50,8 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
             tcx,
             root_def_id,
             concrete_opaque_types: Default::default(),
-            nested_bodies: Default::default(),
+            collect_region_constraints_results: Default::default(),
+            propagated_borrowck_results: Default::default(),
             tainted_by_errors: None,
             consumer,
         }
@@ -42,83 +61,232 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
         self.root_def_id
     }
 
-    /// Collect all defining uses of opaque types inside of this typeck root. This
-    /// expects the hidden type to be mapped to the definition parameters of the opaque
-    /// and errors if we end up with distinct hidden types.
-    pub(super) fn add_concrete_opaque_type(
-        &mut self,
-        def_id: LocalDefId,
-        hidden_ty: OpaqueHiddenType<'tcx>,
-    ) {
-        // Sometimes two opaque types are the same only after we remap the generic parameters
-        // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
-        // `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
-        // only know that once we convert the generic parameters to those of the opaque type.
-        if let Some(prev) = self.concrete_opaque_types.0.get_mut(&def_id) {
-            if prev.ty != hidden_ty.ty {
-                let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| {
-                    let (Ok(e) | Err(e)) =
-                        prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit());
-                    e
-                });
-                prev.ty = Ty::new_error(self.tcx, guar);
-            }
-            // Pick a better span if there is one.
-            // FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
-            prev.span = prev.span.substitute_dummy(hidden_ty.span);
-        } else {
-            self.concrete_opaque_types.0.insert(def_id, hidden_ty);
-        }
+    pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) {
+        self.tainted_by_errors = Some(guar);
     }
 
-    pub(super) fn get_concrete_opaque_type(
+    pub(super) fn used_mut_upvars(
         &mut self,
-        def_id: LocalDefId,
-    ) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> {
-        self.concrete_opaque_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty))
+        nested_body_def_id: LocalDefId,
+    ) -> &SmallVec<[FieldIdx; 8]> {
+        &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars
     }
 
-    pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) {
-        self.tainted_by_errors = Some(guar);
+    pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> {
+        if let Some(guar) = self.tainted_by_errors {
+            Err(guar)
+        } else {
+            Ok(self.tcx.arena.alloc(self.concrete_opaque_types))
+        }
     }
 
-    pub(super) fn get_or_insert_nested(
-        &mut self,
-        def_id: LocalDefId,
-    ) -> &PropagatedBorrowCheckResults<'tcx> {
-        debug_assert_eq!(
-            self.tcx.typeck_root_def_id(def_id.to_def_id()),
-            self.root_def_id.to_def_id()
-        );
-        if !self.nested_bodies.contains_key(&def_id) {
-            let result = super::do_mir_borrowck(self, def_id);
-            if let Some(prev) = self.nested_bodies.insert(def_id, result) {
-                bug!("unexpected previous nested body: {prev:?}");
+    fn handle_opaque_type_uses(&mut self) {
+        let mut per_body_info = Vec::new();
+        for input in self.collect_region_constraints_results.values_mut() {
+            let (num_entries, opaque_types) = clone_and_resolve_opaque_types(
+                &input.infcx,
+                &input.universal_region_relations,
+                &mut input.constraints,
+            );
+            input.deferred_opaque_type_errors = compute_concrete_opaque_types(
+                &input.infcx,
+                &input.universal_region_relations,
+                &input.constraints,
+                Rc::clone(&input.location_map),
+                &mut self.concrete_opaque_types,
+                &opaque_types,
+            );
+            per_body_info.push((num_entries, opaque_types));
+        }
+
+        for (input, (opaque_types_storage_num_entries, opaque_types)) in
+            self.collect_region_constraints_results.values_mut().zip(per_body_info)
+        {
+            if input.deferred_opaque_type_errors.is_empty() {
+                input.deferred_opaque_type_errors = apply_computed_concrete_opaque_types(
+                    &input.infcx,
+                    &input.body_owned,
+                    &input.universal_region_relations.universal_regions,
+                    &input.region_bound_pairs,
+                    &input.known_type_outlives_obligations,
+                    &mut input.constraints,
+                    &mut self.concrete_opaque_types,
+                    &opaque_types,
+                );
             }
+
+            detect_opaque_types_added_while_handling_opaque_types(
+                &input.infcx,
+                opaque_types_storage_num_entries,
+            )
         }
+    }
+
+    /// Computing defining uses of opaques may depend on the propagated region
+    /// requirements of nested bodies, while applying defining uses may introduce
+    /// additional region requirements we need to propagate.
+    ///
+    /// This results in cyclic dependency. To compute the defining uses in parent
+    /// bodies, we need the closure requirements of its nested bodies, but to check
+    /// non-defining uses in nested bodies, we may rely on the defining uses in the
+    /// parent.
+    ///
+    /// We handle this issue by applying closure requirements twice. Once using the
+    /// region constraints from before we've handled opaque types in the nested body
+    /// - which is used by the parent to handle its defining uses - and once after.
+    ///
+    /// As a performance optimization, we also eagerly finish borrowck for bodies
+    /// which don't depend on opaque types. In this case they get removed from
+    /// `collect_region_constraints_results` and the final result gets put into
+    /// `propagated_borrowck_results`.
+    fn apply_closure_requirements_modulo_opaques(&mut self) {
+        let mut closure_requirements_modulo_opaques = FxHashMap::default();
+        // We need to `mem::take` both `self.collect_region_constraints_results` and
+        // `input.deferred_closure_requirements` as we otherwise can't iterate over
+        // them while mutably using the containing struct.
+        let collect_region_constraints_results =
+            mem::take(&mut self.collect_region_constraints_results);
+        // We iterate over all bodies here, visiting nested bodies before their parent.
+        for (def_id, mut input) in collect_region_constraints_results {
+            // A body depends on opaque types if it either has any opaque type uses itself,
+            // or it has a nested body which does.
+            //
+            // If the current body does not depend on any opaque types, we eagerly compute
+            // its final result and write it into `self.propagated_borrowck_results`. This
+            // avoids having to compute its closure requirements modulo regions, as they
+            // are just the same as its final closure requirements.
+            let mut depends_on_opaques = input.infcx.has_opaque_types_in_storage();
 
-        self.nested_bodies.get(&def_id).unwrap()
+            // Iterate over all nested bodies of `input`. If that nested body depends on
+            // opaque types, we apply its closure requirements modulo opaques. Otherwise
+            // we use the closure requirements from its final borrowck result.
+            //
+            // In case we've only applied the closure requirements modulo opaques, we have
+            // to later apply its closure requirements considering opaques, so we put that
+            // nested body back into `deferred_closure_requirements`.
+            for (def_id, args, locations) in mem::take(&mut input.deferred_closure_requirements) {
+                let closure_requirements = match self.propagated_borrowck_results.get(&def_id) {
+                    None => {
+                        depends_on_opaques = true;
+                        input.deferred_closure_requirements.push((def_id, args, locations));
+                        &closure_requirements_modulo_opaques[&def_id]
+                    }
+                    Some(result) => &result.closure_requirements,
+                };
+
+                Self::apply_closure_requirements(
+                    &mut input,
+                    closure_requirements,
+                    def_id,
+                    args,
+                    locations,
+                );
+            }
+
+            // In case the current body does depend on opaques and is a nested body,
+            // we need to compute its closure requirements modulo opaques so that
+            // we're able to use it when visiting its parent later in this function.
+            //
+            // If the current body does not depend on opaque types, we finish borrowck
+            // and write its result into `propagated_borrowck_results`.
+            if depends_on_opaques {
+                if def_id != self.root_def_id {
+                    let req = Self::compute_closure_requirements_modulo_opaques(&input);
+                    closure_requirements_modulo_opaques.insert(def_id, req);
+                }
+                self.collect_region_constraints_results.insert(def_id, input);
+            } else {
+                assert!(input.deferred_closure_requirements.is_empty());
+                let result = borrowck_check_region_constraints(self, input);
+                self.propagated_borrowck_results.insert(def_id, result);
+            }
+        }
     }
 
-    pub(super) fn closure_requirements(
-        &mut self,
-        nested_body_def_id: LocalDefId,
-    ) -> &Option<ClosureRegionRequirements<'tcx>> {
-        &self.get_or_insert_nested(nested_body_def_id).closure_requirements
+    fn compute_closure_requirements_modulo_opaques(
+        input: &CollectRegionConstraintsResult<'tcx>,
+    ) -> Option<ClosureRegionRequirements<'tcx>> {
+        compute_closure_requirements_modulo_opaques(
+            &input.infcx,
+            &input.body_owned,
+            Rc::clone(&input.location_map),
+            &input.universal_region_relations,
+            &input.constraints,
+        )
     }
 
-    pub(super) fn used_mut_upvars(
-        &mut self,
-        nested_body_def_id: LocalDefId,
-    ) -> &SmallVec<[FieldIdx; 8]> {
-        &self.get_or_insert_nested(nested_body_def_id).used_mut_upvars
+    fn apply_closure_requirements(
+        input: &mut CollectRegionConstraintsResult<'tcx>,
+        closure_requirements: &Option<ClosureRegionRequirements<'tcx>>,
+        closure_def_id: LocalDefId,
+        args: ty::GenericArgsRef<'tcx>,
+        locations: Locations,
+    ) {
+        if let Some(closure_requirements) = closure_requirements {
+            constraint_conversion::ConstraintConversion::new(
+                &input.infcx,
+                &input.universal_region_relations.universal_regions,
+                &input.region_bound_pairs,
+                &input.known_type_outlives_obligations,
+                locations,
+                input.body_owned.span,      // irrelevant; will be overridden.
+                ConstraintCategory::Boring, // same as above.
+                &mut input.constraints,
+            )
+            .apply_closure_requirements(closure_requirements, closure_def_id, args);
+        }
     }
 
-    pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> {
-        if let Some(guar) = self.tainted_by_errors {
-            Err(guar)
-        } else {
-            Ok(self.tcx.arena.alloc(self.concrete_opaque_types))
+    pub(super) fn do_mir_borrowck(&mut self) {
+        // The list of all bodies we need to borrowck. This first looks at
+        // nested bodies, and then their parents. This means accessing e.g.
+        // `used_mut_upvars` for a closure can assume that we've already
+        // checked that closure.
+        let all_bodies = self
+            .tcx
+            .nested_bodies_within(self.root_def_id)
+            .iter()
+            .chain(std::iter::once(self.root_def_id));
+        for def_id in all_bodies {
+            let result = borrowck_collect_region_constraints(self, def_id);
+            self.collect_region_constraints_results.insert(def_id, result);
+        }
+
+        // We now apply the closure requirements of nested bodies modulo
+        // regions. In case a body does not depend on opaque types, we
+        // eagerly check its region constraints and use the final closure
+        // requirements.
+        //
+        // We eagerly finish borrowck for bodies which don't depend on
+        // opaques.
+        self.apply_closure_requirements_modulo_opaques();
+
+        // We handle opaque type uses for all bodies together.
+        self.handle_opaque_type_uses();
+
+        // Now walk over all bodies which depend on opaque types and finish borrowck.
+        //
+        // We first apply the final closure requirements from nested bodies which also
+        // depend on opaque types and then finish borrow checking the parent. Bodies
+        // which don't depend on opaques have already been fully borrowchecked in
+        // `apply_closure_requirements_modulo_opaques` as an optimization.
+        for (def_id, mut input) in mem::take(&mut self.collect_region_constraints_results) {
+            for (def_id, args, locations) in mem::take(&mut input.deferred_closure_requirements) {
+                // We visit nested bodies before their parent, so we're already
+                // done with nested bodies at this point.
+                let closure_requirements =
+                    &self.propagated_borrowck_results[&def_id].closure_requirements;
+                Self::apply_closure_requirements(
+                    &mut input,
+                    closure_requirements,
+                    def_id,
+                    args,
+                    locations,
+                );
+            }
+
+            let result = borrowck_check_region_constraints(self, input);
+            self.propagated_borrowck_results.insert(def_id, result);
         }
     }
 }
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index 7bf2df91470..d27a73535ba 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -19,6 +19,7 @@ use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conver
 use crate::universal_regions::UniversalRegions;
 
 #[derive(Debug)]
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct UniversalRegionRelations<'tcx> {
     pub(crate) universal_regions: UniversalRegions<'tcx>,
 
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 0d363935f14..02be78f90b0 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -49,7 +49,7 @@ use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderI
 use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst};
 use crate::type_check::free_region_relations::{CreateResult, UniversalRegionRelations};
 use crate::universal_regions::{DefiningTy, UniversalRegions};
-use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, path_utils};
+use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, DeferredClosureRequirements, path_utils};
 
 macro_rules! span_mirbug {
     ($context:expr, $elem:expr, $($message:tt)*) => ({
@@ -67,7 +67,7 @@ macro_rules! span_mirbug {
 }
 
 pub(crate) mod canonical;
-mod constraint_conversion;
+pub(crate) mod constraint_conversion;
 pub(crate) mod free_region_relations;
 mod input_output;
 pub(crate) mod liveness;
@@ -142,6 +142,7 @@ pub(crate) fn type_check<'tcx>(
         None
     };
 
+    let mut deferred_closure_requirements = Default::default();
     let mut typeck = TypeChecker {
         root_cx,
         infcx,
@@ -157,6 +158,7 @@ pub(crate) fn type_check<'tcx>(
         polonius_facts,
         borrow_set,
         constraints: &mut constraints,
+        deferred_closure_requirements: &mut deferred_closure_requirements,
         polonius_liveness,
     };
 
@@ -191,6 +193,7 @@ pub(crate) fn type_check<'tcx>(
         universal_region_relations,
         region_bound_pairs,
         known_type_outlives_obligations,
+        deferred_closure_requirements,
         polonius_context,
     }
 }
@@ -230,6 +233,7 @@ struct TypeChecker<'a, 'tcx> {
     polonius_facts: &'a mut Option<PoloniusFacts>,
     borrow_set: &'a BorrowSet<'tcx>,
     constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
+    deferred_closure_requirements: &'a mut DeferredClosureRequirements<'tcx>,
     /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints.
     polonius_liveness: Option<PoloniusLivenessContext>,
 }
@@ -241,11 +245,13 @@ pub(crate) struct MirTypeckResults<'tcx> {
     pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
     pub(crate) region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
     pub(crate) known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
+    pub(crate) deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
     pub(crate) polonius_context: Option<PoloniusContext>,
 }
 
 /// A collection of region constraints that must be satisfied for the
 /// program to be considered well-typed.
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct MirTypeckRegionConstraints<'tcx> {
     /// Maps from a `ty::Placeholder` to the corresponding
     /// `PlaceholderIndex` bit that we will use for it.
@@ -2470,21 +2476,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         locations: Locations,
     ) -> ty::InstantiatedPredicates<'tcx> {
         let root_def_id = self.root_cx.root_def_id();
-        if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) {
-            constraint_conversion::ConstraintConversion::new(
-                self.infcx,
-                self.universal_regions,
-                self.region_bound_pairs,
-                self.known_type_outlives_obligations,
-                locations,
-                self.body.span,             // irrelevant; will be overridden.
-                ConstraintCategory::Boring, // same as above.
-                self.constraints,
-            )
-            .apply_closure_requirements(closure_requirements, def_id, args);
-        }
+        // We will have to handle propagated closure requirements for this closure,
+        // but need to defer this until the nested body has been fully borrow checked.
+        self.deferred_closure_requirements.push((def_id, args, locations));
 
-        // Now equate closure args to regions inherited from `root_def_id`. Fixes #98589.
+        // Equate closure args to regions inherited from `root_def_id`. Fixes #98589.
         let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, root_def_id);
 
         let parent_args = match tcx.def_kind(def_id) {
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index 296a2735533..64a7b408434 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -40,6 +40,7 @@ use crate::BorrowckInferCtxt;
 use crate::renumber::RegionCtxt;
 
 #[derive(Debug)]
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct UniversalRegions<'tcx> {
     indices: UniversalRegionIndices<'tcx>,
 
@@ -200,6 +201,7 @@ impl<'tcx> DefiningTy<'tcx> {
 }
 
 #[derive(Debug)]
+#[derive(Clone)] // FIXME(#146079)
 struct UniversalRegionIndices<'tcx> {
     /// For those regions that may appear in the parameter environment
     /// ('static and early-bound regions), we maintain a map from the
diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml
index 9ca44d67150..e56b9e641a1 100644
--- a/compiler/rustc_builtin_macros/Cargo.toml
+++ b/compiler/rustc_builtin_macros/Cargo.toml
@@ -30,6 +30,6 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index bc0a0f034b2..3a28dd7e73c 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -44,9 +44,8 @@ pub(crate) fn codegen_fn<'tcx>(
     let _mir_guard = crate::PrintOnPanic(|| {
         let mut buf = Vec::new();
         with_no_trimmed_paths!({
-            use rustc_middle::mir::pretty;
-            let options = pretty::PrettyPrintMirOptions::from_cli(tcx);
-            pretty::write_mir_fn(tcx, mir, &mut |_, _| Ok(()), &mut buf, options).unwrap();
+            let writer = pretty::MirWriter::new(tcx);
+            writer.write_mir_fn(mir, &mut buf).unwrap();
         });
         String::from_utf8_lossy(&buf).into_owned()
     });
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index b04310f3d54..2d11628250c 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -8,15 +8,15 @@ test = false
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
+bitflags = "2.4.1"
 # To avoid duplicate dependencies, this should match the version of gimli used
 # by `rustc_codegen_ssa` via its `thorin-dwp` dependency.
 gimli = "0.31"
-itertools.workspace = true
-libc.workspace = true
-measureme.workspace = true
+itertools = "0.12"
+libc = "0.2"
+measureme = "12.0.1"
 object = { version = "0.37.0", default-features = false, features = ["std", "read"] }
-rustc-demangle.workspace = true
+rustc-demangle = "0.1.21"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
@@ -38,9 +38,9 @@ rustc_span = { path = "../rustc_span" }
 rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
 rustc_target = { path = "../rustc_target" }
 serde = { version = "1", features = ["derive"] }
-serde_json.workspace = true
+serde_json = "1"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index 7a340ae83f3..f9dc48e3aba 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -26,6 +26,7 @@ static LLVM_OBJECT_READER: ObjectReader = ObjectReader {
     get_symbols: get_llvm_object_symbols,
     is_64_bit_object_file: llvm_is_64_bit_object_file,
     is_ec_object_file: llvm_is_ec_object_file,
+    is_any_arm64_coff: llvm_is_any_arm64_coff,
     get_xcoff_member_alignment: DEFAULT_OBJECT_READER.get_xcoff_member_alignment,
 };
 
@@ -95,3 +96,7 @@ fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool {
 fn llvm_is_ec_object_file(buf: &[u8]) -> bool {
     unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) }
 }
+
+fn llvm_is_any_arm64_coff(buf: &[u8]) -> bool {
+    unsafe { llvm::LLVMRustIsAnyArm64Coff(buf.as_ptr(), buf.len()) }
+}
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index ba590851dbd..b66fc157b3c 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -2686,6 +2686,8 @@ unsafe extern "C" {
 
     pub(crate) fn LLVMRustIsECObject(buf_ptr: *const u8, buf_len: usize) -> bool;
 
+    pub(crate) fn LLVMRustIsAnyArm64Coff(buf_ptr: *const u8, buf_len: usize) -> bool;
+
     pub(crate) fn LLVMRustSetNoSanitizeAddress(Global: &Value);
     pub(crate) fn LLVMRustSetNoSanitizeHWAddress(Global: &Value);
 }
diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs
index ce079f3cb0a..7eb5d302058 100644
--- a/compiler/rustc_codegen_llvm/src/va_arg.rs
+++ b/compiler/rustc_codegen_llvm/src/va_arg.rs
@@ -28,9 +28,12 @@ fn round_pointer_up_to_alignment<'ll>(
     align: Align,
     ptr_ty: &'ll Type,
 ) -> &'ll Value {
-    let mut ptr_as_int = bx.ptrtoint(addr, bx.cx().type_isize());
-    ptr_as_int = round_up_to_alignment(bx, ptr_as_int, align);
-    bx.inttoptr(ptr_as_int, ptr_ty)
+    let ptr = bx.inbounds_ptradd(addr, bx.const_i32(align.bytes() as i32 - 1));
+    bx.call_intrinsic(
+        "llvm.ptrmask",
+        &[ptr_ty, bx.type_i32()],
+        &[ptr, bx.const_int(bx.isize_ty, -(align.bytes() as isize) as i64)],
+    )
 }
 
 fn emit_direct_ptr_va_arg<'ll, 'tcx>(
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 57e1fee2c0a..2dfbc581643 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -5,13 +5,13 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-ar_archive_writer = "0.4.2"
-bitflags.workspace = true
+ar_archive_writer = "0.5"
+bitflags = "2.4.1"
 bstr = "1.11.3"
 # `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version
 # per crate", so if you change this, you need to also change it in `rustc_llvm`.
 cc = "=1.2.16"
-itertools.workspace = true
+itertools = "0.12"
 pathdiff = "0.2.0"
 regex = "1.4"
 rustc_abi = { path = "../rustc_abi" }
@@ -37,18 +37,18 @@ rustc_span = { path = "../rustc_span" }
 rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
-serde_json.workspace = true
+serde_json = "1.0.59"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-tempfile.workspace = true
-thin-vec.workspace = true
+tempfile = "3.2"
+thin-vec = "0.2.12"
 thorin-dwp = "0.9"
-tracing.workspace = true
+tracing = "0.1"
 wasm-encoder = "0.219"
 # tidy-alphabetical-end
 
 [target.'cfg(unix)'.dependencies]
 # tidy-alphabetical-start
-libc.workspace = true
+libc = "0.2.50"
 # tidy-alphabetical-end
 
 [dependencies.object]
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index 84a56f6b0b5..cfd8ceac3a6 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -40,16 +40,18 @@ pub struct ImportLibraryItem {
     pub is_data: bool,
 }
 
-impl From<ImportLibraryItem> for COFFShortExport {
-    fn from(item: ImportLibraryItem) -> Self {
+impl ImportLibraryItem {
+    fn into_coff_short_export(self, sess: &Session) -> COFFShortExport {
+        let import_name = (sess.target.arch == "arm64ec").then(|| self.name.clone());
         COFFShortExport {
-            name: item.name,
+            name: self.name,
             ext_name: None,
-            symbol_name: item.symbol_name,
-            alias_target: None,
-            ordinal: item.ordinal.unwrap_or(0),
-            noname: item.ordinal.is_some(),
-            data: item.is_data,
+            symbol_name: self.symbol_name,
+            import_name,
+            export_as: None,
+            ordinal: self.ordinal.unwrap_or(0),
+            noname: self.ordinal.is_some(),
+            data: self.is_data,
             private: false,
             constant: false,
         }
@@ -113,7 +115,8 @@ pub trait ArchiveBuilderBuilder {
                     .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }),
             };
 
-            let exports = items.into_iter().map(Into::into).collect::<Vec<_>>();
+            let exports =
+                items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::<Vec<_>>();
             let machine = match &*sess.target.arch {
                 "x86_64" => MachineTypes::AMD64,
                 "x86" => MachineTypes::I386,
@@ -134,6 +137,7 @@ pub trait ArchiveBuilderBuilder {
                 // when linking a rust staticlib using `/WHOLEARCHIVE`.
                 // See #129020
                 true,
+                &[],
             ) {
                 sess.dcx()
                     .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() });
@@ -527,7 +531,7 @@ impl<'a> ArArchiveBuilder<'a> {
             &entries,
             archive_kind,
             false,
-            /* is_ec = */ self.sess.target.arch == "arm64ec",
+            /* is_ec = */ Some(self.sess.target.arch == "arm64ec"),
         )?;
         archive_tmpfile.flush()?;
         drop(archive_tmpfile);
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index c3dc3e42b83..5f6976f5d00 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -519,6 +519,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             match self.locals[mir::Local::from_usize(1 + va_list_arg_idx)] {
                 LocalRef::Place(va_list) => {
                     bx.va_end(va_list.val.llval);
+
+                    // Explicitly end the lifetime of the `va_list`, this matters for LLVM.
+                    bx.lifetime_end(va_list.val.llval, va_list.layout.size);
                 }
                 _ => bug!("C-variadic function must have a `VaList` place"),
             }
diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml
index acf19b0f2fc..51dcee8d882 100644
--- a/compiler/rustc_const_eval/Cargo.toml
+++ b/compiler/rustc_const_eval/Cargo.toml
@@ -5,9 +5,9 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-either.workspace = true
+either = "1"
 rustc_abi = { path = "../rustc_abi" }
-rustc_apfloat.workspace = true
+rustc_apfloat = "0.2.0"
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
@@ -22,5 +22,5 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index 852fc11350b..c8296e05f6b 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -6,15 +6,15 @@ edition = "2024"
 [dependencies]
 # tidy-alphabetical-start
 arrayvec = { version = "0.7", default-features = false }
-bitflags.workspace = true
-either.workspace = true
+bitflags = "2.4.1"
+either = "1.0"
 elsa = "1.11.0"
 ena = "0.14.3"
-indexmap.workspace = true
+indexmap = "2.4.0"
 jobserver_crate = { version = "0.1.28", package = "jobserver" }
-measureme.workspace = true
+measureme = "12.0.1"
 parking_lot = "0.12"
-rustc-hash.workspace = true
+rustc-hash = "2.0.0"
 rustc-stable-hash = { version = "0.1.0", features = ["nightly"] }
 rustc_arena = { path = "../rustc_arena" }
 rustc_graphviz = { path = "../rustc_graphviz" }
@@ -25,9 +25,9 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_thread_pool = { path = "../rustc_thread_pool" }
 smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] }
 stacker = "0.1.17"
-tempfile.workspace = true
-thin-vec.workspace = true
-tracing.workspace = true
+tempfile = "3.2"
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [dependencies.hashbrown]
@@ -47,7 +47,7 @@ features = [
 
 [target.'cfg(unix)'.dependencies]
 # tidy-alphabetical-start
-libc.workspace = true
+libc = "0.2"
 # tidy-alphabetical-end
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
diff --git a/compiler/rustc_data_structures/src/frozen.rs b/compiler/rustc_data_structures/src/frozen.rs
index 73190574667..4a60d17de2a 100644
--- a/compiler/rustc_data_structures/src/frozen.rs
+++ b/compiler/rustc_data_structures/src/frozen.rs
@@ -46,7 +46,7 @@
 //!    Frozen::freeze(new_bar)`).
 
 /// An owned immutable value.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Frozen<T>(T);
 
 impl<T> Frozen<T> {
diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml
index 196c8aa3547..ae1dbd2cf51 100644
--- a/compiler/rustc_driver_impl/Cargo.toml
+++ b/compiler/rustc_driver_impl/Cargo.toml
@@ -49,14 +49,14 @@ rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_ty_utils = { path = "../rustc_ty_utils" }
-serde_json.workspace = true
+serde_json = "1.0.59"
 shlex = "1.0"
-tracing.workspace = true
+tracing = { version = "0.1.35" }
 # tidy-alphabetical-end
 
 [target.'cfg(all(unix, any(target_env = "gnu", target_os = "macos")))'.dependencies]
 # tidy-alphabetical-start
-libc.workspace = true
+libc = "0.2"
 # tidy-alphabetical-end
 
 [target.'cfg(windows)'.dependencies.windows]
diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml
index 7d2dc20e136..db22e065907 100644
--- a/compiler/rustc_error_messages/Cargo.toml
+++ b/compiler/rustc_error_messages/Cargo.toml
@@ -17,6 +17,6 @@ rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
-tracing.workspace = true
+tracing = "0.1"
 unic-langid = { version = "0.9.0", features = ["macros"] }
 # tidy-alphabetical-end
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index 67a17ce88fa..7912b8e6098 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -21,10 +21,10 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 serde = { version = "1.0.125", features = ["derive"] }
-serde_json.workspace = true
+serde_json = "1.0.59"
 termcolor = "1.2.0"
 termize = "0.2"
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [target.'cfg(windows)'.dependencies.windows]
diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml
index 9bb7143af51..f897833d85c 100644
--- a/compiler/rustc_expand/Cargo.toml
+++ b/compiler/rustc_expand/Cargo.toml
@@ -29,6 +29,6 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml
index b58f2375852..a4746ac455c 100644
--- a/compiler/rustc_feature/Cargo.toml
+++ b/compiler/rustc_feature/Cargo.toml
@@ -9,5 +9,5 @@ rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_span = { path = "../rustc_span" }
 serde = { version = "1.0.125", features = ["derive"] }
-serde_json.workspace = true
+serde_json = "1.0.59"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 9fe55216f93..6af4cfb0e56 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -203,6 +203,9 @@ declare_features! (
     (accepted, expr_fragment_specifier_2024, "1.83.0", Some(123742)),
     /// Allows arbitrary expressions in key-value attributes at parse time.
     (accepted, extended_key_value_attributes, "1.54.0", Some(78835)),
+    /// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
+    /// for functions with varargs.
+    (accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)),
     /// Allows resolving absolute paths as paths from other crates.
     (accepted, extern_absolute_paths, "1.30.0", Some(44660)),
     /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude.
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 92b435b4b01..573e9aab055 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -492,9 +492,6 @@ declare_features! (
     (incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
     /// Allows using `#[export_stable]` which indicates that an item is exportable.
     (incomplete, export_stable, "1.88.0", Some(139939)),
-    /// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
-    /// for functions with varargs.
-    (unstable, extended_varargs_abi_support, "1.65.0", Some(100189)),
     /// Allows using `system` as a calling convention with varargs.
     (unstable, extern_system_varargs, "1.86.0", Some(136946)),
     /// Allows defining `extern type`s.
diff --git a/compiler/rustc_fluent_macro/Cargo.toml b/compiler/rustc_fluent_macro/Cargo.toml
index 60afd9aca87..d7ef4280aef 100644
--- a/compiler/rustc_fluent_macro/Cargo.toml
+++ b/compiler/rustc_fluent_macro/Cargo.toml
@@ -11,8 +11,8 @@ proc-macro = true
 annotate-snippets = "0.11"
 fluent-bundle = "0.16"
 fluent-syntax = "0.12"
-proc-macro2.workspace = true
-quote.workspace = true
+proc-macro2 = "1"
+quote = "1"
 syn = { version = "2", features = ["full"] }
 unic-langid = { version = "0.9.0", features = ["macros"] }
 # tidy-alphabetical-end
diff --git a/compiler/rustc_fs_util/Cargo.toml b/compiler/rustc_fs_util/Cargo.toml
index 37970e81fea..90a6acade8b 100644
--- a/compiler/rustc_fs_util/Cargo.toml
+++ b/compiler/rustc_fs_util/Cargo.toml
@@ -5,5 +5,5 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-tempfile.workspace = true
+tempfile = "3.7.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml
index e74fcfe7455..1008a3e787d 100644
--- a/compiler/rustc_hir/Cargo.toml
+++ b/compiler/rustc_hir/Cargo.toml
@@ -5,8 +5,8 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
-odht.workspace = true
+bitflags = "2.9.1"
+odht = { version = "0.3.1", features = ["nightly"] }
 rustc_abi = { path = "../rustc_abi" }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
@@ -21,6 +21,6 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index e397c286de2..e3c27c73638 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1884,8 +1884,8 @@ pub enum PatKind<'hir> {
     Binding(BindingMode, HirId, Ident, Option<&'hir Pat<'hir>>),
 
     /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`).
-    /// The `bool` is `true` in the presence of a `..`.
-    Struct(QPath<'hir>, &'hir [PatField<'hir>], bool),
+    /// The `Option` contains the span of a possible `..`.
+    Struct(QPath<'hir>, &'hir [PatField<'hir>], Option<Span>),
 
     /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
     /// If the `..` pattern fragment is present, then `DotDotPos` denotes its position.
@@ -4979,8 +4979,8 @@ mod size_asserts {
     static_assert_size!(ItemKind<'_>, 64);
     static_assert_size!(LetStmt<'_>, 72);
     static_assert_size!(Param<'_>, 32);
-    static_assert_size!(Pat<'_>, 72);
-    static_assert_size!(PatKind<'_>, 48);
+    static_assert_size!(Pat<'_>, 80);
+    static_assert_size!(PatKind<'_>, 56);
     static_assert_size!(Path<'_>, 40);
     static_assert_size!(PathSegment<'_>, 48);
     static_assert_size!(QPath<'_>, 24);
diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
index bb86beb2251..e5017794d8f 100644
--- a/compiler/rustc_hir_analysis/Cargo.toml
+++ b/compiler/rustc_hir_analysis/Cargo.toml
@@ -9,7 +9,7 @@ doctest = false
 
 [dependencies]
 # tidy-alphabetical-start
-itertools.workspace = true
+itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
@@ -29,5 +29,5 @@ rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index eccb88a938f..08b344638dd 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -978,7 +978,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
                         tcx.ensure_ok().fn_sig(def_id);
                         let item = tcx.hir_foreign_item(item);
                         let hir::ForeignItemKind::Fn(sig, ..) = item.kind else { bug!() };
-                        require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span);
+                        check_c_variadic_abi(tcx, sig.decl, abi, item.span);
                     }
                     DefKind::Static { .. } => {
                         tcx.ensure_ok().codegen_fn_attrs(def_id);
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 2e4b151d4dc..b5c0ca4727c 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -98,7 +98,7 @@ use tracing::debug;
 
 use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys;
 use self::region::region_scope_tree;
-use crate::{errors, require_c_abi_if_c_variadic};
+use crate::{check_c_variadic_abi, errors};
 
 /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]
 pub(super) fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index c7b984d9b25..c5e079fe89a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -52,11 +52,11 @@ use rustc_trait_selection::traits::{self, FulfillmentError};
 use tracing::{debug, instrument};
 
 use crate::check::check_abi;
+use crate::check_c_variadic_abi;
 use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation};
 use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
 use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
 use crate::middle::resolve_bound_vars as rbv;
-use crate::require_c_abi_if_c_variadic;
 
 /// A path segment that is semantically allowed to have generic arguments.
 #[derive(Debug)]
@@ -2412,7 +2412,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.lower_ty(t)))
             }
             hir::TyKind::FnPtr(bf) => {
-                require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, hir_ty.span);
+                check_c_variadic_abi(tcx, bf.decl, bf.abi, hir_ty.span);
 
                 Ty::new_fn_ptr(
                     tcx,
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 44a5ceed469..2562ab7542a 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -90,7 +90,7 @@ mod outlives;
 mod variance;
 
 pub use errors::NoVariantNamed;
-use rustc_abi::ExternAbi;
+use rustc_abi::{CVariadicStatus, ExternAbi};
 use rustc_hir::def::DefKind;
 use rustc_hir::lints::DelayedLint;
 use rustc_hir::{self as hir};
@@ -99,7 +99,6 @@ use rustc_middle::mir::interpret::GlobalId;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, Const, Ty, TyCtxt};
 use rustc_session::parse::feature_err;
-use rustc_span::symbol::sym;
 use rustc_span::{ErrorGuaranteed, Span};
 use rustc_trait_selection::traits;
 
@@ -108,46 +107,34 @@ use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer};
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
-fn require_c_abi_if_c_variadic(
-    tcx: TyCtxt<'_>,
-    decl: &hir::FnDecl<'_>,
-    abi: ExternAbi,
-    span: Span,
-) {
-    // ABIs which can stably use varargs
-    if !decl.c_variadic || matches!(abi, ExternAbi::C { .. } | ExternAbi::Cdecl { .. }) {
+fn check_c_variadic_abi(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: ExternAbi, span: Span) {
+    if !decl.c_variadic {
+        // Not even a variadic function.
         return;
     }
 
-    // ABIs with feature-gated stability
-    let extended_abi_support = tcx.features().extended_varargs_abi_support();
-    let extern_system_varargs = tcx.features().extern_system_varargs();
-
-    // If the feature gate has been enabled, we can stop here
-    if extern_system_varargs && let ExternAbi::System { .. } = abi {
-        return;
-    };
-    if extended_abi_support && abi.supports_varargs() {
-        return;
-    };
-
-    // Looks like we need to pick an error to emit.
-    // Is there any feature which we could have enabled to make this work?
-    let unstable_explain =
-        format!("C-variadic functions with the {abi} calling convention are unstable");
-    match abi {
-        ExternAbi::System { .. } => {
-            feature_err(&tcx.sess, sym::extern_system_varargs, span, unstable_explain)
+    match abi.supports_c_variadic() {
+        CVariadicStatus::Stable => {}
+        CVariadicStatus::NotSupported => {
+            tcx.dcx()
+                .create_err(errors::VariadicFunctionCompatibleConvention {
+                    span,
+                    convention: &format!("{abi}"),
+                })
+                .emit();
         }
-        abi if abi.supports_varargs() => {
-            feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, unstable_explain)
+        CVariadicStatus::Unstable { feature } => {
+            if !tcx.features().enabled(feature) {
+                feature_err(
+                    &tcx.sess,
+                    feature,
+                    span,
+                    format!("C-variadic functions with the {abi} calling convention are unstable"),
+                )
+                .emit();
+            }
         }
-        _ => tcx.dcx().create_err(errors::VariadicFunctionCompatibleConvention {
-            span,
-            convention: &format!("{abi}"),
-        }),
     }
-    .emit();
 }
 
 /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index be5859b57c5..52b29e05dcb 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1958,12 +1958,12 @@ impl<'a> State<'a> {
                 self.print_qpath(qpath, true);
                 self.nbsp();
                 self.word("{");
-                let empty = fields.is_empty() && !etc;
+                let empty = fields.is_empty() && etc.is_none();
                 if !empty {
                     self.space();
                 }
                 self.commasep_cmnt(Consistent, fields, |s, f| s.print_patfield(f), |f| f.pat.span);
-                if etc {
+                if etc.is_some() {
                     if !fields.is_empty() {
                         self.word_space(",");
                     }
diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml
index 5af0c6134ca..f00125c3e09 100644
--- a/compiler/rustc_hir_typeck/Cargo.toml
+++ b/compiler/rustc_hir_typeck/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools.workspace = true
+itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr_parsing = { path = "../rustc_attr_parsing" }
@@ -25,5 +25,5 @@ rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index e37ea031672..652644ad78c 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -117,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Err(Ambiguity(..)) => true,
             Err(PrivateMatch(..)) => false,
             Err(IllegalSizedBound { .. }) => true,
-            Err(BadReturnType) => false,
+            Err(BadReturnType) => true,
             Err(ErrorReported(_)) => false,
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index 87a7cd62ae5..11defc3aa03 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -962,13 +962,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
         let cause = self.cause(
             span,
-            ObligationCauseCode::BinOp {
-                lhs_hir_id: lhs_expr.hir_id,
-                rhs_hir_id: opt_rhs_expr.map(|expr| expr.hir_id),
-                rhs_span: opt_rhs_expr.map(|expr| expr.span),
-                rhs_is_lit: opt_rhs_expr
-                    .is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
-                output_ty: expected.only_has_type(self),
+            match opt_rhs_expr {
+                Some(rhs) => ObligationCauseCode::BinOp {
+                    lhs_hir_id: lhs_expr.hir_id,
+                    rhs_hir_id: rhs.hir_id,
+                    rhs_span: rhs.span,
+                    rhs_is_lit: matches!(rhs.kind, hir::ExprKind::Lit(_)),
+                    output_ty: expected.only_has_type(self),
+                },
+                None => ObligationCauseCode::UnOp { hir_id: lhs_expr.hir_id },
             },
         );
 
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 7dc736e5e6b..f735c0a4160 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -605,7 +605,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             },
             PatKind::Struct(_, fields, has_rest_pat) => match opt_path_res.unwrap() {
                 Ok(ResolvedPat { ty, kind: ResolvedPatKind::Struct { variant } }) => self
-                    .check_pat_struct(pat, fields, has_rest_pat, ty, variant, expected, pat_info),
+                    .check_pat_struct(
+                        pat,
+                        fields,
+                        has_rest_pat.is_some(),
+                        ty,
+                        variant,
+                        expected,
+                        pat_info,
+                    ),
                 Err(guar) => {
                     let ty_err = Ty::new_error(self.tcx, guar);
                     for field in fields {
@@ -2428,7 +2436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let len = unmentioned_fields.len();
         let (prefix, postfix, sp) = match fields {
             [] => match &pat.kind {
-                PatKind::Struct(path, [], false) => {
+                PatKind::Struct(path, [], None) => {
                     (" { ", " }", path.span().shrink_to_hi().until(pat.span.shrink_to_hi()))
                 }
                 _ => return err,
diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml
index 8d7f2eb8414..db0a5841887 100644
--- a/compiler/rustc_incremental/Cargo.toml
+++ b/compiler/rustc_incremental/Cargo.toml
@@ -19,6 +19,6 @@ rustc_middle = { path = "../rustc_middle" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_index_macros/Cargo.toml b/compiler/rustc_index_macros/Cargo.toml
index 8593bde2615..34f3109a526 100644
--- a/compiler/rustc_index_macros/Cargo.toml
+++ b/compiler/rustc_index_macros/Cargo.toml
@@ -8,8 +8,8 @@ proc-macro = true
 
 [dependencies]
 # tidy-alphabetical-start
-proc-macro2.workspace = true
-quote.workspace = true
+proc-macro2 = "1"
+quote = "1"
 syn = { version = "2.0.9", features = ["full", "extra-traits"] }
 # tidy-alphabetical-end
 
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index 6d97fa6af1f..08c03614884 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -18,6 +18,6 @@ rustc_middle = { path = "../rustc_middle" }
 rustc_span = { path = "../rustc_span" }
 rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index d1507f08c06..d105d24bed7 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -989,6 +989,10 @@ impl<'tcx> InferCtxt<'tcx> {
         storage.var_infos.clone()
     }
 
+    pub fn has_opaque_types_in_storage(&self) -> bool {
+        !self.inner.borrow().opaque_type_storage.is_empty()
+    }
+
     #[instrument(level = "debug", skip(self), ret)]
     pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)> {
         self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect()
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index 40152e78f8a..473ac5e0cea 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -46,7 +46,7 @@ rustc_thread_pool = { path = "../rustc_thread_pool" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_traits = { path = "../rustc_traits" }
 rustc_ty_utils = { path = "../rustc_ty_utils" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [dev-dependencies]
diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml
index e0019fb1821..343b81bd171 100644
--- a/compiler/rustc_lexer/Cargo.toml
+++ b/compiler/rustc_lexer/Cargo.toml
@@ -14,7 +14,7 @@ Rust lexer used by rustc. No stability guarantees are provided.
 
 # Note that this crate purposefully does not depend on other rustc crates
 [dependencies]
-memchr.workspace = true
+memchr = "2.7.4"
 unicode-properties = { version = "0.1.0", default-features = false, features = ["emoji"] }
 unicode-xid = "0.2.0"
 
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index 483cc3e93dc..d10b1920343 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -550,28 +550,20 @@ impl Cursor<'_> {
         self.eat_while(|ch| ch != '\n' && is_whitespace(ch));
         let invalid_infostring = self.first() != '\n';
 
-        let mut s = self.as_str();
         let mut found = false;
-        let mut size = 0;
-        while let Some(closing) = s.find(&"-".repeat(length_opening as usize)) {
-            let preceding_chars_start = s[..closing].rfind("\n").map_or(0, |i| i + 1);
-            if s[preceding_chars_start..closing].chars().all(is_whitespace) {
-                // candidate found
-                self.bump_bytes(size + closing);
-                // in case like
-                // ---cargo
-                // --- blahblah
-                // or
-                // ---cargo
-                // ----
-                // combine those stuff into this frontmatter token such that it gets detected later.
-                self.eat_until(b'\n');
-                found = true;
-                break;
-            } else {
-                s = &s[closing + length_opening as usize..];
-                size += closing + length_opening as usize;
-            }
+        let nl_fence_pattern = format!("\n{:-<1$}", "", length_opening as usize);
+        if let Some(closing) = self.as_str().find(&nl_fence_pattern) {
+            // candidate found
+            self.bump_bytes(closing + nl_fence_pattern.len());
+            // in case like
+            // ---cargo
+            // --- blahblah
+            // or
+            // ---cargo
+            // ----
+            // combine those stuff into this frontmatter token such that it gets detected later.
+            self.eat_until(b'\n');
+            found = true;
         }
 
         if !found {
diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml
index 7900e4b9ab2..7718f16984d 100644
--- a/compiler/rustc_lint/Cargo.toml
+++ b/compiler/rustc_lint/Cargo.toml
@@ -24,6 +24,6 @@ rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-tracing.workspace = true
+tracing = "0.1"
 unicode-security = "0.1.0"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 97aa1065967..ce92263babd 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -2309,10 +2309,10 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust
-    /// #![feature(sanitize)]
+    /// #![cfg_attr(not(bootstrap), feature(sanitize))]
     ///
     /// #[inline(always)]
-    /// #[sanitize(address = "off")]
+    /// #[cfg_attr(not(bootstrap), sanitize(address = "off"))]
     /// fn x() {}
     ///
     /// fn main() {
@@ -4832,13 +4832,16 @@ declare_lint! {
     ///
     /// ### Example
     ///
-    /// ```rust,compile_fail
+    #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")]
+    #[cfg_attr(bootstrap, doc = "```rust")]
     /// #![doc = in_root!()]
     ///
     /// macro_rules! in_root { () => { "" } }
     ///
     /// fn main() {}
-    /// ```
+    #[cfg_attr(not(bootstrap), doc = "```")]
+    #[cfg_attr(bootstrap, doc = "```")]
+    // ^ Needed to avoid tidy warning about odd number of backticks
     ///
     /// {{produces}}
     ///
diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml
index e74de453be2..cd352ce3d0f 100644
--- a/compiler/rustc_llvm/Cargo.toml
+++ b/compiler/rustc_llvm/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-libc.workspace = true
+libc = "0.2.73"
 # tidy-alphabetical-end
 
 [build-dependencies]
diff --git a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
index a910e78d489..fccfff65cfc 100644
--- a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
@@ -103,14 +103,14 @@ LLVMRustGetSymbols(char *BufPtr, size_t BufLen, void *State,
 #define TRUE_PTR (void *)1
 #define FALSE_PTR (void *)0
 
-extern "C" bool LLVMRustIs64BitSymbolicFile(char *BufPtr, size_t BufLen) {
+bool withBufferAsSymbolicFile(
+    char *BufPtr, size_t BufLen,
+    std::function<bool(object::SymbolicFile &)> Callback) {
   std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(
       StringRef(BufPtr, BufLen), StringRef("LLVMRustGetSymbolsObject"), false);
   SmallString<0> SymNameBuf;
   auto SymName = raw_svector_ostream(SymNameBuf);
 
-  // Code starting from this line is copied from s64BitSymbolicFile in
-  // ArchiveWriter.cpp.
   // In the scenario when LLVMContext is populated SymbolicFile will contain a
   // reference to it, thus SymbolicFile should be destroyed first.
   LLVMContext Context;
@@ -120,48 +120,63 @@ extern "C" bool LLVMRustIs64BitSymbolicFile(char *BufPtr, size_t BufLen) {
     return false;
   }
   std::unique_ptr<object::SymbolicFile> Obj = std::move(*ObjOrErr);
-
-  return Obj != nullptr ? Obj->is64Bit() : false;
-}
-
-extern "C" bool LLVMRustIsECObject(char *BufPtr, size_t BufLen) {
-  std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(
-      StringRef(BufPtr, BufLen), StringRef("LLVMRustGetSymbolsObject"), false);
-  SmallString<0> SymNameBuf;
-  auto SymName = raw_svector_ostream(SymNameBuf);
-
-  // In the scenario when LLVMContext is populated SymbolicFile will contain a
-  // reference to it, thus SymbolicFile should be destroyed first.
-  LLVMContext Context;
-  Expected<std::unique_ptr<object::SymbolicFile>> ObjOrErr =
-      getSymbolicFile(Buf->getMemBufferRef(), Context);
-  if (!ObjOrErr) {
-    return false;
-  }
-  std::unique_ptr<object::SymbolicFile> Obj = std::move(*ObjOrErr);
-
   if (Obj == nullptr) {
     return false;
   }
+  return Callback(*Obj);
+}
 
-  // Code starting from this line is copied from isECObject in
-  // ArchiveWriter.cpp with an extra #if to work with LLVM 17.
-  if (Obj->isCOFF())
-    return cast<llvm::object::COFFObjectFile>(&*Obj)->getMachine() !=
-           COFF::IMAGE_FILE_MACHINE_ARM64;
-
-  if (Obj->isCOFFImportFile())
-    return cast<llvm::object::COFFImportFile>(&*Obj)->getMachine() !=
-           COFF::IMAGE_FILE_MACHINE_ARM64;
-
-  if (Obj->isIR()) {
-    Expected<std::string> TripleStr =
-        getBitcodeTargetTriple(Obj->getMemoryBufferRef());
-    if (!TripleStr)
-      return false;
-    Triple T(*TripleStr);
-    return T.isWindowsArm64EC() || T.getArch() == Triple::x86_64;
-  }
+extern "C" bool LLVMRustIs64BitSymbolicFile(char *BufPtr, size_t BufLen) {
+  return withBufferAsSymbolicFile(
+      BufPtr, BufLen, [](object::SymbolicFile &Obj) { return Obj.is64Bit(); });
+}
+
+extern "C" bool LLVMRustIsECObject(char *BufPtr, size_t BufLen) {
+  return withBufferAsSymbolicFile(
+      BufPtr, BufLen, [](object::SymbolicFile &Obj) {
+        // Code starting from this line is copied from isECObject in
+        // ArchiveWriter.cpp with an extra #if to work with LLVM 17.
+        if (Obj.isCOFF())
+          return cast<llvm::object::COFFObjectFile>(&Obj)->getMachine() !=
+                 COFF::IMAGE_FILE_MACHINE_ARM64;
+
+        if (Obj.isCOFFImportFile())
+          return cast<llvm::object::COFFImportFile>(&Obj)->getMachine() !=
+                 COFF::IMAGE_FILE_MACHINE_ARM64;
+
+        if (Obj.isIR()) {
+          Expected<std::string> TripleStr =
+              getBitcodeTargetTriple(Obj.getMemoryBufferRef());
+          if (!TripleStr)
+            return false;
+          Triple T(*TripleStr);
+          return T.isWindowsArm64EC() || T.getArch() == Triple::x86_64;
+        }
+
+        return false;
+      });
+}
 
-  return false;
+extern "C" bool LLVMRustIsAnyArm64Coff(char *BufPtr, size_t BufLen) {
+  return withBufferAsSymbolicFile(
+      BufPtr, BufLen, [](object::SymbolicFile &Obj) {
+        // Code starting from this line is copied from isAnyArm64COFF in
+        // ArchiveWriter.cpp.
+        if (Obj.isCOFF())
+          return COFF::isAnyArm64(cast<COFFObjectFile>(&Obj)->getMachine());
+
+        if (Obj.isCOFFImportFile())
+          return COFF::isAnyArm64(cast<COFFImportFile>(&Obj)->getMachine());
+
+        if (Obj.isIR()) {
+          Expected<std::string> TripleStr =
+              getBitcodeTargetTriple(Obj.getMemoryBufferRef());
+          if (!TripleStr)
+            return false;
+          Triple T(*TripleStr);
+          return T.isOSWindows() && T.getArch() == Triple::aarch64;
+        }
+
+        return false;
+      });
 }
diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml
index e9d7b4723f8..c673d51a1d4 100644
--- a/compiler/rustc_log/Cargo.toml
+++ b/compiler/rustc_log/Cargo.toml
@@ -5,10 +5,10 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
+tracing = "0.1.28"
 tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635
 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
 tracing-tree = "0.3.1"
-tracing.workspace = true
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml
index 5add2691b88..f9d3b758359 100644
--- a/compiler/rustc_macros/Cargo.toml
+++ b/compiler/rustc_macros/Cargo.toml
@@ -8,8 +8,8 @@ proc-macro = true
 
 [dependencies]
 # tidy-alphabetical-start
-proc-macro2.workspace = true
-quote.workspace = true
+proc-macro2 = "1"
+quote = "1"
 syn = { version = "2.0.9", features = ["full"] }
 synstructure = "0.13.0"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml
index a8f3dd18353..1b40d9f684e 100644
--- a/compiler/rustc_metadata/Cargo.toml
+++ b/compiler/rustc_metadata/Cargo.toml
@@ -5,9 +5,9 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
+bitflags = "2.4.1"
 libloading = "0.8.0"
-odht.workspace = true
+odht = { version = "0.3.1", features = ["nightly"] }
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr_parsing = { path = "../rustc_attr_parsing" }
@@ -30,11 +30,11 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-tempfile.workspace = true
-tracing.workspace = true
+tempfile = "3.7.1"
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [target.'cfg(target_os = "aix")'.dependencies]
 # tidy-alphabetical-start
-libc.workspace = true
+libc = "0.2"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index f0832405567..fbcce16cedc 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -5,12 +5,12 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
-either.workspace = true
+bitflags = "2.4.1"
+either = "1.5.0"
 gsgdt = "0.1.2"
-polonius-engine.workspace = true
+polonius-engine = "0.13.0"
 rustc_abi = { path = "../rustc_abi" }
-rustc_apfloat.workspace = true
+rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_ir = { path = "../rustc_ast_ir" }
@@ -34,8 +34,8 @@ rustc_target = { path = "../rustc_target" }
 rustc_thread_pool = { path = "../rustc_thread_pool" }
 rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index c977e5329c2..da2245b12d2 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -62,9 +62,7 @@ pub use terminator::*;
 
 pub use self::generic_graph::graphviz_safe_def_name;
 pub use self::graphviz::write_mir_graphviz;
-pub use self::pretty::{
-    PassWhere, create_dump_file, display_allocation, dump_enabled, dump_mir, write_mir_pretty,
-};
+pub use self::pretty::{MirDumper, PassWhere, display_allocation, write_mir_pretty};
 
 /// Types for locals
 pub type LocalDecls<'tcx> = IndexSlice<Local, LocalDecl<'tcx>>;
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 128ae8549f7..a7d99f513a1 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -44,7 +44,7 @@ pub enum PassWhere {
 }
 
 /// Cosmetic options for pretty-printing the MIR contents, gathered from the CLI. Each pass can
-/// override these when dumping its own specific MIR information with [`dump_mir_with_options`].
+/// override these when dumping its own specific MIR information with `dump_mir`.
 #[derive(Copy, Clone)]
 pub struct PrettyPrintMirOptions {
     /// Whether to include extra comments, like span info. From `-Z mir-include-spans`.
@@ -58,277 +58,253 @@ impl PrettyPrintMirOptions {
     }
 }
 
-/// If the session is properly configured, dumps a human-readable representation of the MIR (with
-/// default pretty-printing options) into:
-///
-/// ```text
-/// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
-/// ```
-///
-/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
-/// where `<filter>` takes the following forms:
-///
-/// - `all` -- dump MIR for all fns, all passes, all everything
-/// - a filter defined by a set of substrings combined with `&` and `|`
-///   (`&` has higher precedence). At least one of the `|`-separated groups
-///   must match; an `|`-separated group matches if all of its `&`-separated
-///   substrings are matched.
-///
-/// Example:
-///
-/// - `nll` == match if `nll` appears in the name
-/// - `foo & nll` == match if `foo` and `nll` both appear in the name
-/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
-///   or `typeck` appears in the name.
-/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
-///   or `typeck` and `bar` both appear in the name.
-#[inline]
-pub fn dump_mir<'tcx, F>(
-    tcx: TyCtxt<'tcx>,
-    pass_num: bool,
-    pass_name: &str,
-    disambiguator: &dyn Display,
-    body: &Body<'tcx>,
-    extra_data: F,
-) where
-    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
-{
-    dump_mir_with_options(
-        tcx,
-        pass_num,
-        pass_name,
-        disambiguator,
-        body,
-        extra_data,
-        PrettyPrintMirOptions::from_cli(tcx),
-    );
+/// Manages MIR dumping, which is MIR writing done to a file with a specific name. In particular,
+/// it makes it impossible to dump MIR to one of these files when it hasn't been requested from the
+/// command line. Layered on top of `MirWriter`, which does the actual writing.
+pub struct MirDumper<'dis, 'de, 'tcx> {
+    show_pass_num: bool,
+    pass_name: &'static str,
+    disambiguator: &'dis dyn Display,
+    writer: MirWriter<'de, 'tcx>,
 }
 
-/// If the session is properly configured, dumps a human-readable representation of the MIR, with
-/// the given [pretty-printing options][PrettyPrintMirOptions].
-///
-/// See [`dump_mir`] for more details.
-///
-#[inline]
-pub fn dump_mir_with_options<'tcx, F>(
-    tcx: TyCtxt<'tcx>,
-    pass_num: bool,
-    pass_name: &str,
-    disambiguator: &dyn Display,
-    body: &Body<'tcx>,
-    extra_data: F,
-    options: PrettyPrintMirOptions,
-) where
-    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
-{
-    if !dump_enabled(tcx, pass_name, body.source.def_id()) {
-        return;
-    }
-
-    dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data, options);
-}
+impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> {
+    // If dumping should be performed (e.g. because it was requested on the
+    // CLI), returns a `MirDumper` with default values for the following fields:
+    // - `show_pass_num`: `false`
+    // - `disambiguator`: `&0`
+    // - `writer.extra_data`: a no-op
+    // - `writer.options`: default options derived from CLI flags
+    pub fn new(tcx: TyCtxt<'tcx>, pass_name: &'static str, body: &Body<'tcx>) -> Option<Self> {
+        let dump_enabled = if let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir {
+            // see notes on #41697 below
+            let node_path =
+                ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
+            filters.split('|').any(|or_filter| {
+                or_filter.split('&').all(|and_filter| {
+                    let and_filter_trimmed = and_filter.trim();
+                    and_filter_trimmed == "all"
+                        || pass_name.contains(and_filter_trimmed)
+                        || node_path.contains(and_filter_trimmed)
+                })
+            })
+        } else {
+            false
+        };
 
-pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool {
-    let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir else {
-        return false;
-    };
-    // see notes on #41697 below
-    let node_path = ty::print::with_forced_impl_filename_line!(tcx.def_path_str(def_id));
-    filters.split('|').any(|or_filter| {
-        or_filter.split('&').all(|and_filter| {
-            let and_filter_trimmed = and_filter.trim();
-            and_filter_trimmed == "all"
-                || pass_name.contains(and_filter_trimmed)
-                || node_path.contains(and_filter_trimmed)
+        dump_enabled.then_some(MirDumper {
+            show_pass_num: false,
+            pass_name,
+            disambiguator: &0,
+            writer: MirWriter::new(tcx),
         })
-    })
-}
+    }
 
-// #41697 -- we use `with_forced_impl_filename_line()` because
-// `def_path_str()` would otherwise trigger `type_of`, and this can
-// run while we are already attempting to evaluate `type_of`.
+    pub fn tcx(&self) -> TyCtxt<'tcx> {
+        self.writer.tcx
+    }
 
-/// Most use-cases of dumping MIR should use the [dump_mir] entrypoint instead, which will also
-/// check if dumping MIR is enabled, and if this body matches the filters passed on the CLI.
-///
-/// That being said, if the above requirements have been validated already, this function is where
-/// most of the MIR dumping occurs, if one needs to export it to a file they have created with
-/// [create_dump_file], rather than to a new file created as part of [dump_mir], or to stdout/stderr
-/// for debugging purposes.
-pub fn dump_mir_to_writer<'tcx, F>(
-    tcx: TyCtxt<'tcx>,
-    pass_name: &str,
-    disambiguator: &dyn Display,
-    body: &Body<'tcx>,
-    w: &mut dyn io::Write,
-    mut extra_data: F,
-    options: PrettyPrintMirOptions,
-) -> io::Result<()>
-where
-    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
-{
-    // see notes on #41697 above
-    let def_path =
-        ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
-    // ignore-tidy-odd-backticks the literal below is fine
-    write!(w, "// MIR for `{def_path}")?;
-    match body.source.promoted {
-        None => write!(w, "`")?,
-        Some(promoted) => write!(w, "::{promoted:?}`")?,
-    }
-    writeln!(w, " {disambiguator} {pass_name}")?;
-    if let Some(ref layout) = body.coroutine_layout_raw() {
-        writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
+    #[must_use]
+    pub fn set_show_pass_num(mut self) -> Self {
+        self.show_pass_num = true;
+        self
     }
-    writeln!(w)?;
-    extra_data(PassWhere::BeforeCFG, w)?;
-    write_user_type_annotations(tcx, body, w)?;
-    write_mir_fn(tcx, body, &mut extra_data, w, options)?;
-    extra_data(PassWhere::AfterCFG, w)
-}
 
-fn dump_matched_mir_node<'tcx, F>(
-    tcx: TyCtxt<'tcx>,
-    pass_num: bool,
-    pass_name: &str,
-    disambiguator: &dyn Display,
-    body: &Body<'tcx>,
-    extra_data: F,
-    options: PrettyPrintMirOptions,
-) where
-    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
-{
-    let _: io::Result<()> = try {
-        let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
-        dump_mir_to_writer(tcx, pass_name, disambiguator, body, &mut file, extra_data, options)?;
-    };
+    #[must_use]
+    pub fn set_disambiguator(mut self, disambiguator: &'dis dyn Display) -> Self {
+        self.disambiguator = disambiguator;
+        self
+    }
 
-    if tcx.sess.opts.unstable_opts.dump_mir_graphviz {
-        let _: io::Result<()> = try {
-            let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body)?;
-            write_mir_fn_graphviz(tcx, body, false, &mut file)?;
-        };
+    #[must_use]
+    pub fn set_extra_data(
+        mut self,
+        extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,
+    ) -> Self {
+        self.writer.extra_data = extra_data;
+        self
     }
-}
 
-/// Returns the path to the filename where we should dump a given MIR.
-/// Also used by other bits of code (e.g., NLL inference) that dump
-/// graphviz data or other things.
-fn dump_path<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    extension: &str,
-    pass_num: bool,
-    pass_name: &str,
-    disambiguator: &dyn Display,
-    body: &Body<'tcx>,
-) -> PathBuf {
-    let source = body.source;
-    let promotion_id = match source.promoted {
-        Some(id) => format!("-{id:?}"),
-        None => String::new(),
-    };
+    #[must_use]
+    pub fn set_options(mut self, options: PrettyPrintMirOptions) -> Self {
+        self.writer.options = options;
+        self
+    }
 
-    let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
-        String::new()
-    } else if pass_num {
-        let (dialect_index, phase_index) = body.phase.index();
-        format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count)
-    } else {
-        ".-------".to_string()
-    };
+    /// If the session is properly configured, dumps a human-readable representation of the MIR
+    /// (with default pretty-printing options) into:
+    ///
+    /// ```text
+    /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
+    /// ```
+    ///
+    /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
+    /// where `<filter>` takes the following forms:
+    ///
+    /// - `all` -- dump MIR for all fns, all passes, all everything
+    /// - a filter defined by a set of substrings combined with `&` and `|`
+    ///   (`&` has higher precedence). At least one of the `|`-separated groups
+    ///   must match; an `|`-separated group matches if all of its `&`-separated
+    ///   substrings are matched.
+    ///
+    /// Example:
+    ///
+    /// - `nll` == match if `nll` appears in the name
+    /// - `foo & nll` == match if `foo` and `nll` both appear in the name
+    /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
+    ///   or `typeck` appears in the name.
+    /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
+    ///   or `typeck` and `bar` both appear in the name.
+    pub fn dump_mir(&self, body: &Body<'tcx>) {
+        let _: io::Result<()> = try {
+            let mut file = self.create_dump_file("mir", body)?;
+            self.dump_mir_to_writer(body, &mut file)?;
+        };
 
-    let crate_name = tcx.crate_name(source.def_id().krate);
-    let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
-    // All drop shims have the same DefId, so we have to add the type
-    // to get unique file names.
-    let shim_disambiguator = match source.instance {
-        ty::InstanceKind::DropGlue(_, Some(ty)) => {
-            // Unfortunately, pretty-printed typed are not very filename-friendly.
-            // We dome some filtering.
-            let mut s = ".".to_owned();
-            s.extend(ty.to_string().chars().filter_map(|c| match c {
-                ' ' => None,
-                ':' | '<' | '>' => Some('_'),
-                c => Some(c),
-            }));
-            s
-        }
-        ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
-            let mut s = ".".to_owned();
-            s.extend(ty.to_string().chars().filter_map(|c| match c {
-                ' ' => None,
-                ':' | '<' | '>' => Some('_'),
-                c => Some(c),
-            }));
-            s
-        }
-        ty::InstanceKind::AsyncDropGlue(_, ty) => {
-            let ty::Coroutine(_, args) = ty.kind() else {
-                bug!();
+        if self.tcx().sess.opts.unstable_opts.dump_mir_graphviz {
+            let _: io::Result<()> = try {
+                let mut file = self.create_dump_file("dot", body)?;
+                write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?;
             };
-            let ty = args.first().unwrap().expect_ty();
-            let mut s = ".".to_owned();
-            s.extend(ty.to_string().chars().filter_map(|c| match c {
-                ' ' => None,
-                ':' | '<' | '>' => Some('_'),
-                c => Some(c),
-            }));
-            s
         }
-        ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {
-            let mut s = ".".to_owned();
-            s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {
-                ' ' => None,
-                ':' | '<' | '>' => Some('_'),
-                c => Some(c),
-            }));
-            s.push('.');
-            s.extend(impl_cor.to_string().chars().filter_map(|c| match c {
-                ' ' => None,
-                ':' | '<' | '>' => Some('_'),
-                c => Some(c),
-            }));
-            s
+    }
+
+    // #41697 -- we use `with_forced_impl_filename_line()` because `def_path_str()` would otherwise
+    // trigger `type_of`, and this can run while we are already attempting to evaluate `type_of`.
+    pub fn dump_mir_to_writer(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {
+        // see notes on #41697 above
+        let def_path = ty::print::with_forced_impl_filename_line!(
+            self.tcx().def_path_str(body.source.def_id())
+        );
+        // ignore-tidy-odd-backticks the literal below is fine
+        write!(w, "// MIR for `{def_path}")?;
+        match body.source.promoted {
+            None => write!(w, "`")?,
+            Some(promoted) => write!(w, "::{promoted:?}`")?,
+        }
+        writeln!(w, " {} {}", self.disambiguator, self.pass_name)?;
+        if let Some(ref layout) = body.coroutine_layout_raw() {
+            writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
         }
-        _ => String::new(),
-    };
+        writeln!(w)?;
+        (self.writer.extra_data)(PassWhere::BeforeCFG, w)?;
+        write_user_type_annotations(self.tcx(), body, w)?;
+        self.writer.write_mir_fn(body, w)?;
+        (self.writer.extra_data)(PassWhere::AfterCFG, w)
+    }
 
-    let mut file_path = PathBuf::new();
-    file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
+    /// Returns the path to the filename where we should dump a given MIR.
+    /// Also used by other bits of code (e.g., NLL inference) that dump
+    /// graphviz data or other things.
+    fn dump_path(&self, extension: &str, body: &Body<'tcx>) -> PathBuf {
+        let tcx = self.tcx();
+        let source = body.source;
+        let promotion_id = match source.promoted {
+            Some(id) => format!("-{id:?}"),
+            None => String::new(),
+        };
 
-    let file_name = format!(
-        "{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",
-    );
+        let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
+            String::new()
+        } else if self.show_pass_num {
+            let (dialect_index, phase_index) = body.phase.index();
+            format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count)
+        } else {
+            ".-------".to_string()
+        };
 
-    file_path.push(&file_name);
+        let crate_name = tcx.crate_name(source.def_id().krate);
+        let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
+        // All drop shims have the same DefId, so we have to add the type
+        // to get unique file names.
+        let shim_disambiguator = match source.instance {
+            ty::InstanceKind::DropGlue(_, Some(ty)) => {
+                // Unfortunately, pretty-printed types are not very filename-friendly.
+                // We do some filtering.
+                let mut s = ".".to_owned();
+                s.extend(ty.to_string().chars().filter_map(|c| match c {
+                    ' ' => None,
+                    ':' | '<' | '>' => Some('_'),
+                    c => Some(c),
+                }));
+                s
+            }
+            ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
+                let mut s = ".".to_owned();
+                s.extend(ty.to_string().chars().filter_map(|c| match c {
+                    ' ' => None,
+                    ':' | '<' | '>' => Some('_'),
+                    c => Some(c),
+                }));
+                s
+            }
+            ty::InstanceKind::AsyncDropGlue(_, ty) => {
+                let ty::Coroutine(_, args) = ty.kind() else {
+                    bug!();
+                };
+                let ty = args.first().unwrap().expect_ty();
+                let mut s = ".".to_owned();
+                s.extend(ty.to_string().chars().filter_map(|c| match c {
+                    ' ' => None,
+                    ':' | '<' | '>' => Some('_'),
+                    c => Some(c),
+                }));
+                s
+            }
+            ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {
+                let mut s = ".".to_owned();
+                s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {
+                    ' ' => None,
+                    ':' | '<' | '>' => Some('_'),
+                    c => Some(c),
+                }));
+                s.push('.');
+                s.extend(impl_cor.to_string().chars().filter_map(|c| match c {
+                    ' ' => None,
+                    ':' | '<' | '>' => Some('_'),
+                    c => Some(c),
+                }));
+                s
+            }
+            _ => String::new(),
+        };
 
-    file_path
-}
+        let mut file_path = PathBuf::new();
+        file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
 
-/// Attempts to open a file where we should dump a given MIR or other
-/// bit of MIR-related data. Used by `mir-dump`, but also by other
-/// bits of code (e.g., NLL inference) that dump graphviz data or
-/// other things, and hence takes the extension as an argument.
-pub fn create_dump_file<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    extension: &str,
-    pass_num: bool,
-    pass_name: &str,
-    disambiguator: &dyn Display,
-    body: &Body<'tcx>,
-) -> io::Result<io::BufWriter<fs::File>> {
-    let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, body);
-    if let Some(parent) = file_path.parent() {
-        fs::create_dir_all(parent).map_err(|e| {
-            io::Error::new(
-                e.kind(),
-                format!("IO error creating MIR dump directory: {parent:?}; {e}"),
-            )
-        })?;
+        let pass_name = self.pass_name;
+        let disambiguator = self.disambiguator;
+        let file_name = format!(
+            "{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",
+        );
+
+        file_path.push(&file_name);
+
+        file_path
+    }
+
+    /// Attempts to open a file where we should dump a given MIR or other
+    /// bit of MIR-related data. Used by `mir-dump`, but also by other
+    /// bits of code (e.g., NLL inference) that dump graphviz data or
+    /// other things, and hence takes the extension as an argument.
+    pub fn create_dump_file(
+        &self,
+        extension: &str,
+        body: &Body<'tcx>,
+    ) -> io::Result<io::BufWriter<fs::File>> {
+        let file_path = self.dump_path(extension, body);
+        if let Some(parent) = file_path.parent() {
+            fs::create_dir_all(parent).map_err(|e| {
+                io::Error::new(
+                    e.kind(),
+                    format!("IO error creating MIR dump directory: {parent:?}; {e}"),
+                )
+            })?;
+        }
+        fs::File::create_buffered(&file_path).map_err(|e| {
+            io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
+        })
     }
-    fs::File::create_buffered(&file_path).map_err(|e| {
-        io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
-    })
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -341,7 +317,7 @@ pub fn write_mir_pretty<'tcx>(
     single: Option<DefId>,
     w: &mut dyn io::Write,
 ) -> io::Result<()> {
-    let options = PrettyPrintMirOptions::from_cli(tcx);
+    let writer = MirWriter::new(tcx);
 
     writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
     writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
@@ -357,11 +333,11 @@ pub fn write_mir_pretty<'tcx>(
         }
 
         let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
-            write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
+            writer.write_mir_fn(body, w)?;
 
             for body in tcx.promoted_mir(def_id) {
                 writeln!(w)?;
-                write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
+                writer.write_mir_fn(body, w)?;
             }
             Ok(())
         };
@@ -373,7 +349,7 @@ pub fn write_mir_pretty<'tcx>(
             writeln!(w, "// MIR FOR CTFE")?;
             // Do not use `render_body`, as that would render the promoteds again, but these
             // are shared between mir_for_ctfe and optimized_mir
-            write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w, options)?;
+            writer.write_mir_fn(tcx.mir_for_ctfe(def_id), w)?;
         } else {
             let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
             render_body(w, instance_mir)?;
@@ -382,31 +358,35 @@ pub fn write_mir_pretty<'tcx>(
     Ok(())
 }
 
-/// Write out a human-readable textual representation for the given function.
-pub fn write_mir_fn<'tcx, F>(
+/// Does the writing of MIR to output, e.g. a file.
+pub struct MirWriter<'de, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    body: &Body<'tcx>,
-    extra_data: &mut F,
-    w: &mut dyn io::Write,
+    extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,
     options: PrettyPrintMirOptions,
-) -> io::Result<()>
-where
-    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
-{
-    write_mir_intro(tcx, body, w, options)?;
-    for block in body.basic_blocks.indices() {
-        extra_data(PassWhere::BeforeBlock(block), w)?;
-        write_basic_block(tcx, block, body, extra_data, w, options)?;
-        if block.index() + 1 != body.basic_blocks.len() {
-            writeln!(w)?;
-        }
+}
+
+impl<'de, 'tcx> MirWriter<'de, 'tcx> {
+    pub fn new(tcx: TyCtxt<'tcx>) -> Self {
+        MirWriter { tcx, extra_data: &|_, _| Ok(()), options: PrettyPrintMirOptions::from_cli(tcx) }
     }
 
-    writeln!(w, "}}")?;
+    /// Write out a human-readable textual representation for the given function.
+    pub fn write_mir_fn(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {
+        write_mir_intro(self.tcx, body, w, self.options)?;
+        for block in body.basic_blocks.indices() {
+            (self.extra_data)(PassWhere::BeforeBlock(block), w)?;
+            self.write_basic_block(block, body, w)?;
+            if block.index() + 1 != body.basic_blocks.len() {
+                writeln!(w)?;
+            }
+        }
 
-    write_allocations(tcx, body, w)?;
+        writeln!(w, "}}")?;
 
-    Ok(())
+        write_allocations(self.tcx, body, w)?;
+
+        Ok(())
+    }
 }
 
 /// Prints local variables in a scope tree.
@@ -719,95 +699,88 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
 ///////////////////////////////////////////////////////////////////////////
 // Basic blocks and their parts (statements, terminators, ...)
 
-/// Write out a human-readable textual representation for the given basic block.
-fn write_basic_block<'tcx, F>(
-    tcx: TyCtxt<'tcx>,
-    block: BasicBlock,
-    body: &Body<'tcx>,
-    extra_data: &mut F,
-    w: &mut dyn io::Write,
-    options: PrettyPrintMirOptions,
-) -> io::Result<()>
-where
-    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
-{
-    let data = &body[block];
-
-    // Basic block label at the top.
-    let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
-    writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
+impl<'de, 'tcx> MirWriter<'de, 'tcx> {
+    /// Write out a human-readable textual representation for the given basic block.
+    fn write_basic_block(
+        &self,
+        block: BasicBlock,
+        body: &Body<'tcx>,
+        w: &mut dyn io::Write,
+    ) -> io::Result<()> {
+        let data = &body[block];
+
+        // Basic block label at the top.
+        let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
+        writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
+
+        // List of statements in the middle.
+        let mut current_location = Location { block, statement_index: 0 };
+        for statement in &data.statements {
+            (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
+            let indented_body = format!("{INDENT}{INDENT}{statement:?};");
+            if self.options.include_extra_comments {
+                writeln!(
+                    w,
+                    "{:A$} // {}{}",
+                    indented_body,
+                    if self.tcx.sess.verbose_internals() {
+                        format!("{current_location:?}: ")
+                    } else {
+                        String::new()
+                    },
+                    comment(self.tcx, statement.source_info),
+                    A = ALIGN,
+                )?;
+            } else {
+                writeln!(w, "{indented_body}")?;
+            }
 
-    // List of statements in the middle.
-    let mut current_location = Location { block, statement_index: 0 };
-    for statement in &data.statements {
-        extra_data(PassWhere::BeforeLocation(current_location), w)?;
-        let indented_body = format!("{INDENT}{INDENT}{statement:?};");
-        if options.include_extra_comments {
-            writeln!(
+            write_extra(
+                self.tcx,
                 w,
-                "{:A$} // {}{}",
-                indented_body,
-                if tcx.sess.verbose_internals() {
-                    format!("{current_location:?}: ")
-                } else {
-                    String::new()
-                },
-                comment(tcx, statement.source_info),
-                A = ALIGN,
+                &|visitor| visitor.visit_statement(statement, current_location),
+                self.options,
             )?;
-        } else {
-            writeln!(w, "{indented_body}")?;
-        }
 
-        write_extra(
-            tcx,
-            w,
-            |visitor| {
-                visitor.visit_statement(statement, current_location);
-            },
-            options,
-        )?;
+            (self.extra_data)(PassWhere::AfterLocation(current_location), w)?;
 
-        extra_data(PassWhere::AfterLocation(current_location), w)?;
+            current_location.statement_index += 1;
+        }
 
-        current_location.statement_index += 1;
-    }
+        // Terminator at the bottom.
+        (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
+        if data.terminator.is_some() {
+            let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
+            if self.options.include_extra_comments {
+                writeln!(
+                    w,
+                    "{:A$} // {}{}",
+                    indented_terminator,
+                    if self.tcx.sess.verbose_internals() {
+                        format!("{current_location:?}: ")
+                    } else {
+                        String::new()
+                    },
+                    comment(self.tcx, data.terminator().source_info),
+                    A = ALIGN,
+                )?;
+            } else {
+                writeln!(w, "{indented_terminator}")?;
+            }
 
-    // Terminator at the bottom.
-    extra_data(PassWhere::BeforeLocation(current_location), w)?;
-    if data.terminator.is_some() {
-        let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
-        if options.include_extra_comments {
-            writeln!(
+            write_extra(
+                self.tcx,
                 w,
-                "{:A$} // {}{}",
-                indented_terminator,
-                if tcx.sess.verbose_internals() {
-                    format!("{current_location:?}: ")
-                } else {
-                    String::new()
-                },
-                comment(tcx, data.terminator().source_info),
-                A = ALIGN,
+                &|visitor| visitor.visit_terminator(data.terminator(), current_location),
+                self.options,
             )?;
-        } else {
-            writeln!(w, "{indented_terminator}")?;
         }
 
-        write_extra(
-            tcx,
-            w,
-            |visitor| {
-                visitor.visit_terminator(data.terminator(), current_location);
-            },
-            options,
-        )?;
-    }
-
-    extra_data(PassWhere::AfterLocation(current_location), w)?;
-    extra_data(PassWhere::AfterTerminator(block), w)?;
+        (self.extra_data)(PassWhere::AfterLocation(current_location), w)?;
+        (self.extra_data)(PassWhere::AfterTerminator(block), w)?;
 
-    writeln!(w, "{INDENT}}}")
+        writeln!(w, "{INDENT}}}")
+    }
 }
 
 impl Debug for Statement<'_> {
@@ -1374,15 +1347,12 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
 /// After we print the main statement, we sometimes dump extra
 /// information. There's often a lot of little things "nuzzled up" in
 /// a statement.
-fn write_extra<'tcx, F>(
+fn write_extra<'tcx>(
     tcx: TyCtxt<'tcx>,
     write: &mut dyn io::Write,
-    mut visit_op: F,
+    visit_op: &dyn Fn(&mut ExtraComments<'tcx>),
     options: PrettyPrintMirOptions,
-) -> io::Result<()>
-where
-    F: FnMut(&mut ExtraComments<'tcx>),
-{
+) -> io::Result<()> {
     if options.include_extra_comments {
         let mut extra_comments = ExtraComments { tcx, comments: vec![] };
         visit_op(&mut extra_comments);
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 7bd8a0525a2..8ce70f75c67 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -450,6 +450,8 @@ rustc_queries! {
         }
     }
 
+    /// A list of all bodies inside of `key`, nested bodies are always stored
+    /// before their parent.
     query nested_bodies_within(
         key: LocalDefId
     ) -> &'tcx ty::List<LocalDefId> {
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 32f91bfba6b..ab8a3142953 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -389,10 +389,14 @@ pub enum ObligationCauseCode<'tcx> {
     /// against.
     MatchImpl(ObligationCause<'tcx>, DefId),
 
+    UnOp {
+        hir_id: HirId,
+    },
+
     BinOp {
         lhs_hir_id: HirId,
-        rhs_hir_id: Option<HirId>,
-        rhs_span: Option<Span>,
+        rhs_hir_id: HirId,
+        rhs_span: Span,
         rhs_is_lit: bool,
         output_ty: Option<Ty<'tcx>>,
     },
diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml
index 440cb0bdbf3..f756f0a19ee 100644
--- a/compiler/rustc_mir_build/Cargo.toml
+++ b/compiler/rustc_mir_build/Cargo.toml
@@ -5,9 +5,9 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools.workspace = true
+itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
-rustc_apfloat.workspace = true
+rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
@@ -23,5 +23,5 @@ rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs
index eb99c184bd2..7676b720e35 100644
--- a/compiler/rustc_mir_build/src/builder/expr/into.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/into.rs
@@ -293,9 +293,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     this.diverge_from(loop_block);
 
                     // Logic for `match`.
-                    let scrutinee_place_builder =
-                        unpack!(body_block = this.as_place_builder(body_block, scrutinee));
                     let scrutinee_span = this.thir.exprs[scrutinee].span;
+                    let scrutinee_place_builder = unpack!(
+                        body_block = this.lower_scrutinee(body_block, scrutinee, scrutinee_span)
+                    );
+
                     let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
 
                     let mut patterns = Vec::with_capacity(arms.len());
@@ -343,7 +345,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                         expr_span,
                                         |this| {
                                             this.lower_match_arms(
-                                                destination,
+                                                state_place,
                                                 scrutinee_place_builder,
                                                 scrutinee_span,
                                                 arms,
diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs
index 6ee9674bb08..d216c4ecd11 100644
--- a/compiler/rustc_mir_build/src/builder/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs
@@ -388,7 +388,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     }
 
     /// Evaluate the scrutinee and add the PlaceMention for it.
-    fn lower_scrutinee(
+    pub(crate) fn lower_scrutinee(
         &mut self,
         mut block: BasicBlock,
         scrutinee_id: ExprId,
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index 6a8f0b21ee0..cdb2c5561ce 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -806,10 +806,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         );
         body.coverage_info_hi = self.coverage_info.as_ref().map(|b| b.as_done());
 
-        use rustc_middle::mir::pretty;
-        let options = pretty::PrettyPrintMirOptions::from_cli(self.tcx);
-        pretty::write_mir_fn(self.tcx, &body, &mut |_, _| Ok(()), &mut std::io::stdout(), options)
-            .unwrap();
+        let writer = pretty::MirWriter::new(self.tcx);
+        writer.write_mir_fn(&body, &mut std::io::stdout()).unwrap();
     }
 
     fn finish(self) -> Body<'tcx> {
@@ -827,18 +825,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         );
         body.coverage_info_hi = self.coverage_info.map(|b| b.into_done());
 
+        let writer = pretty::MirWriter::new(self.tcx);
         for (index, block) in body.basic_blocks.iter().enumerate() {
             if block.terminator.is_none() {
-                use rustc_middle::mir::pretty;
-                let options = pretty::PrettyPrintMirOptions::from_cli(self.tcx);
-                pretty::write_mir_fn(
-                    self.tcx,
-                    &body,
-                    &mut |_, _| Ok(()),
-                    &mut std::io::stdout(),
-                    options,
-                )
-                .unwrap();
+                writer.write_mir_fn(&body, &mut std::io::stdout()).unwrap();
                 span_bug!(self.fn_span, "no terminator on block {:?}", index);
             }
         }
diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml
index 11713bb77f1..9621f9f20bd 100644
--- a/compiler/rustc_mir_dataflow/Cargo.toml
+++ b/compiler/rustc_mir_dataflow/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-polonius-engine.workspace = true
+polonius-engine = "0.13.0"
 regex = "1"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
@@ -18,5 +18,5 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_span = { path = "../rustc_span" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index 372a3f3a8b8..b85b82b8f6d 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -10,8 +10,7 @@ use std::{io, ops, str};
 use regex::Regex;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_middle::mir::{
-    self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name,
-    traversal,
+    self, BasicBlock, Body, Location, MirDumper, graphviz_safe_def_name, traversal,
 };
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -61,11 +60,13 @@ where
                 fs::File::create_buffered(&path)?
             }
 
-            None if dump_enabled(tcx, A::NAME, def_id) => {
-                create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
+            None => {
+                let Some(dumper) = MirDumper::new(tcx, A::NAME, body) else {
+                    return Ok(());
+                };
+                let disambiguator = &pass_name.unwrap_or("-----");
+                dumper.set_disambiguator(disambiguator).create_dump_file("dot", body)?
             }
-
-            _ => return Ok(()),
         }
     };
     let mut file = match file {
diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml
index 99ef67e2625..08c43a4648c 100644
--- a/compiler/rustc_mir_transform/Cargo.toml
+++ b/compiler/rustc_mir_transform/Cargo.toml
@@ -5,8 +5,8 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-either.workspace = true
-itertools.workspace = true
+either = "1"
+itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
@@ -26,5 +26,5 @@ rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 761d5461a99..4603c695ded 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -1294,7 +1294,9 @@ fn create_coroutine_resume_function<'tcx>(
 
     pm::run_passes_no_validate(tcx, body, &[&abort_unwinding_calls::AbortUnwindingCalls], None);
 
-    dump_mir(tcx, false, "coroutine_resume", &0, body, |_, _| Ok(()));
+    if let Some(dumper) = MirDumper::new(tcx, "coroutine_resume", body) {
+        dumper.dump_mir(body);
+    }
 }
 
 /// An operation that can be performed on a coroutine.
@@ -1446,7 +1448,9 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
 
         assert!(body.coroutine_drop().is_none() && body.coroutine_drop_async().is_none());
 
-        dump_mir(tcx, false, "coroutine_before", &0, body, |_, _| Ok(()));
+        if let Some(dumper) = MirDumper::new(tcx, "coroutine_before", body) {
+            dumper.dump_mir(body);
+        }
 
         // The first argument is the coroutine type passed by value
         let coroutine_ty = body.local_decls.raw[1].ty;
@@ -1506,7 +1510,10 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
         ) {
             let context_mut_ref = transform_async_context(tcx, body);
             expand_async_drops(tcx, body, context_mut_ref, coroutine_kind, coroutine_ty);
-            dump_mir(tcx, false, "coroutine_async_drop_expand", &0, body, |_, _| Ok(()));
+
+            if let Some(dumper) = MirDumper::new(tcx, "coroutine_async_drop_expand", body) {
+                dumper.dump_mir(body);
+            }
         } else {
             cleanup_async_drops(body);
         }
@@ -1605,14 +1612,18 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
         // This is expanded to a drop ladder in `elaborate_coroutine_drops`.
         let drop_clean = insert_clean_drop(tcx, body, has_async_drops);
 
-        dump_mir(tcx, false, "coroutine_pre-elab", &0, body, |_, _| Ok(()));
+        if let Some(dumper) = MirDumper::new(tcx, "coroutine_pre-elab", body) {
+            dumper.dump_mir(body);
+        }
 
         // Expand `drop(coroutine_struct)` to a drop ladder which destroys upvars.
         // If any upvars are moved out of, drop elaboration will handle upvar destruction.
         // However we need to also elaborate the code generated by `insert_clean_drop`.
         elaborate_coroutine_drops(tcx, body);
 
-        dump_mir(tcx, false, "coroutine_post-transform", &0, body, |_, _| Ok(()));
+        if let Some(dumper) = MirDumper::new(tcx, "coroutine_post-transform", body) {
+            dumper.dump_mir(body);
+        }
 
         let can_unwind = can_unwind(tcx, body);
 
diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
index 81d7b7ba02c..951ff69c19e 100644
--- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
@@ -77,7 +77,7 @@ use rustc_hir::definitions::DisambiguatorState;
 use rustc_middle::bug;
 use rustc_middle::hir::place::{Projection, ProjectionKind};
 use rustc_middle::mir::visit::MutVisitor;
-use rustc_middle::mir::{self, dump_mir};
+use rustc_middle::mir::{self, MirDumper};
 use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt};
 
 pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
@@ -225,7 +225,10 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
     );
     by_move_body.source =
         mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id()));
-    dump_mir(tcx, false, "built", &"after", &by_move_body, |_, _| Ok(()));
+
+    if let Some(dumper) = MirDumper::new(tcx, "built", &by_move_body) {
+        dumper.set_disambiguator(&"after").dump_mir(&by_move_body);
+    }
 
     // Feed HIR because we try to access this body's attrs in the inliner.
     body_def.feed_hir();
diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs
index 1a314e029f4..fd2d8b2b056 100644
--- a/compiler/rustc_mir_transform/src/coroutine/drop.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs
@@ -605,7 +605,9 @@ pub(super) fn create_coroutine_drop_shim<'tcx>(
     // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible
     // filename.
     body.source.instance = coroutine_instance;
-    dump_mir(tcx, false, "coroutine_drop", &0, &body, |_, _| Ok(()));
+    if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop", &body) {
+        dumper.dump_mir(&body);
+    }
     body.source.instance = drop_instance;
 
     // Creating a coroutine drop shim happens on `Analysis(PostCleanup) -> Runtime(Initial)`
@@ -696,7 +698,9 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>(
         None,
     );
 
-    dump_mir(tcx, false, "coroutine_drop_async", &0, &body, |_, _| Ok(()));
+    if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_async", &body) {
+        dumper.dump_mir(&body);
+    }
 
     body
 }
@@ -741,7 +745,9 @@ pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>(
     };
     body.basic_blocks_mut()[call_bb].terminator = Some(Terminator { source_info, kind });
 
-    dump_mir(tcx, false, "coroutine_drop_proxy_async", &0, &body, |_, _| Ok(()));
+    if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_proxy_async", &body) {
+        dumper.dump_mir(&body);
+    }
 
     body
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/expansion.rs b/compiler/rustc_mir_transform/src/coverage/expansion.rs
new file mode 100644
index 00000000000..91e0528f52f
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/coverage/expansion.rs
@@ -0,0 +1,127 @@
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
+use rustc_middle::mir::coverage::BasicCoverageBlock;
+use rustc_span::{ExpnId, ExpnKind, Span};
+
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct SpanWithBcb {
+    pub(crate) span: Span,
+    pub(crate) bcb: BasicCoverageBlock,
+}
+
+#[derive(Debug)]
+pub(crate) struct ExpnTree {
+    nodes: FxIndexMap<ExpnId, ExpnNode>,
+}
+
+impl ExpnTree {
+    pub(crate) fn get(&self, expn_id: ExpnId) -> Option<&ExpnNode> {
+        self.nodes.get(&expn_id)
+    }
+
+    /// Yields the tree node for the given expansion ID (if present), followed
+    /// by the nodes of all of its descendants in depth-first order.
+    pub(crate) fn iter_node_and_descendants(
+        &self,
+        root_expn_id: ExpnId,
+    ) -> impl Iterator<Item = &ExpnNode> {
+        gen move {
+            let Some(root_node) = self.get(root_expn_id) else { return };
+            yield root_node;
+
+            // Stack of child-node-ID iterators that drives the depth-first traversal.
+            let mut iter_stack = vec![root_node.child_expn_ids.iter()];
+
+            while let Some(curr_iter) = iter_stack.last_mut() {
+                // Pull the next ID from the top of the stack.
+                let Some(&curr_id) = curr_iter.next() else {
+                    iter_stack.pop();
+                    continue;
+                };
+
+                // Yield this node.
+                let Some(node) = self.get(curr_id) else { continue };
+                yield node;
+
+                // Push the node's children, to be traversed next.
+                if !node.child_expn_ids.is_empty() {
+                    iter_stack.push(node.child_expn_ids.iter());
+                }
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct ExpnNode {
+    /// Storing the expansion ID in its own node is not strictly necessary,
+    /// but is helpful for debugging and might be useful later.
+    #[expect(dead_code)]
+    pub(crate) expn_id: ExpnId,
+
+    // Useful info extracted from `ExpnData`.
+    pub(crate) expn_kind: ExpnKind,
+    /// Non-dummy `ExpnData::call_site` span.
+    pub(crate) call_site: Option<Span>,
+    /// Expansion ID of `call_site`, if present.
+    /// This links an expansion node to its parent in the tree.
+    pub(crate) call_site_expn_id: Option<ExpnId>,
+
+    /// Spans (and their associated BCBs) belonging to this expansion.
+    pub(crate) spans: Vec<SpanWithBcb>,
+    /// Expansions whose call-site is in this expansion.
+    pub(crate) child_expn_ids: FxIndexSet<ExpnId>,
+}
+
+impl ExpnNode {
+    fn new(expn_id: ExpnId) -> Self {
+        let expn_data = expn_id.expn_data();
+
+        let call_site = Some(expn_data.call_site).filter(|sp| !sp.is_dummy());
+        let call_site_expn_id = try { call_site?.ctxt().outer_expn() };
+
+        Self {
+            expn_id,
+
+            expn_kind: expn_data.kind.clone(),
+            call_site,
+            call_site_expn_id,
+
+            spans: vec![],
+            child_expn_ids: FxIndexSet::default(),
+        }
+    }
+}
+
+/// Given a collection of span/BCB pairs from potentially-different syntax contexts,
+/// arranges them into an "expansion tree" based on their expansion call-sites.
+pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> ExpnTree {
+    let mut nodes = FxIndexMap::default();
+    let new_node = |&expn_id: &ExpnId| ExpnNode::new(expn_id);
+
+    for span_with_bcb in spans {
+        // Create a node for this span's enclosing expansion, and add the span to it.
+        let expn_id = span_with_bcb.span.ctxt().outer_expn();
+        let node = nodes.entry(expn_id).or_insert_with_key(new_node);
+        node.spans.push(span_with_bcb);
+
+        // Now walk up the expansion call-site chain, creating nodes and registering children.
+        let mut prev = expn_id;
+        let mut curr_expn_id = node.call_site_expn_id;
+        while let Some(expn_id) = curr_expn_id {
+            let entry = nodes.entry(expn_id);
+            let node_existed = matches!(entry, IndexEntry::Occupied(_));
+
+            let node = entry.or_insert_with_key(new_node);
+            node.child_expn_ids.insert(prev);
+
+            if node_existed {
+                break;
+            }
+
+            prev = expn_id;
+            curr_expn_id = node.call_site_expn_id;
+        }
+    }
+
+    ExpnTree { nodes }
+}
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index c5fef299244..08c7d346009 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -8,6 +8,7 @@ use crate::coverage::graph::CoverageGraph;
 use crate::coverage::mappings::ExtractedMappings;
 
 mod counters;
+mod expansion;
 mod graph;
 mod hir_info;
 mod mappings;
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index d1b04c8f587..325935ee846 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,15 +1,14 @@
-use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::mir;
 use rustc_middle::mir::coverage::{Mapping, MappingKind, START_BCB};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::source_map::SourceMap;
-use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span};
+use rustc_span::{BytePos, DesugaringKind, ExpnId, ExpnKind, MacroKind, Span};
 use tracing::instrument;
 
+use crate::coverage::expansion::{self, ExpnTree, SpanWithBcb};
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
 use crate::coverage::hir_info::ExtractedHirInfo;
-use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir};
-use crate::coverage::unexpand;
+use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir};
 
 mod from_mir;
 
@@ -34,19 +33,51 @@ pub(super) fn extract_refined_covspans<'tcx>(
     let &ExtractedHirInfo { body_span, .. } = hir_info;
 
     let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);
-    let mut covspans = raw_spans
-        .into_iter()
-        .filter_map(|RawSpanFromMir { raw_span, bcb }| try {
-            let (span, expn_kind) =
-                unexpand::unexpand_into_body_span_with_expn_kind(raw_span, body_span)?;
-            // Discard any spans that fill the entire body, because they tend
-            // to represent compiler-inserted code, e.g. implicitly returning `()`.
-            if span.source_equal(body_span) {
-                return None;
-            };
-            SpanFromMir { span, expn_kind, bcb }
-        })
-        .collect::<Vec<_>>();
+    // Use the raw spans to build a tree of expansions for this function.
+    let expn_tree = expansion::build_expn_tree(
+        raw_spans
+            .into_iter()
+            .map(|RawSpanFromMir { raw_span, bcb }| SpanWithBcb { span: raw_span, bcb }),
+    );
+
+    let mut covspans = vec![];
+    let mut push_covspan = |covspan: Covspan| {
+        let covspan_span = covspan.span;
+        // Discard any spans not contained within the function body span.
+        // Also discard any spans that fill the entire body, because they tend
+        // to represent compiler-inserted code, e.g. implicitly returning `()`.
+        if !body_span.contains(covspan_span) || body_span.source_equal(covspan_span) {
+            return;
+        }
+
+        // Each pushed covspan should have the same context as the body span.
+        // If it somehow doesn't, discard the covspan, or panic in debug builds.
+        if !body_span.eq_ctxt(covspan_span) {
+            debug_assert!(
+                false,
+                "span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}"
+            );
+            return;
+        }
+
+        covspans.push(covspan);
+    };
+
+    if let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) {
+        for &SpanWithBcb { span, bcb } in &node.spans {
+            push_covspan(Covspan { span, bcb });
+        }
+
+        // For each expansion with its call-site in the body span, try to
+        // distill a corresponding covspan.
+        for &child_expn_id in &node.child_expn_ids {
+            if let Some(covspan) =
+                single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id)
+            {
+                push_covspan(covspan);
+            }
+        }
+    }
 
     // Only proceed if we found at least one usable span.
     if covspans.is_empty() {
@@ -57,17 +88,10 @@ pub(super) fn extract_refined_covspans<'tcx>(
     // Otherwise, add a fake span at the start of the body, to avoid an ugly
     // gap between the start of the body and the first real span.
     // FIXME: Find a more principled way to solve this problem.
-    covspans.push(SpanFromMir::for_fn_sig(
-        hir_info.fn_sig_span.unwrap_or_else(|| body_span.shrink_to_lo()),
-    ));
-
-    // First, perform the passes that need macro information.
-    covspans.sort_by(|a, b| graph.cmp_in_dominator_order(a.bcb, b.bcb));
-    remove_unwanted_expansion_spans(&mut covspans);
-    shrink_visible_macro_spans(tcx, &mut covspans);
-
-    // We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
-    let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::<Vec<_>>();
+    covspans.push(Covspan {
+        span: hir_info.fn_sig_span.unwrap_or_else(|| body_span.shrink_to_lo()),
+        bcb: START_BCB,
+    });
 
     let compare_covspans = |a: &Covspan, b: &Covspan| {
         compare_spans(a.span, b.span)
@@ -117,43 +141,37 @@ pub(super) fn extract_refined_covspans<'tcx>(
     }));
 }
 
-/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
-/// multiple condition/consequent blocks that have the span of the whole macro
-/// invocation, which is unhelpful. Keeping only the first such span seems to
-/// give better mappings, so remove the others.
-///
-/// Similarly, `await` expands to a branch on the discriminant of `Poll`, which
-/// leads to incorrect coverage if the `Future` is immediately ready (#98712).
-///
-/// (The input spans should be sorted in BCB dominator order, so that the
-/// retained "first" span is likely to dominate the others.)
-fn remove_unwanted_expansion_spans(covspans: &mut Vec<SpanFromMir>) {
-    let mut deduplicated_spans = FxHashSet::default();
-
-    covspans.retain(|covspan| {
-        match covspan.expn_kind {
-            // Retain only the first await-related or macro-expanded covspan with this span.
-            Some(ExpnKind::Desugaring(DesugaringKind::Await)) => {
-                deduplicated_spans.insert(covspan.span)
-            }
-            Some(ExpnKind::Macro(MacroKind::Bang, _)) => deduplicated_spans.insert(covspan.span),
-            // Ignore (retain) other spans.
-            _ => true,
+/// For a single child expansion, try to distill it into a single span+BCB mapping.
+fn single_covspan_for_child_expn(
+    tcx: TyCtxt<'_>,
+    graph: &CoverageGraph,
+    expn_tree: &ExpnTree,
+    expn_id: ExpnId,
+) -> Option<Covspan> {
+    let node = expn_tree.get(expn_id)?;
+
+    let bcbs =
+        expn_tree.iter_node_and_descendants(expn_id).flat_map(|n| n.spans.iter().map(|s| s.bcb));
+
+    let bcb = match node.expn_kind {
+        // For bang-macros (e.g. `assert!`, `trace!`) and for `await`, taking
+        // the "first" BCB in dominator order seems to give good results.
+        ExpnKind::Macro(MacroKind::Bang, _) | ExpnKind::Desugaring(DesugaringKind::Await) => {
+            bcbs.min_by(|&a, &b| graph.cmp_in_dominator_order(a, b))?
         }
-    });
-}
-
-/// When a span corresponds to a macro invocation that is visible from the
-/// function body, truncate it to just the macro name plus `!`.
-/// This seems to give better results for code that uses macros.
-fn shrink_visible_macro_spans(tcx: TyCtxt<'_>, covspans: &mut Vec<SpanFromMir>) {
-    let source_map = tcx.sess.source_map();
+        // For other kinds of expansion, taking the "last" (most-dominated) BCB
+        // seems to give good results.
+        _ => bcbs.max_by(|&a, &b| graph.cmp_in_dominator_order(a, b))?,
+    };
 
-    for covspan in covspans {
-        if matches!(covspan.expn_kind, Some(ExpnKind::Macro(MacroKind::Bang, _))) {
-            covspan.span = source_map.span_through_char(covspan.span, '!');
-        }
+    // For bang-macro expansions, limit the call-site span to just the macro
+    // name plus `!`, excluding the macro arguments.
+    let mut span = node.call_site?;
+    if matches!(node.expn_kind, ExpnKind::Macro(MacroKind::Bang, _)) {
+        span = tcx.sess.source_map().span_through_char(span, '!');
     }
+
+    Some(Covspan { span, bcb })
 }
 
 /// Discard all covspans that overlap a hole.
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index 7985e1c0798..dfeaa90dc2e 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -5,10 +5,9 @@ use rustc_middle::mir::coverage::CoverageKind;
 use rustc_middle::mir::{
     self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
 };
-use rustc_span::{ExpnKind, Span};
+use rustc_span::Span;
 
-use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
-use crate::coverage::spans::Covspan;
+use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
 
 #[derive(Debug)]
 pub(crate) struct RawSpanFromMir {
@@ -160,32 +159,3 @@ impl Hole {
         true
     }
 }
-
-#[derive(Debug)]
-pub(crate) struct SpanFromMir {
-    /// A span that has been extracted from MIR and then "un-expanded" back to
-    /// within the current function's `body_span`. After various intermediate
-    /// processing steps, this span is emitted as part of the final coverage
-    /// mappings.
-    ///
-    /// With the exception of `fn_sig_span`, this should always be contained
-    /// within `body_span`.
-    pub(crate) span: Span,
-    pub(crate) expn_kind: Option<ExpnKind>,
-    pub(crate) bcb: BasicCoverageBlock,
-}
-
-impl SpanFromMir {
-    pub(crate) fn for_fn_sig(fn_sig_span: Span) -> Self {
-        Self::new(fn_sig_span, None, START_BCB)
-    }
-
-    pub(crate) fn new(span: Span, expn_kind: Option<ExpnKind>, bcb: BasicCoverageBlock) -> Self {
-        Self { span, expn_kind, bcb }
-    }
-
-    pub(crate) fn into_covspan(self) -> Covspan {
-        let Self { span, expn_kind: _, bcb } = self;
-        Covspan { span, bcb }
-    }
-}
diff --git a/compiler/rustc_mir_transform/src/coverage/unexpand.rs b/compiler/rustc_mir_transform/src/coverage/unexpand.rs
index cb861544736..922edd3cc4f 100644
--- a/compiler/rustc_mir_transform/src/coverage/unexpand.rs
+++ b/compiler/rustc_mir_transform/src/coverage/unexpand.rs
@@ -1,4 +1,4 @@
-use rustc_span::{ExpnKind, Span};
+use rustc_span::Span;
 
 /// Walks through the expansion ancestors of `original_span` to find a span that
 /// is contained in `body_span` and has the same [syntax context] as `body_span`.
@@ -7,49 +7,3 @@ pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> O
     // we can just delegate directly to `find_ancestor_inside_same_ctxt`.
     original_span.find_ancestor_inside_same_ctxt(body_span)
 }
-
-/// Walks through the expansion ancestors of `original_span` to find a span that
-/// is contained in `body_span` and has the same [syntax context] as `body_span`.
-///
-/// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`),
-/// the returned symbol will be the name of that macro (e.g. `foo`).
-pub(crate) fn unexpand_into_body_span_with_expn_kind(
-    original_span: Span,
-    body_span: Span,
-) -> Option<(Span, Option<ExpnKind>)> {
-    let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
-
-    let expn_kind = prev.map(|prev| prev.ctxt().outer_expn_data().kind);
-
-    Some((span, expn_kind))
-}
-
-/// Walks through the expansion ancestors of `original_span` to find a span that
-/// is contained in `body_span` and has the same [syntax context] as `body_span`.
-/// The ancestor that was traversed just before the matching span (if any) is
-/// also returned.
-///
-/// For example, a return value of `Some((ancestor, Some(prev)))` means that:
-/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)`
-/// - `prev.parent_callsite() == ancestor`
-///
-/// [syntax context]: rustc_span::SyntaxContext
-fn unexpand_into_body_span_with_prev(
-    original_span: Span,
-    body_span: Span,
-) -> Option<(Span, Option<Span>)> {
-    let mut prev = None;
-    let mut curr = original_span;
-
-    while !body_span.contains(curr) || !curr.eq_ctxt(body_span) {
-        prev = Some(curr);
-        curr = curr.parent_callsite()?;
-    }
-
-    debug_assert_eq!(Some(curr), original_span.find_ancestor_inside_same_ctxt(body_span));
-    if let Some(prev) = prev {
-        debug_assert_eq!(Some(curr), prev.parent_callsite());
-    }
-
-    Some((curr, prev))
-}
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 4c94a6c524e..cf7425251e8 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -137,8 +137,8 @@ use rustc_index::interval::SparseIntervalMatrix;
 use rustc_middle::bug;
 use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
 use rustc_middle::mir::{
-    Body, HasLocalDecls, InlineAsmOperand, Local, LocalKind, Location, Operand, PassWhere, Place,
-    Rvalue, Statement, StatementKind, TerminatorKind, dump_mir, traversal,
+    Body, HasLocalDecls, InlineAsmOperand, Local, LocalKind, Location, MirDumper, Operand,
+    PassWhere, Place, Rvalue, Statement, StatementKind, TerminatorKind, traversal,
 };
 use rustc_middle::ty::TyCtxt;
 use rustc_mir_dataflow::Analysis;
@@ -810,11 +810,15 @@ fn dest_prop_mir_dump<'tcx>(
         let location = points.point_from_location(location);
         live.rows().filter(|&r| live.contains(r, location)).collect::<Vec<_>>()
     };
-    dump_mir(tcx, false, "DestinationPropagation-dataflow", &round, body, |pass_where, w| {
-        if let PassWhere::BeforeLocation(loc) = pass_where {
-            writeln!(w, "        // live: {:?}", locals_live_at(loc))?;
-        }
 
-        Ok(())
-    });
+    if let Some(dumper) = MirDumper::new(tcx, "DestinationPropagation-dataflow", body) {
+        let extra_data = &|pass_where, w: &mut dyn std::io::Write| {
+            if let PassWhere::BeforeLocation(loc) = pass_where {
+                writeln!(w, "        // live: {:?}", locals_live_at(loc))?;
+            }
+            Ok(())
+        };
+
+        dumper.set_disambiguator(&round).set_extra_data(extra_data).dump_mir(body)
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 08f25276cec..8f319e64916 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -5,6 +5,7 @@
 #![feature(const_type_name)]
 #![feature(cow_is_borrowed)]
 #![feature(file_buffered)]
+#![feature(gen_blocks)]
 #![feature(if_let_guard)]
 #![feature(impl_trait_in_assoc_type)]
 #![feature(try_blocks)]
diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
index 2f0edf31162..794984d2f3e 100644
--- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
+++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
@@ -12,8 +12,8 @@ use rustc_index::{IndexSlice, IndexVec};
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
 use rustc_middle::bug;
 use rustc_middle::mir::{
-    self, BasicBlock, Body, ClearCrossCrate, Local, Location, Place, StatementKind, TerminatorKind,
-    dump_mir,
+    self, BasicBlock, Body, ClearCrossCrate, Local, Location, MirDumper, Place, StatementKind,
+    TerminatorKind,
 };
 use rustc_middle::ty::significant_drop_order::{
     extract_component_with_significant_dtor, ty_dtor_span,
@@ -227,7 +227,10 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
         return;
     }
 
-    dump_mir(tcx, false, "lint_tail_expr_drop_order", &0 as _, body, |_, _| Ok(()));
+    if let Some(dumper) = MirDumper::new(tcx, "lint_tail_expr_drop_order", body) {
+        dumper.dump_mir(body);
+    }
+
     let locals_with_user_names = collect_user_names(body);
     let is_closure_like = tcx.is_closure_like(def_id.to_def_id());
 
diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs
index 5b3ddcc777b..ab09cdf787e 100644
--- a/compiler/rustc_mir_transform/src/pass_manager.rs
+++ b/compiler/rustc_mir_transform/src/pass_manager.rs
@@ -2,7 +2,7 @@ use std::cell::RefCell;
 use std::collections::hash_map::Entry;
 
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
-use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase};
+use rustc_middle::mir::{Body, MirDumper, MirPhase, RuntimePhase};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use tracing::trace;
@@ -281,16 +281,22 @@ fn run_passes_inner<'tcx>(
         let lint = tcx.sess.opts.unstable_opts.lint_mir;
 
         for pass in passes {
-            let name = pass.name();
+            let pass_name = pass.name();
 
             if !should_run_pass(tcx, *pass, optimizations) {
                 continue;
             };
 
-            let dump_enabled = pass.is_mir_dump_enabled();
+            let dumper = if pass.is_mir_dump_enabled()
+                && let Some(dumper) = MirDumper::new(tcx, pass_name, body)
+            {
+                Some(dumper.set_show_pass_num().set_disambiguator(&"before"))
+            } else {
+                None
+            };
 
-            if dump_enabled {
-                dump_mir_for_pass(tcx, body, name, false);
+            if let Some(dumper) = dumper.as_ref() {
+                dumper.dump_mir(body);
             }
 
             if let Some(prof_arg) = &prof_arg {
@@ -302,14 +308,15 @@ fn run_passes_inner<'tcx>(
                 pass.run_pass(tcx, body);
             }
 
-            if dump_enabled {
-                dump_mir_for_pass(tcx, body, name, true);
+            if let Some(dumper) = dumper {
+                dumper.set_disambiguator(&"after").dump_mir(body);
             }
+
             if validate {
-                validate_body(tcx, body, format!("after pass {name}"));
+                validate_body(tcx, body, format!("after pass {pass_name}"));
             }
             if lint {
-                lint_body(tcx, body, format!("after pass {name}"));
+                lint_body(tcx, body, format!("after pass {pass_name}"));
             }
 
             body.pass_count += 1;
@@ -345,18 +352,9 @@ pub(super) fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when
     validate::Validator { when }.run_pass(tcx, body);
 }
 
-fn dump_mir_for_pass<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, pass_name: &str, is_after: bool) {
-    mir::dump_mir(
-        tcx,
-        true,
-        pass_name,
-        if is_after { &"after" } else { &"before" },
-        body,
-        |_, _| Ok(()),
-    );
-}
-
 pub(super) fn dump_mir_for_phase_change<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
     assert_eq!(body.pass_count, 0);
-    mir::dump_mir(tcx, true, body.phase.name(), &"after", body, |_, _| Ok(()))
+    if let Some(dumper) = MirDumper::new(tcx, body.phase.name(), body) {
+        dumper.set_show_pass_num().set_disambiguator(&"after").dump_mir(body)
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index c6760b3583f..bca8ffb693b 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -1242,14 +1242,12 @@ fn build_construct_coroutine_by_move_shim<'tcx>(
 
     let body =
         new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span);
-    dump_mir(
-        tcx,
-        false,
-        if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" },
-        &0,
-        &body,
-        |_, _| Ok(()),
-    );
+
+    let pass_name =
+        if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" };
+    if let Some(dumper) = MirDumper::new(tcx, pass_name, &body) {
+        dumper.dump_mir(&body);
+    }
 
     body
 }
diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml
index 78266d3c6d8..09a55f0b5f8 100644
--- a/compiler/rustc_monomorphize/Cargo.toml
+++ b/compiler/rustc_monomorphize/Cargo.toml
@@ -16,6 +16,6 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 serde = "1"
-serde_json.workspace = true
-tracing.workspace = true
+serde_json = "1"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 6a836442c32..cffeb6f9807 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -1535,7 +1535,20 @@ impl<'v> RootCollector<'_, 'v> {
     fn process_nested_body(&mut self, def_id: LocalDefId) {
         match self.tcx.def_kind(def_id) {
             DefKind::Closure => {
-                if self.strategy == MonoItemCollectionStrategy::Eager
+                // for 'pub async fn foo(..)' also trying to monomorphize foo::{closure}
+                let is_pub_fn_coroutine =
+                    match *self.tcx.type_of(def_id).instantiate_identity().kind() {
+                        ty::Coroutine(cor_id, _args) => {
+                            let tcx = self.tcx;
+                            let parent_id = tcx.parent(cor_id);
+                            tcx.def_kind(parent_id) == DefKind::Fn
+                                && tcx.asyncness(parent_id).is_async()
+                                && tcx.visibility(parent_id).is_public()
+                        }
+                        ty::Closure(..) | ty::CoroutineClosure(..) => false,
+                        _ => unreachable!(),
+                    };
+                if (self.strategy == MonoItemCollectionStrategy::Eager || is_pub_fn_coroutine)
                     && !self
                         .tcx
                         .generics_of(self.tcx.typeck_root_def_id(def_id.to_def_id()))
diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml
index 43db90c08f3..05bcabad02f 100644
--- a/compiler/rustc_next_trait_solver/Cargo.toml
+++ b/compiler/rustc_next_trait_solver/Cargo.toml
@@ -5,13 +5,13 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-derive-where.workspace = true
+derive-where = "1.2.7"
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
 rustc_index = { path = "../rustc_index", default-features = false }
 rustc_macros = { path = "../rustc_macros", optional = true }
 rustc_type_ir = { path = "../rustc_type_ir", default-features = false }
 rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml
index 7cb4ae7ff5f..6d738a10371 100644
--- a/compiler/rustc_parse/Cargo.toml
+++ b/compiler/rustc_parse/Cargo.toml
@@ -5,8 +5,8 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
-rustc-literal-escaper.workspace = true
+bitflags = "2.4.1"
+rustc-literal-escaper = "0.0.5"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_data_structures = { path = "../rustc_data_structures" }
@@ -18,8 +18,8 @@ rustc_lexer = { path = "../rustc_lexer" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 unicode-normalization = "0.1.11"
 unicode-width = "0.2.0"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 9754691a0b9..c4d30b3d328 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -1516,7 +1516,7 @@ impl<'a> Parser<'a> {
                 || self.check_noexpect(&token::DotDotDot)
                 || self.check_keyword(exp!(Underscore))
             {
-                etc = PatFieldsRest::Rest;
+                etc = PatFieldsRest::Rest(self.token.span);
                 let mut etc_sp = self.token.span;
                 if first_etc_and_maybe_comma_span.is_none() {
                     if let Some(comma_tok) =
diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml
index 645e34b6e32..d178fcda1fb 100644
--- a/compiler/rustc_parse_format/Cargo.toml
+++ b/compiler/rustc_parse_format/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-rustc-literal-escaper.workspace = true
+rustc-literal-escaper = "0.0.5"
 rustc_lexer = { path = "../rustc_lexer" }
 # tidy-alphabetical-end
 
diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml
index c74608a6146..ba81ef3103b 100644
--- a/compiler/rustc_passes/Cargo.toml
+++ b/compiler/rustc_passes/Cargo.toml
@@ -24,5 +24,5 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 801a533c943..ae16a51bc69 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -1583,7 +1583,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
         });
 
         let can_remove = match pat.kind {
-            hir::PatKind::Struct(_, fields, true) => {
+            hir::PatKind::Struct(_, fields, Some(_)) => {
                 // if all fields are shorthand, remove the struct field, otherwise, mark with _ as prefix
                 fields.iter().all(|f| f.is_shorthand)
             }
diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml
index e4909ab6d16..a59f7bbeb9e 100644
--- a/compiler/rustc_pattern_analysis/Cargo.toml
+++ b/compiler/rustc_pattern_analysis/Cargo.toml
@@ -5,9 +5,9 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-rustc-hash.workspace = true
+rustc-hash = "2.0.0"
 rustc_abi = { path = "../rustc_abi", optional = true }
-rustc_apfloat.workspace = true
+rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena", optional = true }
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
 rustc_errors = { path = "../rustc_errors", optional = true }
@@ -19,7 +19,7 @@ rustc_middle = { path = "../rustc_middle", optional = true }
 rustc_session = { path = "../rustc_session", optional = true }
 rustc_span = { path = "../rustc_span", optional = true }
 smallvec = { version = "1.8.1", features = ["union"] }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [dev-dependencies]
diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml
index 7de58132e13..c8bfdb91304 100644
--- a/compiler/rustc_privacy/Cargo.toml
+++ b/compiler/rustc_privacy/Cargo.toml
@@ -15,5 +15,5 @@ rustc_middle = { path = "../rustc_middle" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_ty_utils = { path = "../rustc_ty_utils" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_proc_macro/Cargo.toml b/compiler/rustc_proc_macro/Cargo.toml
index 99e7fc7abc6..beb95aa3b52 100644
--- a/compiler/rustc_proc_macro/Cargo.toml
+++ b/compiler/rustc_proc_macro/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
 
 [dependencies]
 # tidy-alphabetical-start
-rustc-literal-escaper.workspace = true
+rustc-literal-escaper = "0.0.5"
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_public/Cargo.toml b/compiler/rustc_public/Cargo.toml
index e67e4fe6739..70af30c1a5f 100644
--- a/compiler/rustc_public/Cargo.toml
+++ b/compiler/rustc_public/Cargo.toml
@@ -12,9 +12,9 @@ rustc_public_bridge = { path = "../rustc_public_bridge" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-scoped-tls.workspace = true
+scoped-tls = "1.0"
 serde = { version = "1.0.125", features = [ "derive" ] }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml
index 3d5cf0eb72d..e5cceacf15d 100644
--- a/compiler/rustc_query_impl/Cargo.toml
+++ b/compiler/rustc_query_impl/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-measureme.workspace = true 
+measureme = "12.0.1"
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_hashes = { path = "../rustc_hashes" }
 rustc_hir = { path = "../rustc_hir" }
@@ -15,5 +15,5 @@ rustc_query_system = { path = "../rustc_query_system" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml
index 0df933bc81c..7480ba03474 100644
--- a/compiler/rustc_query_system/Cargo.toml
+++ b/compiler/rustc_query_system/Cargo.toml
@@ -21,7 +21,7 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_thread_pool = { path = "../rustc_thread_pool" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [dependencies.hashbrown]
diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml
index 4da4c0840df..eb98a6e85c0 100644
--- a/compiler/rustc_resolve/Cargo.toml
+++ b/compiler/rustc_resolve/Cargo.toml
@@ -5,9 +5,9 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
-indexmap.workspace = true
-itertools.workspace = true
+bitflags = "2.4.1"
+indexmap = "2.4.0"
+itertools = "0.12"
 pulldown-cmark = { version = "0.11", features = ["html"], default-features = false }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
@@ -27,6 +27,6 @@ rustc_query_system = { path = "../rustc_query_system" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index f75a625a279..fa3c06059b3 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1008,16 +1008,13 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
                         let msg = format!("extern crate `{ident}` already in extern prelude");
                         self.r.tcx.dcx().span_delayed_bug(item.span, msg);
                     } else {
-                        entry.item_binding = Some(imported_binding);
-                        entry.introduced_by_item = orig_name.is_some();
+                        entry.item_binding = Some((imported_binding, orig_name.is_some()));
                     }
                     entry
                 }
                 Entry::Vacant(vacant) => vacant.insert(ExternPreludeEntry {
-                    item_binding: Some(imported_binding),
-                    flag_binding: Cell::new(None),
-                    only_item: true,
-                    introduced_by_item: true,
+                    item_binding: Some((imported_binding, true)),
+                    flag_binding: None,
                 }),
             };
         }
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index 11d93a58ae2..50a1ad23a54 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -204,7 +204,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> {
                 .r
                 .extern_prelude
                 .get(&Macros20NormalizedIdent::new(extern_crate.ident))
-                .is_none_or(|entry| entry.introduced_by_item)
+                .is_none_or(|entry| entry.introduced_by_item())
             {
                 continue;
             }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 337e7d2dd86..5dfc4292a38 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -322,7 +322,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         let from_item = self
             .extern_prelude
             .get(&Macros20NormalizedIdent::new(ident))
-            .is_none_or(|entry| entry.introduced_by_item);
+            .is_none_or(|entry| entry.introduced_by_item());
         // Only suggest removing an import if both bindings are to the same def, if both spans
         // aren't dummy spans. Further, if both bindings are imports, then the ident must have
         // been introduced by an item.
@@ -1845,7 +1845,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         let AmbiguityError { kind, ident, b1, b2, misc1, misc2, .. } = *ambiguity_error;
         let extern_prelude_ambiguity = || {
             self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)).is_some_and(|entry| {
-                entry.item_binding == Some(b1) && entry.flag_binding.get() == Some(b2)
+                entry.item_binding.map(|(b, _)| b) == Some(b1)
+                    && entry.flag_binding.as_ref().and_then(|pb| pb.get().binding()) == Some(b2)
             })
         };
         let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() {
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index d4f7fb276a9..6a12fafcb1e 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -3922,7 +3922,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
 
     fn record_patterns_with_skipped_bindings(&mut self, pat: &Pat, rest: &ast::PatFieldsRest) {
         match rest {
-            ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) => {
+            ast::PatFieldsRest::Rest(_) | ast::PatFieldsRest::Recovered(_) => {
                 // Record that the pattern doesn't introduce all the bindings it could.
                 if let Some(partial_res) = self.r.partial_res_map.get(&pat.id)
                     && let Some(res) = partial_res.full_res()
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 2afb52ef4d4..9674c0356c2 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -32,7 +32,7 @@ use std::sync::Arc;
 use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
 use effective_visibilities::EffectiveVisibilitiesVisitor;
 use errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst};
-use imports::{Import, ImportData, ImportKind, NameResolution};
+use imports::{Import, ImportData, ImportKind, NameResolution, PendingBinding};
 use late::{
     ForwardGenericParamBanReason, HasGenericParams, PathSource, PatternSource,
     UnnecessaryQualification,
@@ -1025,18 +1025,26 @@ impl<'ra> NameBindingData<'ra> {
     }
 }
 
-#[derive(Default, Clone)]
 struct ExternPreludeEntry<'ra> {
     /// Binding from an `extern crate` item.
-    item_binding: Option<NameBinding<'ra>>,
+    /// The boolean flag is true is `item_binding` is non-redundant, happens either when
+    /// `flag_binding` is `None`, or when `extern crate` introducing `item_binding` used renaming.
+    item_binding: Option<(NameBinding<'ra>, /* introduced by item */ bool)>,
     /// Binding from an `--extern` flag, lazily populated on first use.
-    flag_binding: Cell<Option<NameBinding<'ra>>>,
-    /// There was no `--extern` flag introducing this name,
-    /// `flag_binding` doesn't need to be populated.
-    only_item: bool,
-    /// `item_binding` is non-redundant, happens either when `only_item` is true,
-    /// or when `extern crate` introducing `item_binding` used renaming.
-    introduced_by_item: bool,
+    flag_binding: Option<Cell<PendingBinding<'ra>>>,
+}
+
+impl ExternPreludeEntry<'_> {
+    fn introduced_by_item(&self) -> bool {
+        matches!(self.item_binding, Some((_, true)))
+    }
+
+    fn flag() -> Self {
+        ExternPreludeEntry {
+            item_binding: None,
+            flag_binding: Some(Cell::new(PendingBinding::Pending)),
+        }
+    }
 }
 
 struct DeriveData {
@@ -1528,7 +1536,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     && let name = Symbol::intern(name)
                     && name.can_be_raw()
                 {
-                    Some((Macros20NormalizedIdent::with_dummy_span(name), Default::default()))
+                    let ident = Macros20NormalizedIdent::with_dummy_span(name);
+                    Some((ident, ExternPreludeEntry::flag()))
                 } else {
                     None
                 }
@@ -1536,11 +1545,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             .collect();
 
         if !attr::contains_name(attrs, sym::no_core) {
-            extern_prelude
-                .insert(Macros20NormalizedIdent::with_dummy_span(sym::core), Default::default());
+            let ident = Macros20NormalizedIdent::with_dummy_span(sym::core);
+            extern_prelude.insert(ident, ExternPreludeEntry::flag());
             if !attr::contains_name(attrs, sym::no_std) {
-                extern_prelude
-                    .insert(Macros20NormalizedIdent::with_dummy_span(sym::std), Default::default());
+                let ident = Macros20NormalizedIdent::with_dummy_span(sym::std);
+                extern_prelude.insert(ident, ExternPreludeEntry::flag());
             }
         }
 
@@ -2062,12 +2071,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             }
             // Avoid marking `extern crate` items that refer to a name from extern prelude,
             // but not introduce it, as used if they are accessed from lexical scope.
-            if used == Used::Scope {
-                if let Some(entry) = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)) {
-                    if !entry.introduced_by_item && entry.item_binding == Some(used_binding) {
-                        return;
-                    }
-                }
+            if used == Used::Scope
+                && let Some(entry) = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident))
+                && entry.item_binding == Some((used_binding, false))
+            {
+                return;
             }
             let old_used = self.import_use_map.entry(import).or_insert(used);
             if *old_used < used {
@@ -2226,7 +2234,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         finalize: bool,
     ) -> Option<NameBinding<'ra>> {
         let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident));
-        entry.and_then(|entry| entry.item_binding).map(|binding| {
+        entry.and_then(|entry| entry.item_binding).map(|(binding, _)| {
             if finalize {
                 self.get_mut().record_use(ident, binding, Used::Scope);
             }
@@ -2236,31 +2244,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
 
     fn extern_prelude_get_flag(&self, ident: Ident, finalize: bool) -> Option<NameBinding<'ra>> {
         let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident));
-        entry.and_then(|entry| match entry.flag_binding.get() {
-            Some(binding) => {
-                if finalize {
-                    self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span);
+        entry.and_then(|entry| entry.flag_binding.as_ref()).and_then(|flag_binding| {
+            let binding = match flag_binding.get() {
+                PendingBinding::Ready(binding) => {
+                    if finalize {
+                        self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span);
+                    }
+                    binding
                 }
-                Some(binding)
-            }
-            None if entry.only_item => None,
-            None => {
-                let crate_id = if finalize {
-                    self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span)
-                } else {
-                    self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name)
-                };
-                match crate_id {
-                    Some(crate_id) => {
+                PendingBinding::Pending => {
+                    let crate_id = if finalize {
+                        self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span)
+                    } else {
+                        self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name)
+                    };
+                    crate_id.map(|crate_id| {
                         let res = Res::Def(DefKind::Mod, crate_id.as_def_id());
-                        let binding =
-                            self.arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT);
-                        entry.flag_binding.set(Some(binding));
-                        Some(binding)
-                    }
-                    None => finalize.then_some(self.dummy_binding),
+                        self.arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT)
+                    })
                 }
-            }
+            };
+            flag_binding.set(PendingBinding::Ready(binding));
+            binding.or_else(|| finalize.then_some(self.dummy_binding))
         })
     }
 
diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml
index 3dbeaa435f3..9069d2c233d 100644
--- a/compiler/rustc_sanitizers/Cargo.toml
+++ b/compiler/rustc_sanitizers/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
+bitflags = "2.5.0"
 rustc_abi = { path = "../rustc_abi" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_hir = { path = "../rustc_hir" }
@@ -13,6 +13,6 @@ rustc_middle = { path = "../rustc_middle" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
-tracing.workspace = true
+tracing = "0.1"
 twox-hash = "1.6.3"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml
index 853f87ebed9..948242352e7 100644
--- a/compiler/rustc_serialize/Cargo.toml
+++ b/compiler/rustc_serialize/Cargo.toml
@@ -5,14 +5,14 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-indexmap.workspace = true
+indexmap = "2.0.0"
 rustc_hashes = { path = "../rustc_hashes" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
+thin-vec = "0.2.12"
 # tidy-alphabetical-end
 
 [dev-dependencies]
 # tidy-alphabetical-start
 rustc_macros = { path = "../rustc_macros" }
-tempfile.workspace = true
+tempfile = "3.2"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index 5870fb29ae8..60d56b1b808 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
+bitflags = "2.4.1"
 getopts = "0.2"
 rand = "0.9.0"
 rustc_abi = { path = "../rustc_abi" }
@@ -23,12 +23,14 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 termize = "0.2"
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [target.'cfg(unix)'.dependencies]
+# FIXME: Remove this pin once this rustix issue is resolved
+# https://github.com/bytecodealliance/rustix/issues/1496
 # tidy-alphabetical-start
-libc.workspace = true
+libc = "=0.2.174"
 # tidy-alphabetical-end
 
 [target.'cfg(windows)'.dependencies.windows]
diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml
index da6f2101d23..43a2d692577 100644
--- a/compiler/rustc_span/Cargo.toml
+++ b/compiler/rustc_span/Cargo.toml
@@ -6,8 +6,8 @@ edition = "2024"
 [dependencies]
 # tidy-alphabetical-start
 blake3 = "1.5.2"
-derive-where.workspace = true
-indexmap.workspace = true
+derive-where = "1.2.7"
+indexmap = { version = "2.0.0" }
 itoa = "1.0"
 md5 = { package = "md-5", version = "0.10.0" }
 rustc_arena = { path = "../rustc_arena" }
@@ -16,9 +16,9 @@ rustc_hashes = { path = "../rustc_hashes" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
-scoped-tls.workspace = true
+scoped-tls = "1.0"
 sha1 = "0.10.0"
 sha2 = "0.10.1"
-tracing.workspace = true
+tracing = "0.1"
 unicode-width = "0.2.0"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml
index 0ceac4b3e1b..0df9c7682bf 100644
--- a/compiler/rustc_symbol_mangling/Cargo.toml
+++ b/compiler/rustc_symbol_mangling/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2024"
 [dependencies]
 # tidy-alphabetical-start
 punycode = "0.4.0"
-rustc-demangle.workspace = true
+rustc-demangle = "0.1.21"
 rustc_abi = { path = "../rustc_abi" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
@@ -15,5 +15,5 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml
index ed59ee2a575..7fabb227d9a 100644
--- a/compiler/rustc_target/Cargo.toml
+++ b/compiler/rustc_target/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
+bitflags = "2.4.1"
 object = { version = "0.37.0", default-features = false, features = ["elf", "macho"] }
 rustc_abi = { path = "../rustc_abi" }
 rustc_data_structures = { path = "../rustc_data_structures" }
@@ -16,8 +16,8 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 serde = "1.0.219"
 serde_derive = "1.0.219"
-serde_json.workspace = true
+serde_json = "1.0.59"
 serde_path_to_error = "0.1.17"
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs
index 126f0251239..05783fde1ad 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs
@@ -11,7 +11,7 @@ pub(crate) fn target() -> Target {
             description: Some("ARM64 Trusty".into()),
             tier: Some(3),
             host_tools: Some(false),
-            std: Some(false),
+            std: Some(true),
         },
         pointer_width: 64,
         data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
diff --git a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs
index 8f93523909e..7292412a18d 100644
--- a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs
@@ -20,9 +20,9 @@ pub(crate) fn target() -> Target {
         llvm_target: "arm64ec-pc-windows-msvc".into(),
         metadata: TargetMetadata {
             description: Some("Arm64EC Windows MSVC".into()),
-            tier: Some(3),
+            tier: Some(2),
             host_tools: Some(false),
-            std: None, // ?
+            std: Some(true),
         },
         pointer_width: 64,
         data_layout:
diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs
index 31d492e83cd..2b0b0e1d117 100644
--- a/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs
+++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs
@@ -13,7 +13,7 @@ pub(crate) fn target() -> Target {
             description: Some("Armv7-A Trusty".into()),
             tier: Some(3),
             host_tools: Some(false),
-            std: Some(false),
+            std: Some(true),
         },
         pointer_width: 32,
         data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs
index c7b002bc9bb..dbcb5fd4e11 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs
@@ -12,7 +12,7 @@ pub(crate) fn target() -> Target {
             description: Some("x86_64 Trusty".into()),
             tier: Some(3),
             host_tools: Some(false),
-            std: Some(false),
+            std: Some(true),
         },
         pointer_width: 64,
         data_layout:
diff --git a/compiler/rustc_thread_pool/Cargo.toml b/compiler/rustc_thread_pool/Cargo.toml
index 8e8c6469512..c92984470b7 100644
--- a/compiler/rustc_thread_pool/Cargo.toml
+++ b/compiler/rustc_thread_pool/Cargo.toml
@@ -20,10 +20,10 @@ smallvec = "1.8.1"
 [dev-dependencies]
 rand = "0.9"
 rand_xorshift = "0.4"
-scoped-tls.workspace = true
+scoped-tls = "1.0"
 
 [target.'cfg(unix)'.dev-dependencies]
-libc.workspace = true
+libc = "0.2"
 
 [[test]]
 name = "stack_overflow_crash"
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index 6b79b98d1bf..1071105522d 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools.workspace = true
+itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
@@ -21,6 +21,6 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2"
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index d768e0bf63f..bc984f30472 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -3,7 +3,8 @@ use std::borrow::Cow;
 use std::path::PathBuf;
 
 use rustc_abi::ExternAbi;
-use rustc_ast::TraitObjectSyntax;
+use rustc_ast::ast::LitKind;
+use rustc_ast::{LitIntType, TraitObjectSyntax};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::codes::*;
@@ -280,6 +281,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             (suggested, noted_missing_impl) = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
                         }
 
+                        suggested |= self.detect_negative_literal(
+                            &obligation,
+                            main_trait_predicate,
+                            &mut err,
+                        );
+
                         if let Some(ret_span) = self.return_type_span(&obligation) {
                             if is_try_conversion {
                                 let ty = self.tcx.short_string(
@@ -950,6 +957,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         Ok(())
     }
 
+    fn detect_negative_literal(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+        err: &mut Diag<'_>,
+    ) -> bool {
+        if let ObligationCauseCode::UnOp { hir_id, .. } = obligation.cause.code()
+            && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id)
+            && let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = expr.kind
+            && let hir::ExprKind::Lit(lit) = inner.kind
+            && let LitKind::Int(_, LitIntType::Unsuffixed) = lit.node
+        {
+            err.span_suggestion_verbose(
+                lit.span.shrink_to_hi(),
+                "consider specifying an integer type that can be negative",
+                match trait_pred.skip_binder().self_ty().kind() {
+                    ty::Uint(ty::UintTy::Usize) => "isize",
+                    ty::Uint(ty::UintTy::U8) => "i8",
+                    ty::Uint(ty::UintTy::U16) => "i16",
+                    ty::Uint(ty::UintTy::U32) => "i32",
+                    ty::Uint(ty::UintTy::U64) => "i64",
+                    ty::Uint(ty::UintTy::U128) => "i128",
+                    _ => "i64",
+                }
+                .to_string(),
+                Applicability::MaybeIncorrect,
+            );
+            return true;
+        }
+        false
+    }
+
     /// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`,
     /// identify those method chain sub-expressions that could or could not have been annotated
     /// with `?`.
@@ -2730,9 +2769,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         suggested: bool,
     ) {
         let body_def_id = obligation.cause.body_id;
-        let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } =
-            obligation.cause.code()
-        {
+        let span = if let ObligationCauseCode::BinOp { rhs_span, .. } = obligation.cause.code() {
             *rhs_span
         } else {
             span
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index aa153d3607b..40a27c9aebe 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -554,7 +554,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 return true;
             }
         } else if let (
-            ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id: Some(rhs_hir_id), .. },
+            ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. },
             predicate,
         ) = code.peel_derives_with_predicate()
             && let Some(typeck_results) = &self.typeck_results
@@ -2801,6 +2801,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             | ObligationCauseCode::QuestionMark
             | ObligationCauseCode::CheckAssociatedTypeBounds { .. }
             | ObligationCauseCode::LetElse
+            | ObligationCauseCode::UnOp { .. }
             | ObligationCauseCode::BinOp { .. }
             | ObligationCauseCode::AscribeUserTypeProvePredicate(..)
             | ObligationCauseCode::AlwaysApplicableImpl
@@ -3839,9 +3840,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let rhs_span = match obligation.cause.code() {
-            ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => {
-                span
-            }
+            ObligationCauseCode::BinOp { rhs_span, rhs_is_lit, .. } if *rhs_is_lit => rhs_span,
             _ => return,
         };
         if let ty::Float(_) = trait_pred.skip_binder().self_ty().kind()
@@ -5108,16 +5107,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let tcx = self.tcx;
         let predicate = predicate.upcast(tcx);
         match *cause_code {
-            ObligationCauseCode::BinOp {
-                lhs_hir_id,
-                rhs_hir_id: Some(rhs_hir_id),
-                rhs_span: Some(rhs_span),
-                ..
-            } if let Some(typeck_results) = &self.typeck_results
-                && let hir::Node::Expr(lhs) = tcx.hir_node(lhs_hir_id)
-                && let hir::Node::Expr(rhs) = tcx.hir_node(rhs_hir_id)
-                && let Some(lhs_ty) = typeck_results.expr_ty_opt(lhs)
-                && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) =>
+            ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, rhs_span, .. }
+                if let Some(typeck_results) = &self.typeck_results
+                    && let hir::Node::Expr(lhs) = tcx.hir_node(lhs_hir_id)
+                    && let hir::Node::Expr(rhs) = tcx.hir_node(rhs_hir_id)
+                    && let Some(lhs_ty) = typeck_results.expr_ty_opt(lhs)
+                    && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) =>
             {
                 if let Some(pred) = predicate.as_trait_clause()
                     && tcx.is_lang_item(pred.def_id(), LangItem::PartialEq)
diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml
index 24360a94cc7..9f40f4d5c23 100644
--- a/compiler/rustc_traits/Cargo.toml
+++ b/compiler/rustc_traits/Cargo.toml
@@ -10,5 +10,5 @@ rustc_infer = { path = "../rustc_infer" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_span = { path = "../rustc_span" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml
index 2ac2b46cc58..e61717e5e9c 100644
--- a/compiler/rustc_transmute/Cargo.toml
+++ b/compiler/rustc_transmute/Cargo.toml
@@ -11,12 +11,12 @@ rustc_hir = { path = "../rustc_hir", optional = true }
 rustc_middle = { path = "../rustc_middle", optional = true }
 rustc_span = { path = "../rustc_span", optional = true }
 smallvec = "1.8.1"
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [dev-dependencies]
 # tidy-alphabetical-start
-itertools.workspace = true
+itertools = "0.12"
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml
index 394cde5a323..ce08b300cc8 100644
--- a/compiler/rustc_ty_utils/Cargo.toml
+++ b/compiler/rustc_ty_utils/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools.workspace = true
+itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
@@ -20,5 +20,5 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
-tracing.workspace = true
+tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_ty_utils/src/nested_bodies.rs b/compiler/rustc_ty_utils/src/nested_bodies.rs
index 7c74d8eb635..11dfbad7dbb 100644
--- a/compiler/rustc_ty_utils/src/nested_bodies.rs
+++ b/compiler/rustc_ty_utils/src/nested_bodies.rs
@@ -22,9 +22,11 @@ impl<'tcx> Visitor<'tcx> for NestedBodiesVisitor<'tcx> {
     fn visit_nested_body(&mut self, id: hir::BodyId) {
         let body_def_id = self.tcx.hir_body_owner_def_id(id);
         if self.tcx.typeck_root_def_id(body_def_id.to_def_id()) == self.root_def_id {
-            self.nested_bodies.push(body_def_id);
+            // We visit nested bodies before adding the current body. This
+            // means that nested bodies are always stored before their parent.
             let body = self.tcx.hir_body(id);
             self.visit_body(body);
+            self.nested_bodies.push(body_def_id);
         }
     }
 }
diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml
index 42860fa2d88..d55e9b3b1be 100644
--- a/compiler/rustc_type_ir/Cargo.toml
+++ b/compiler/rustc_type_ir/Cargo.toml
@@ -5,11 +5,11 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags.workspace = true
-derive-where.workspace = true
+bitflags = "2.4.1"
+derive-where = "1.2.7"
 ena = "0.14.3"
-indexmap.workspace = true
-rustc-hash.workspace = true
+indexmap = "2.0.0"
+rustc-hash = "2.0.0"
 rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false }
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
 rustc_error_messages = { path = "../rustc_error_messages", optional = true }
@@ -19,8 +19,8 @@ rustc_serialize = { path = "../rustc_serialize", optional = true }
 rustc_span = { path = "../rustc_span", optional = true }
 rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
 smallvec = { version = "1.8.1", default-features = false, features = ["const_generics"] }
-thin-vec.workspace = true
-tracing.workspace = true
+thin-vec = "0.2.12"
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [features]
diff --git a/compiler/rustc_type_ir_macros/Cargo.toml b/compiler/rustc_type_ir_macros/Cargo.toml
index 29a2cc89033..15a55575099 100644
--- a/compiler/rustc_type_ir_macros/Cargo.toml
+++ b/compiler/rustc_type_ir_macros/Cargo.toml
@@ -8,8 +8,8 @@ proc-macro = true
 
 [dependencies]
 # tidy-alphabetical-start
-proc-macro2.workspace = true
-quote.workspace = true
+proc-macro2 = "1"
+quote = "1"
 syn = { version = "2.0.9", features = ["full"] }
 synstructure = "0.13.0"
 # tidy-alphabetical-end
diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs
index 95d1e2069ac..c0a27775694 100644
--- a/library/core/src/ops/range.rs
+++ b/library/core/src/ops/range.rs
@@ -736,6 +736,31 @@ impl<T> Bound<T> {
     }
 }
 
+impl<T: Copy> Bound<&T> {
+    /// Map a `Bound<&T>` to a `Bound<T>` by copying the contents of the bound.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(bound_copied)]
+    ///
+    /// use std::ops::Bound::*;
+    /// use std::ops::RangeBounds;
+    ///
+    /// assert_eq!((1..12).start_bound(), Included(&1));
+    /// assert_eq!((1..12).start_bound().copied(), Included(1));
+    /// ```
+    #[unstable(feature = "bound_copied", issue = "145966")]
+    #[must_use]
+    pub fn copied(self) -> Bound<T> {
+        match self {
+            Bound::Unbounded => Bound::Unbounded,
+            Bound::Included(x) => Bound::Included(*x),
+            Bound::Excluded(x) => Bound::Excluded(*x),
+        }
+    }
+}
+
 impl<T: Clone> Bound<&T> {
     /// Map a `Bound<&T>` to a `Bound<T>` by cloning the contents of the bound.
     ///
@@ -745,8 +770,11 @@ impl<T: Clone> Bound<&T> {
     /// use std::ops::Bound::*;
     /// use std::ops::RangeBounds;
     ///
-    /// assert_eq!((1..12).start_bound(), Included(&1));
-    /// assert_eq!((1..12).start_bound().cloned(), Included(1));
+    /// let a1 = String::from("a");
+    /// let (a2, a3, a4) = (a1.clone(), a1.clone(), a1.clone());
+    ///
+    /// assert_eq!(Included(&a1), (a2..).start_bound());
+    /// assert_eq!(Included(a3), (a4..).start_bound().cloned());
     /// ```
     #[must_use = "`self` will be dropped if the result is not used"]
     #[stable(feature = "bound_cloned", since = "1.55.0")]
diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs
index ac4a2066530..c173d7f0ae0 100644
--- a/library/coretests/tests/floats/f128.rs
+++ b/library/coretests/tests/floats/f128.rs
@@ -1,18 +1,18 @@
 // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
 #![cfg(target_has_reliable_f128)]
 
-use std::f128::consts;
-
 use super::{assert_approx_eq, assert_biteq};
 
 // Note these tolerances make sense around zero, but not for more extreme exponents.
 
 /// Default tolerances. Works for values that should be near precise but not exact. Roughly
 /// the precision carried by `100 * 100`.
+#[allow(unused)]
 const TOL: f128 = 1e-12;
 
 /// For operations that are near exact, usually not involving math of different
 /// signs.
+#[allow(unused)]
 const TOL_PRECISE: f128 = 1e-28;
 
 /// First pattern over the mantissa
@@ -44,70 +44,12 @@ fn test_mul_add() {
 
 #[test]
 #[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_recip() {
-    let nan: f128 = f128::NAN;
-    let inf: f128 = f128::INFINITY;
-    let neg_inf: f128 = f128::NEG_INFINITY;
-    assert_biteq!(1.0f128.recip(), 1.0);
-    assert_biteq!(2.0f128.recip(), 0.5);
-    assert_biteq!((-0.4f128).recip(), -2.5);
-    assert_biteq!(0.0f128.recip(), inf);
+fn test_max_recip() {
     assert_approx_eq!(
         f128::MAX.recip(),
         8.40525785778023376565669454330438228902076605e-4933,
         1e-4900
     );
-    assert!(nan.recip().is_nan());
-    assert_biteq!(inf.recip(), 0.0);
-    assert_biteq!(neg_inf.recip(), -0.0);
-}
-
-#[test]
-#[cfg(not(miri))]
-#[cfg(target_has_reliable_f128_math)]
-fn test_powi() {
-    let nan: f128 = f128::NAN;
-    let inf: f128 = f128::INFINITY;
-    let neg_inf: f128 = f128::NEG_INFINITY;
-    assert_biteq!(1.0f128.powi(1), 1.0);
-    assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL);
-    assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL);
-    assert_biteq!(8.3f128.powi(0), 1.0);
-    assert!(nan.powi(2).is_nan());
-    assert_biteq!(inf.powi(3), inf);
-    assert_biteq!(neg_inf.powi(2), inf);
-}
-
-#[test]
-fn test_to_degrees() {
-    let pi: f128 = consts::PI;
-    let nan: f128 = f128::NAN;
-    let inf: f128 = f128::INFINITY;
-    let neg_inf: f128 = f128::NEG_INFINITY;
-    assert_biteq!(0.0f128.to_degrees(), 0.0);
-    assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL);
-    assert_approx_eq!(pi.to_degrees(), 180.0, TOL);
-    assert!(nan.to_degrees().is_nan());
-    assert_biteq!(inf.to_degrees(), inf);
-    assert_biteq!(neg_inf.to_degrees(), neg_inf);
-    assert_biteq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703);
-}
-
-#[test]
-fn test_to_radians() {
-    let pi: f128 = consts::PI;
-    let nan: f128 = f128::NAN;
-    let inf: f128 = f128::INFINITY;
-    let neg_inf: f128 = f128::NEG_INFINITY;
-    assert_biteq!(0.0f128.to_radians(), 0.0);
-    assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL);
-    assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL);
-    // check approx rather than exact because round trip for pi doesn't fall on an exactly
-    // representable value (unlike `f32` and `f64`).
-    assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE);
-    assert!(nan.to_radians().is_nan());
-    assert_biteq!(inf.to_radians(), inf);
-    assert_biteq!(neg_inf.to_radians(), neg_inf);
 }
 
 #[test]
diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs
index bb9c8a002fe..c12de7221ba 100644
--- a/library/coretests/tests/floats/f16.rs
+++ b/library/coretests/tests/floats/f16.rs
@@ -1,8 +1,6 @@
 // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
 #![cfg(target_has_reliable_f16)]
 
-use std::f16::consts;
-
 use super::{assert_approx_eq, assert_biteq};
 
 /// Tolerance for results on the order of 10.0e-2
@@ -50,64 +48,8 @@ fn test_mul_add() {
 
 #[test]
 #[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_recip() {
-    let nan: f16 = f16::NAN;
-    let inf: f16 = f16::INFINITY;
-    let neg_inf: f16 = f16::NEG_INFINITY;
-    assert_biteq!(1.0f16.recip(), 1.0);
-    assert_biteq!(2.0f16.recip(), 0.5);
-    assert_biteq!((-0.4f16).recip(), -2.5);
-    assert_biteq!(0.0f16.recip(), inf);
+fn test_max_recip() {
     assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4);
-    assert!(nan.recip().is_nan());
-    assert_biteq!(inf.recip(), 0.0);
-    assert_biteq!(neg_inf.recip(), -0.0);
-}
-
-#[test]
-#[cfg(not(miri))]
-#[cfg(target_has_reliable_f16_math)]
-fn test_powi() {
-    let nan: f16 = f16::NAN;
-    let inf: f16 = f16::INFINITY;
-    let neg_inf: f16 = f16::NEG_INFINITY;
-    assert_biteq!(1.0f16.powi(1), 1.0);
-    assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0);
-    assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2);
-    assert_biteq!(8.3f16.powi(0), 1.0);
-    assert!(nan.powi(2).is_nan());
-    assert_biteq!(inf.powi(3), inf);
-    assert_biteq!(neg_inf.powi(2), inf);
-}
-
-#[test]
-fn test_to_degrees() {
-    let pi: f16 = consts::PI;
-    let nan: f16 = f16::NAN;
-    let inf: f16 = f16::INFINITY;
-    let neg_inf: f16 = f16::NEG_INFINITY;
-    assert_biteq!(0.0f16.to_degrees(), 0.0);
-    assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2);
-    assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2);
-    assert!(nan.to_degrees().is_nan());
-    assert_biteq!(inf.to_degrees(), inf);
-    assert_biteq!(neg_inf.to_degrees(), neg_inf);
-    assert_biteq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703);
-}
-
-#[test]
-fn test_to_radians() {
-    let pi: f16 = consts::PI;
-    let nan: f16 = f16::NAN;
-    let inf: f16 = f16::INFINITY;
-    let neg_inf: f16 = f16::NEG_INFINITY;
-    assert_biteq!(0.0f16.to_radians(), 0.0);
-    assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0);
-    assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0);
-    assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0);
-    assert!(nan.to_radians().is_nan());
-    assert_biteq!(inf.to_radians(), inf);
-    assert_biteq!(neg_inf.to_radians(), neg_inf);
 }
 
 #[test]
diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs
index e77e44655dc..b79295f470d 100644
--- a/library/coretests/tests/floats/f32.rs
+++ b/library/coretests/tests/floats/f32.rs
@@ -1,5 +1,4 @@
 use core::f32;
-use core::f32::consts;
 
 use super::{assert_approx_eq, assert_biteq};
 
@@ -9,11 +8,6 @@ const NAN_MASK1: u32 = 0x002a_aaaa;
 /// Second pattern over the mantissa
 const NAN_MASK2: u32 = 0x0055_5555;
 
-/// Miri adds some extra errors to float functions; make sure the tests still pass.
-/// These values are purely used as a canary to test against and are thus not a stable guarantee Rust provides.
-/// They serve as a way to get an idea of the real precision of floating point operations on different platforms.
-const APPROX_DELTA: f32 = if cfg!(miri) { 1e-4 } else { 1e-6 };
-
 // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/
 #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)]
 #[test]
@@ -33,64 +27,6 @@ fn test_mul_add() {
 }
 
 #[test]
-fn test_recip() {
-    let nan: f32 = f32::NAN;
-    let inf: f32 = f32::INFINITY;
-    let neg_inf: f32 = f32::NEG_INFINITY;
-    assert_biteq!(1.0f32.recip(), 1.0);
-    assert_biteq!(2.0f32.recip(), 0.5);
-    assert_biteq!((-0.4f32).recip(), -2.5);
-    assert_biteq!(0.0f32.recip(), inf);
-    assert!(nan.recip().is_nan());
-    assert_biteq!(inf.recip(), 0.0);
-    assert_biteq!(neg_inf.recip(), -0.0);
-}
-
-#[test]
-fn test_powi() {
-    let nan: f32 = f32::NAN;
-    let inf: f32 = f32::INFINITY;
-    let neg_inf: f32 = f32::NEG_INFINITY;
-    assert_approx_eq!(1.0f32.powi(1), 1.0);
-    assert_approx_eq!((-3.1f32).powi(2), 9.61, APPROX_DELTA);
-    assert_approx_eq!(5.9f32.powi(-2), 0.028727);
-    assert_biteq!(8.3f32.powi(0), 1.0);
-    assert!(nan.powi(2).is_nan());
-    assert_biteq!(inf.powi(3), inf);
-    assert_biteq!(neg_inf.powi(2), inf);
-}
-
-#[test]
-fn test_to_degrees() {
-    let pi: f32 = consts::PI;
-    let nan: f32 = f32::NAN;
-    let inf: f32 = f32::INFINITY;
-    let neg_inf: f32 = f32::NEG_INFINITY;
-    assert_biteq!(0.0f32.to_degrees(), 0.0);
-    assert_approx_eq!((-5.8f32).to_degrees(), -332.315521);
-    assert_biteq!(pi.to_degrees(), 180.0);
-    assert!(nan.to_degrees().is_nan());
-    assert_biteq!(inf.to_degrees(), inf);
-    assert_biteq!(neg_inf.to_degrees(), neg_inf);
-    assert_biteq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703);
-}
-
-#[test]
-fn test_to_radians() {
-    let pi: f32 = consts::PI;
-    let nan: f32 = f32::NAN;
-    let inf: f32 = f32::INFINITY;
-    let neg_inf: f32 = f32::NEG_INFINITY;
-    assert_biteq!(0.0f32.to_radians(), 0.0);
-    assert_approx_eq!(154.6f32.to_radians(), 2.698279);
-    assert_approx_eq!((-332.31f32).to_radians(), -5.799903);
-    assert_biteq!(180.0f32.to_radians(), pi);
-    assert!(nan.to_radians().is_nan());
-    assert_biteq!(inf.to_radians(), inf);
-    assert_biteq!(neg_inf.to_radians(), neg_inf);
-}
-
-#[test]
 fn test_float_bits_conv() {
     assert_eq!((1f32).to_bits(), 0x3f800000);
     assert_eq!((12.5f32).to_bits(), 0x41480000);
diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs
index fea9cc19b39..a2540586328 100644
--- a/library/coretests/tests/floats/f64.rs
+++ b/library/coretests/tests/floats/f64.rs
@@ -1,5 +1,4 @@
 use core::f64;
-use core::f64::consts;
 
 use super::{assert_approx_eq, assert_biteq};
 
@@ -28,63 +27,6 @@ fn test_mul_add() {
 }
 
 #[test]
-fn test_recip() {
-    let nan: f64 = f64::NAN;
-    let inf: f64 = f64::INFINITY;
-    let neg_inf: f64 = f64::NEG_INFINITY;
-    assert_biteq!(1.0f64.recip(), 1.0);
-    assert_biteq!(2.0f64.recip(), 0.5);
-    assert_biteq!((-0.4f64).recip(), -2.5);
-    assert_biteq!(0.0f64.recip(), inf);
-    assert!(nan.recip().is_nan());
-    assert_biteq!(inf.recip(), 0.0);
-    assert_biteq!(neg_inf.recip(), -0.0);
-}
-
-#[test]
-fn test_powi() {
-    let nan: f64 = f64::NAN;
-    let inf: f64 = f64::INFINITY;
-    let neg_inf: f64 = f64::NEG_INFINITY;
-    assert_approx_eq!(1.0f64.powi(1), 1.0);
-    assert_approx_eq!((-3.1f64).powi(2), 9.61);
-    assert_approx_eq!(5.9f64.powi(-2), 0.028727);
-    assert_biteq!(8.3f64.powi(0), 1.0);
-    assert!(nan.powi(2).is_nan());
-    assert_biteq!(inf.powi(3), inf);
-    assert_biteq!(neg_inf.powi(2), inf);
-}
-
-#[test]
-fn test_to_degrees() {
-    let pi: f64 = consts::PI;
-    let nan: f64 = f64::NAN;
-    let inf: f64 = f64::INFINITY;
-    let neg_inf: f64 = f64::NEG_INFINITY;
-    assert_biteq!(0.0f64.to_degrees(), 0.0);
-    assert_approx_eq!((-5.8f64).to_degrees(), -332.315521);
-    assert_biteq!(pi.to_degrees(), 180.0);
-    assert!(nan.to_degrees().is_nan());
-    assert_biteq!(inf.to_degrees(), inf);
-    assert_biteq!(neg_inf.to_degrees(), neg_inf);
-}
-
-#[test]
-fn test_to_radians() {
-    let pi: f64 = consts::PI;
-    let nan: f64 = f64::NAN;
-    let inf: f64 = f64::INFINITY;
-    let neg_inf: f64 = f64::NEG_INFINITY;
-    assert_biteq!(0.0f64.to_radians(), 0.0);
-    assert_approx_eq!(154.6f64.to_radians(), 2.698279);
-    assert_approx_eq!((-332.31f64).to_radians(), -5.799903);
-    assert_biteq!(180.0f64.to_radians(), pi);
-    assert!(nan.to_radians().is_nan());
-    assert_biteq!(inf.to_radians(), inf);
-    assert_biteq!(neg_inf.to_radians(), neg_inf);
-}
-
-#[test]
 fn test_float_bits_conv() {
     assert_eq!((1f64).to_bits(), 0x3ff0000000000000);
     assert_eq!((12.5f64).to_bits(), 0x4029000000000000);
diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs
index 2c2a07920d0..5f59cb9cce3 100644
--- a/library/coretests/tests/floats/mod.rs
+++ b/library/coretests/tests/floats/mod.rs
@@ -1,13 +1,20 @@
 use std::num::FpCategory as Fp;
 use std::ops::{Add, Div, Mul, Rem, Sub};
 
-trait TestableFloat {
+trait TestableFloat: Sized {
     /// Unsigned int with the same size, for converting to/from bits.
     type Int;
     /// Set the default tolerance for float comparison based on the type.
     const APPROX: Self;
+    /// Allow looser tolerance for f32 on miri
+    const POWI_APPROX: Self = Self::APPROX;
+    /// Allow looser tolerance for f16
+    const _180_TO_RADIANS_APPROX: Self = Self::APPROX;
+    /// Allow for looser tolerance for f16
+    const PI_TO_DEGREES_APPROX: Self = Self::APPROX;
     const ZERO: Self;
     const ONE: Self;
+    const PI: Self;
     const MIN_POSITIVE_NORMAL: Self;
     const MAX_SUBNORMAL: Self;
     /// Smallest number
@@ -25,8 +32,11 @@ trait TestableFloat {
 impl TestableFloat for f16 {
     type Int = u16;
     const APPROX: Self = 1e-3;
+    const _180_TO_RADIANS_APPROX: Self = 1e-2;
+    const PI_TO_DEGREES_APPROX: Self = 0.125;
     const ZERO: Self = 0.0;
     const ONE: Self = 1.0;
+    const PI: Self = std::f16::consts::PI;
     const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
     const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
     const TINY: Self = Self::from_bits(0x1);
@@ -39,8 +49,13 @@ impl TestableFloat for f16 {
 impl TestableFloat for f32 {
     type Int = u32;
     const APPROX: Self = 1e-6;
+    /// Miri adds some extra errors to float functions; make sure the tests still pass.
+    /// These values are purely used as a canary to test against and are thus not a stable guarantee Rust provides.
+    /// They serve as a way to get an idea of the real precision of floating point operations on different platforms.
+    const POWI_APPROX: Self = if cfg!(miri) { 1e-4 } else { Self::APPROX };
     const ZERO: Self = 0.0;
     const ONE: Self = 1.0;
+    const PI: Self = std::f32::consts::PI;
     const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
     const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
     const TINY: Self = Self::from_bits(0x1);
@@ -55,6 +70,7 @@ impl TestableFloat for f64 {
     const APPROX: Self = 1e-6;
     const ZERO: Self = 0.0;
     const ONE: Self = 1.0;
+    const PI: Self = std::f64::consts::PI;
     const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
     const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
     const TINY: Self = Self::from_bits(0x1);
@@ -69,6 +85,7 @@ impl TestableFloat for f128 {
     const APPROX: Self = 1e-9;
     const ZERO: Self = 0.0;
     const ONE: Self = 1.0;
+    const PI: Self = std::f128::consts::PI;
     const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
     const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
     const TINY: Self = Self::from_bits(0x1);
@@ -1340,3 +1357,86 @@ float_test! {
         assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &s_nan()));
     }
 }
+
+float_test! {
+    name: recip,
+    attrs: {
+        f16: #[cfg(any(miri, target_has_reliable_f16_math))],
+        f128: #[cfg(any(miri, target_has_reliable_f128_math))],
+    },
+    test<Float> {
+        let nan: Float = Float::NAN;
+        let inf: Float = Float::INFINITY;
+        let neg_inf: Float = Float::NEG_INFINITY;
+        assert_biteq!((1.0 as Float).recip(), 1.0);
+        assert_biteq!((2.0 as Float).recip(), 0.5);
+        assert_biteq!((-0.4 as Float).recip(), -2.5);
+        assert_biteq!((0.0 as Float).recip(), inf);
+        assert!(nan.recip().is_nan());
+        assert_biteq!(inf.recip(), 0.0);
+        assert_biteq!(neg_inf.recip(), -0.0);
+    }
+}
+
+float_test! {
+    name: powi,
+    attrs: {
+        const: #[cfg(false)],
+        f16: #[cfg(all(not(miri), target_has_reliable_f16_math))],
+        f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
+    },
+    test<Float> {
+        let nan: Float = Float::NAN;
+        let inf: Float = Float::INFINITY;
+        let neg_inf: Float = Float::NEG_INFINITY;
+        assert_approx_eq!(Float::ONE.powi(1), Float::ONE);
+        assert_approx_eq!((-3.1 as Float).powi(2), 9.6100000000000005506706202140776519387, Float::POWI_APPROX);
+        assert_approx_eq!((5.9 as Float).powi(-2), 0.028727377190462507313100483690639638451);
+        assert_biteq!((8.3 as Float).powi(0), Float::ONE);
+        assert!(nan.powi(2).is_nan());
+        assert_biteq!(inf.powi(3), inf);
+        assert_biteq!(neg_inf.powi(2), inf);
+    }
+}
+
+float_test! {
+    name: to_degrees,
+    attrs: {
+        f16: #[cfg(target_has_reliable_f16)],
+        f128: #[cfg(target_has_reliable_f128)],
+    },
+    test<Float> {
+        let pi: Float = Float::PI;
+        let nan: Float = Float::NAN;
+        let inf: Float = Float::INFINITY;
+        let neg_inf: Float = Float::NEG_INFINITY;
+        assert_biteq!((0.0 as Float).to_degrees(), 0.0);
+        assert_approx_eq!((-5.8 as Float).to_degrees(), -332.31552117587745090765431723855668471);
+        assert_approx_eq!(pi.to_degrees(), 180.0, Float::PI_TO_DEGREES_APPROX);
+        assert!(nan.to_degrees().is_nan());
+        assert_biteq!(inf.to_degrees(), inf);
+        assert_biteq!(neg_inf.to_degrees(), neg_inf);
+        assert_biteq!((1.0 as Float).to_degrees(), 57.2957795130823208767981548141051703);
+    }
+}
+
+float_test! {
+    name: to_radians,
+    attrs: {
+        f16: #[cfg(target_has_reliable_f16)],
+        f128: #[cfg(target_has_reliable_f128)],
+    },
+    test<Float> {
+        let pi: Float = Float::PI;
+        let nan: Float = Float::NAN;
+        let inf: Float = Float::INFINITY;
+        let neg_inf: Float = Float::NEG_INFINITY;
+        assert_biteq!((0.0 as Float).to_radians(), 0.0);
+        assert_approx_eq!((154.6 as Float).to_radians(), 2.6982790235832334267135442069489767804);
+        assert_approx_eq!((-332.31 as Float).to_radians(), -5.7999036373023566567593094812182763013);
+        assert_approx_eq!((180.0 as Float).to_radians(), pi, Float::_180_TO_RADIANS_APPROX);
+        assert!(nan.to_radians().is_nan());
+        assert_biteq!(inf.to_radians(), inf);
+        assert_biteq!(neg_inf.to_radians(), neg_inf);
+    }
+}
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 3771699c59f..7bdc6ff7882 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -290,7 +290,6 @@
 #![feature(doc_masked)]
 #![feature(doc_notable_trait)]
 #![feature(dropck_eyepatch)]
-#![feature(extended_varargs_abi_support)]
 #![feature(f16)]
 #![feature(f128)]
 #![feature(ffi_const)]
diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs
index aec089f7e5c..f0b6068e06c 100644
--- a/library/std/src/sys/pal/unix/os.rs
+++ b/library/std/src/sys/pal/unix/os.rs
@@ -186,14 +186,24 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
     if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
 }
 
-pub type SplitPaths<'a> = impl Iterator<Item = PathBuf>;
+// This can't just be `impl Iterator` because that requires `'a` to be live on
+// drop (see #146045).
+pub type SplitPaths<'a> = iter::Map<
+    slice::Split<'a, u8, impl FnMut(&u8) -> bool + 'static>,
+    impl FnMut(&[u8]) -> PathBuf + 'static,
+>;
 
 #[define_opaque(SplitPaths)]
 pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
-    unparsed
-        .as_bytes()
-        .split(|&b| b == PATH_SEPARATOR)
-        .map(|part| PathBuf::from(OsStr::from_bytes(part)))
+    fn is_separator(&b: &u8) -> bool {
+        b == PATH_SEPARATOR
+    }
+
+    fn into_pathbuf(part: &[u8]) -> PathBuf {
+        PathBuf::from(OsStr::from_bytes(part))
+    }
+
+    unparsed.as_bytes().split(is_separator).map(into_pathbuf)
 }
 
 #[derive(Debug)]
diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs
index 38c906c1d87..c61a8ec4d20 100644
--- a/library/std/tests/floats/f32.rs
+++ b/library/std/tests/floats/f32.rs
@@ -79,8 +79,8 @@ fn test_log() {
     let nan: f32 = f32::NAN;
     let inf: f32 = f32::INFINITY;
     let neg_inf: f32 = f32::NEG_INFINITY;
-    assert_approx_eq!(10.0f32.log(10.0), 1.0);
-    assert_approx_eq!(2.3f32.log(3.5), 0.664858);
+    assert_approx_eq!(10.0f32.log(10.0), 1.0, APPROX_DELTA);
+    assert_approx_eq!(2.3f32.log(3.5), 0.664858, APPROX_DELTA);
     assert_approx_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0, APPROX_DELTA);
     assert!(1.0f32.log(1.0).is_nan());
     assert!(1.0f32.log(-13.9).is_nan());
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index bebae893ee7..a604e7c0585 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -8,8 +8,8 @@ use crate::core::build_steps::compile::{
 };
 use crate::core::build_steps::tool;
 use crate::core::build_steps::tool::{
-    COMPILETEST_ALLOW_FEATURES, SourceType, ToolTargetBuildMode, get_tool_target_compiler,
-    prepare_tool_cargo,
+    COMPILETEST_ALLOW_FEATURES, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, ToolTargetBuildMode,
+    get_tool_target_compiler, prepare_tool_cargo,
 };
 use crate::core::builder::{
     self, Alias, Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
@@ -791,7 +791,7 @@ tool_check_step!(MiroptTestTools {
 tool_check_step!(TestFloatParse {
     path: "src/tools/test-float-parse",
     mode: |_builder| Mode::ToolStd,
-    allow_features: tool::TestFloatParse::ALLOW_FEATURES
+    allow_features: TEST_FLOAT_PARSE_ALLOW_FEATURES
 });
 tool_check_step!(FeaturesStatusDump {
     path: "src/tools/features-status-dump",
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 9ef1fee1fec..0789eefa894 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -791,7 +791,11 @@ fn doc_std(
 }
 
 /// Prepare a compiler that will be able to document something for `target` at `stage`.
-fn prepare_doc_compiler(builder: &Builder<'_>, target: TargetSelection, stage: u32) -> Compiler {
+pub fn prepare_doc_compiler(
+    builder: &Builder<'_>,
+    target: TargetSelection,
+    stage: u32,
+) -> Compiler {
     assert!(stage > 0, "Cannot document anything in stage 0");
     let build_compiler = builder.compiler(stage - 1, builder.host_target);
     builder.std(build_compiler, target);
@@ -1289,6 +1293,8 @@ impl Step for RustcBook {
         // functional sysroot.
         builder.std(self.build_compiler, self.target);
         let mut cmd = builder.tool_cmd(Tool::LintDocs);
+        cmd.arg("--build-rustc-stage");
+        cmd.arg(self.build_compiler.stage.to_string());
         cmd.arg("--src");
         cmd.arg(builder.src.join("compiler"));
         cmd.arg("--out");
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 26b4aaa8b5b..95be5360b0b 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -12,14 +12,14 @@ use std::{env, fs, iter};
 use build_helper::exit;
 
 use crate::core::build_steps::compile::{Std, run_cargo};
-use crate::core::build_steps::doc::DocumentationFormat;
+use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler};
 use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags};
 use crate::core::build_steps::llvm::get_llvm_version;
 use crate::core::build_steps::run::get_completion_paths;
 use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget;
 use crate::core::build_steps::tool::{
-    self, COMPILETEST_ALLOW_FEATURES, RustcPrivateCompilers, SourceType, Tool, ToolTargetBuildMode,
-    get_tool_target_compiler,
+    self, COMPILETEST_ALLOW_FEATURES, RustcPrivateCompilers, SourceType,
+    TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, ToolTargetBuildMode, get_tool_target_compiler,
 };
 use crate::core::build_steps::toolstate::ToolState;
 use crate::core::build_steps::{compile, dist, llvm};
@@ -96,7 +96,14 @@ impl Step for CrateBootstrap {
         );
 
         let crate_name = path.rsplit_once('/').unwrap().1;
-        run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder);
+        run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder, Mode::ToolBootstrap);
+    }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::test("crate-bootstrap", self.host)
+                .with_metadata(self.path.as_path().to_string_lossy().to_string()),
+        )
     }
 }
 
@@ -146,7 +153,15 @@ You can skip linkcheck with --skip src/tools/linkchecker"
             SourceType::InTree,
             &[],
         );
-        run_cargo_test(cargo, &[], &[], "linkchecker self tests", bootstrap_host, builder);
+        run_cargo_test(
+            cargo,
+            &[],
+            &[],
+            "linkchecker self tests",
+            bootstrap_host,
+            builder,
+            Mode::ToolBootstrap,
+        );
 
         if builder.doc_tests == DocTests::No {
             return;
@@ -173,6 +188,10 @@ You can skip linkcheck with --skip src/tools/linkchecker"
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Linkcheck { host: run.target });
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::test("link-check", self.host))
+    }
 }
 
 fn check_if_tidy_is_installed(builder: &Builder<'_>) -> bool {
@@ -221,6 +240,10 @@ impl Step for HtmlCheck {
             .arg(builder.doc_out(self.target))
             .run(builder);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::test("html-check", self.target))
+    }
 }
 
 /// Builds cargo and then runs the `src/tools/cargotest` tool, which checks out
@@ -399,6 +422,10 @@ impl Step for Cargo {
         let _time = helpers::timeit(builder);
         add_flags_and_try_run_tests(builder, &mut cargo);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::test("cargo", self.host).built_by(self.build_compiler))
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -455,7 +482,14 @@ impl Step for RustAnalyzer {
         cargo.env("SKIP_SLOW_TESTS", "1");
 
         cargo.add_rustc_lib_path(builder);
-        run_cargo_test(cargo, &[], &[], "rust-analyzer", host, builder);
+        run_cargo_test(cargo, &[], &[], "rust-analyzer", host, builder, Mode::ToolRustc);
+    }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::test("rust-analyzer", self.compilers.target())
+                .built_by(self.compilers.build_compiler()),
+        )
     }
 }
 
@@ -506,7 +540,14 @@ impl Step for Rustfmt {
 
         cargo.add_rustc_lib_path(builder);
 
-        run_cargo_test(cargo, &[], &[], "rustfmt", target, builder);
+        run_cargo_test(cargo, &[], &[], "rustfmt", target, builder, Mode::ToolRustc);
+    }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::test("rustfmt", self.compilers.target())
+                .built_by(self.compilers.build_compiler()),
+        )
     }
 }
 
@@ -792,7 +833,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         cargo.env("TEST_RUSTC", builder.rustc(compiler));
 
         cargo.allow_features(COMPILETEST_ALLOW_FEATURES);
-        run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder);
+        run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder, Mode::ToolStd);
     }
 }
 
@@ -887,6 +928,13 @@ impl Step for Clippy {
             crate::exit!(1);
         }
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::test("clippy", self.compilers.target())
+                .built_by(self.compilers.build_compiler()),
+        )
+    }
 }
 
 fn bin_path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString {
@@ -895,9 +943,11 @@ fn bin_path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString {
     env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect("")
 }
 
+/// Run the rustdoc-themes tool to test a given compiler.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RustdocTheme {
-    pub compiler: Compiler,
+    /// The compiler (more accurately, its rustdoc) that we test.
+    test_compiler: Compiler,
 }
 
 impl Step for RustdocTheme {
@@ -910,9 +960,9 @@ impl Step for RustdocTheme {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let compiler = run.builder.compiler(run.builder.top_stage, run.target);
+        let test_compiler = run.builder.compiler(run.builder.top_stage, run.target);
 
-        run.builder.ensure(RustdocTheme { compiler });
+        run.builder.ensure(RustdocTheme { test_compiler });
     }
 
     fn run(self, builder: &Builder<'_>) {
@@ -920,21 +970,34 @@ impl Step for RustdocTheme {
         let mut cmd = builder.tool_cmd(Tool::RustdocTheme);
         cmd.arg(rustdoc.to_str().unwrap())
             .arg(builder.src.join("src/librustdoc/html/static/css/rustdoc.css").to_str().unwrap())
-            .env("RUSTC_STAGE", self.compiler.stage.to_string())
-            .env("RUSTC_SYSROOT", builder.sysroot(self.compiler))
-            .env("RUSTDOC_LIBDIR", builder.sysroot_target_libdir(self.compiler, self.compiler.host))
+            .env("RUSTC_STAGE", self.test_compiler.stage.to_string())
+            .env("RUSTC_SYSROOT", builder.sysroot(self.test_compiler))
+            .env(
+                "RUSTDOC_LIBDIR",
+                builder.sysroot_target_libdir(self.test_compiler, self.test_compiler.host),
+            )
             .env("CFG_RELEASE_CHANNEL", &builder.config.channel)
-            .env("RUSTDOC_REAL", builder.rustdoc_for_compiler(self.compiler))
+            .env("RUSTDOC_REAL", builder.rustdoc_for_compiler(self.test_compiler))
             .env("RUSTC_BOOTSTRAP", "1");
-        cmd.args(linker_args(builder, self.compiler.host, LldThreads::No));
+        cmd.args(linker_args(builder, self.test_compiler.host, LldThreads::No));
 
         cmd.delay_failure().run(builder);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::test("rustdoc-theme", self.test_compiler.host)
+                .stage(self.test_compiler.stage),
+        )
+    }
 }
 
+/// Test rustdoc JS for the standard library.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RustdocJSStd {
-    pub target: TargetSelection,
+    /// Compiler that will build the standary library.
+    build_compiler: Compiler,
+    target: TargetSelection,
 }
 
 impl Step for RustdocJSStd {
@@ -948,7 +1011,10 @@ impl Step for RustdocJSStd {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(RustdocJSStd { target: run.target });
+        run.builder.ensure(RustdocJSStd {
+            build_compiler: run.builder.compiler(run.builder.top_stage, run.builder.host_target),
+            target: run.target,
+        });
     }
 
     fn run(self, builder: &Builder<'_>) {
@@ -976,19 +1042,18 @@ impl Step for RustdocJSStd {
             }
         }
         builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler(
-            builder.compiler(builder.top_stage, builder.host_target),
+            self.build_compiler,
             self.target,
             DocumentationFormat::Html,
         ));
-        let _guard = builder.msg(
-            Kind::Test,
-            "rustdoc-js-std",
-            None,
-            (builder.config.host_target, builder.top_stage),
-            self.target,
-        );
+        let _guard =
+            builder.msg(Kind::Test, "rustdoc-js-std", None, self.build_compiler, self.target);
         command.run(builder);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::test("rustdoc-js-std", self.target).stage(self.build_compiler.stage))
+    }
 }
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -1046,10 +1111,12 @@ fn get_browser_ui_test_version(builder: &Builder<'_>, npm: &Path) -> Option<Stri
         .or_else(|| get_browser_ui_test_version_inner(builder, npm, true))
 }
 
+/// Run GUI tests on a given rustdoc.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RustdocGUI {
-    pub target: TargetSelection,
-    pub compiler: Compiler,
+    /// The compiler whose rustdoc we are testing.
+    test_compiler: Compiler,
+    target: TargetSelection,
 }
 
 impl Step for RustdocGUI {
@@ -1073,12 +1140,12 @@ impl Step for RustdocGUI {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
-        run.builder.ensure(RustdocGUI { target: run.target, compiler });
+        let test_compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+        run.builder.ensure(RustdocGUI { test_compiler, target: run.target });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        builder.std(self.compiler, self.target);
+        builder.std(self.test_compiler, self.target);
 
         let mut cmd = builder.tool_cmd(Tool::RustdocGUITest);
 
@@ -1086,7 +1153,7 @@ impl Step for RustdocGUI {
         build_stamp::clear_if_dirty(
             builder,
             &out_dir,
-            &builder.rustdoc_for_compiler(self.compiler),
+            &builder.rustdoc_for_compiler(self.test_compiler),
         );
 
         if let Some(src) = builder.config.src.to_str() {
@@ -1103,10 +1170,10 @@ impl Step for RustdocGUI {
 
         cmd.arg("--jobs").arg(builder.jobs().to_string());
 
-        cmd.env("RUSTDOC", builder.rustdoc_for_compiler(self.compiler))
-            .env("RUSTC", builder.rustc(self.compiler));
+        cmd.env("RUSTDOC", builder.rustdoc_for_compiler(self.test_compiler))
+            .env("RUSTC", builder.rustc(self.test_compiler));
 
-        add_rustdoc_cargo_linker_args(&mut cmd, builder, self.compiler.host, LldThreads::No);
+        add_rustdoc_cargo_linker_args(&mut cmd, builder, self.test_compiler.host, LldThreads::No);
 
         for path in &builder.paths {
             if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) {
@@ -1133,9 +1200,13 @@ impl Step for RustdocGUI {
         }
 
         let _time = helpers::timeit(builder);
-        let _guard = builder.msg(Kind::Test, "rustdoc-gui", None, self.compiler, self.target);
+        let _guard = builder.msg_test("rustdoc-gui", (self.target, self.test_compiler.stage));
         try_run_tests(builder, &mut cmd, true);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::test("rustdoc-gui", self.target).stage(self.test_compiler.stage))
+    }
 }
 
 /// Runs `src/tools/tidy` and `cargo fmt --check` to detect various style
@@ -1253,76 +1324,6 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
     }
 }
 
-fn testdir(builder: &Builder<'_>, host: TargetSelection) -> PathBuf {
-    builder.out.join(host).join("test")
-}
-
-/// Declares a test step that invokes compiletest on a particular test suite.
-macro_rules! test {
-    (
-        $( #[$attr:meta] )* // allow docstrings and attributes
-        $name:ident {
-            path: $path:expr,
-            mode: $mode:expr,
-            suite: $suite:expr,
-            default: $default:expr
-            $( , IS_HOST: $IS_HOST:expr )? // default: false
-            $( , compare_mode: $compare_mode:expr )? // default: None
-            $( , )? // optional trailing comma
-        }
-    ) => {
-        $( #[$attr] )*
-        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-        pub struct $name {
-            pub compiler: Compiler,
-            pub target: TargetSelection,
-        }
-
-        impl Step for $name {
-            type Output = ();
-            const DEFAULT: bool = $default;
-            const IS_HOST: bool = (const {
-                #[allow(unused_assignments, unused_mut)]
-                let mut value = false;
-                $( value = $IS_HOST; )?
-                value
-            });
-
-            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-                run.suite_path($path)
-            }
-
-            fn make_run(run: RunConfig<'_>) {
-                let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
-
-                run.builder.ensure($name { compiler, target: run.target });
-            }
-
-            fn run(self, builder: &Builder<'_>) {
-                builder.ensure(Compiletest {
-                    compiler: self.compiler,
-                    target: self.target,
-                    mode: $mode,
-                    suite: $suite,
-                    path: $path,
-                    compare_mode: (const {
-                        #[allow(unused_assignments, unused_mut)]
-                        let mut value = None;
-                        $( value = $compare_mode; )?
-                        value
-                    }),
-                })
-            }
-
-            fn metadata(&self) -> Option<StepMetadata> {
-                Some(
-                    StepMetadata::test(stringify!($name), self.target)
-                )
-            }
-        }
-    };
-}
-
 /// Runs `cargo test` on the `src/tools/run-make-support` crate.
 /// That crate is used by run-make tests.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1358,7 +1359,15 @@ impl Step for CrateRunMakeSupport {
             &[],
         );
         cargo.allow_features("test");
-        run_cargo_test(cargo, &[], &[], "run-make-support self test", host, builder);
+        run_cargo_test(
+            cargo,
+            &[],
+            &[],
+            "run-make-support self test",
+            host,
+            builder,
+            Mode::ToolBootstrap,
+        );
     }
 }
 
@@ -1395,10 +1404,88 @@ impl Step for CrateBuildHelper {
             &[],
         );
         cargo.allow_features("test");
-        run_cargo_test(cargo, &[], &[], "build_helper self test", host, builder);
+        run_cargo_test(
+            cargo,
+            &[],
+            &[],
+            "build_helper self test",
+            host,
+            builder,
+            Mode::ToolBootstrap,
+        );
     }
 }
 
+fn testdir(builder: &Builder<'_>, host: TargetSelection) -> PathBuf {
+    builder.out.join(host).join("test")
+}
+
+/// Declares a test step that invokes compiletest on a particular test suite.
+macro_rules! test {
+    (
+        $( #[$attr:meta] )* // allow docstrings and attributes
+        $name:ident {
+            path: $path:expr,
+            mode: $mode:expr,
+            suite: $suite:expr,
+            default: $default:expr
+            $( , IS_HOST: $IS_HOST:expr )? // default: false
+            $( , compare_mode: $compare_mode:expr )? // default: None
+            $( , )? // optional trailing comma
+        }
+    ) => {
+        $( #[$attr] )*
+        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+        pub struct $name {
+            pub compiler: Compiler,
+            pub target: TargetSelection,
+        }
+
+        impl Step for $name {
+            type Output = ();
+            const DEFAULT: bool = $default;
+            const IS_HOST: bool = (const {
+                #[allow(unused_assignments, unused_mut)]
+                let mut value = false;
+                $( value = $IS_HOST; )?
+                value
+            });
+
+            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+                run.suite_path($path)
+            }
+
+            fn make_run(run: RunConfig<'_>) {
+                let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+
+                run.builder.ensure($name { compiler, target: run.target });
+            }
+
+            fn run(self, builder: &Builder<'_>) {
+                builder.ensure(Compiletest {
+                    compiler: self.compiler,
+                    target: self.target,
+                    mode: $mode,
+                    suite: $suite,
+                    path: $path,
+                    compare_mode: (const {
+                        #[allow(unused_assignments, unused_mut)]
+                        let mut value = None;
+                        $( value = $compare_mode; )?
+                        value
+                    }),
+                })
+            }
+
+            fn metadata(&self) -> Option<StepMetadata> {
+                Some(
+                    StepMetadata::test(stringify!($name), self.target)
+                )
+            }
+        }
+    };
+}
+
 test!(Ui { path: "tests/ui", mode: "ui", suite: "ui", default: true });
 
 test!(Crashes { path: "tests/crashes", mode: "crashes", suite: "crashes", default: true });
@@ -1741,7 +1828,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             builder.std(compiler, target);
         }
 
-        builder.ensure(RemoteCopyLibs { compiler, target });
+        builder.ensure(RemoteCopyLibs { build_compiler: compiler, target });
 
         // compiletest currently has... a lot of arguments, so let's just pass all
         // of them!
@@ -2277,9 +2364,10 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?}
     }
 }
 
+/// Runs the documentation tests for a book in `src/doc` using the `rustdoc` of `test_compiler`.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 struct BookTest {
-    compiler: Compiler,
+    test_compiler: Compiler,
     path: PathBuf,
     name: &'static str,
     is_ext_doc: bool,
@@ -2294,9 +2382,6 @@ impl Step for BookTest {
         run.never()
     }
 
-    /// Runs the documentation tests for a book in `src/doc`.
-    ///
-    /// This uses the `rustdoc` that sits next to `compiler`.
     fn run(self, builder: &Builder<'_>) {
         // External docs are different from local because:
         // - Some books need pre-processing by mdbook before being tested.
@@ -2319,13 +2404,13 @@ impl BookTest {
     /// This runs the equivalent of `mdbook test` (via the rustbook wrapper)
     /// which in turn runs `rustdoc --test` on each file in the book.
     fn run_ext_doc(self, builder: &Builder<'_>) {
-        let compiler = self.compiler;
+        let test_compiler = self.test_compiler;
 
-        builder.std(compiler, compiler.host);
+        builder.std(test_compiler, test_compiler.host);
 
         // mdbook just executes a binary named "rustdoc", so we need to update
         // PATH so that it points to our rustdoc.
-        let mut rustdoc_path = builder.rustdoc_for_compiler(compiler);
+        let mut rustdoc_path = builder.rustdoc_for_compiler(test_compiler);
         rustdoc_path.pop();
         let old_path = env::var_os("PATH").unwrap_or_default();
         let new_path = env::join_paths(iter::once(rustdoc_path).chain(env::split_paths(&old_path)))
@@ -2348,7 +2433,7 @@ impl BookTest {
                 let target = builder.config.host_target;
                 let cargo = tool::prepare_tool_cargo(
                     builder,
-                    compiler,
+                    test_compiler,
                     mode,
                     target,
                     Kind::Build,
@@ -2357,7 +2442,7 @@ impl BookTest {
                     &[],
                 );
 
-                let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target))
+                let stamp = BuildStamp::new(&builder.cargo_out(test_compiler, mode, target))
                     .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap());
 
                 let output_paths = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false);
@@ -2390,8 +2475,8 @@ impl BookTest {
             Kind::Test,
             format_args!("mdbook {}", self.path.display()),
             None,
-            compiler,
-            compiler.host,
+            test_compiler,
+            test_compiler.host,
         );
         let _time = helpers::timeit(builder);
         let toolstate = if rustbook_cmd.delay_failure().run(builder) {
@@ -2404,12 +2489,18 @@ impl BookTest {
 
     /// This runs `rustdoc --test` on all `.md` files in the path.
     fn run_local_doc(self, builder: &Builder<'_>) {
-        let compiler = self.compiler;
-        let host = self.compiler.host;
+        let test_compiler = self.test_compiler;
+        let host = self.test_compiler.host;
 
-        builder.std(compiler, host);
+        builder.std(test_compiler, host);
 
-        let _guard = builder.msg(Kind::Test, format!("book {}", self.name), None, compiler, host);
+        let _guard = builder.msg(
+            Kind::Test,
+            format!("book {}", self.name),
+            None,
+            (test_compiler.host, test_compiler.stage - 1),
+            host,
+        );
 
         // Do a breadth-first traversal of the `src/doc` directory and just run
         // tests for all files that end in `*.md`
@@ -2432,7 +2523,7 @@ impl BookTest {
         files.sort();
 
         for file in files {
-            markdown_test(builder, compiler, &file);
+            markdown_test(builder, test_compiler, &file);
         }
     }
 }
@@ -2448,7 +2539,7 @@ macro_rules! test_book {
         $(
             #[derive(Debug, Clone, PartialEq, Eq, Hash)]
             pub struct $name {
-                compiler: Compiler,
+                test_compiler: Compiler,
             }
 
             impl Step for $name {
@@ -2462,7 +2553,7 @@ macro_rules! test_book {
 
                 fn make_run(run: RunConfig<'_>) {
                     run.builder.ensure($name {
-                        compiler: run.builder.compiler(run.builder.top_stage, run.target),
+                        test_compiler: run.builder.compiler(run.builder.top_stage, run.target),
                     });
                 }
 
@@ -2482,7 +2573,7 @@ macro_rules! test_book {
                     )?
 
                     builder.ensure(BookTest {
-                        compiler: self.compiler,
+                        test_compiler: self.test_compiler,
                         path: PathBuf::from($path),
                         name: $book_name,
                         is_ext_doc: !$default,
@@ -2602,7 +2693,8 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) ->
 /// which have their own separate test steps.)
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CrateLibrustc {
-    compiler: Compiler,
+    /// The compiler that will run unit tests and doctests on the in-tree rustc source.
+    build_compiler: Compiler,
     target: TargetSelection,
     crates: Vec<String>,
 }
@@ -2619,18 +2711,18 @@ impl Step for CrateLibrustc {
     fn make_run(run: RunConfig<'_>) {
         let builder = run.builder;
         let host = run.build_triple();
-        let compiler = builder.compiler_for(builder.top_stage, host, host);
+        let build_compiler = builder.compiler(builder.top_stage - 1, host);
         let crates = run.make_run_crates(Alias::Compiler);
 
-        builder.ensure(CrateLibrustc { compiler, target: run.target, crates });
+        builder.ensure(CrateLibrustc { build_compiler, target: run.target, crates });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        builder.std(self.compiler, self.target);
+        builder.std(self.build_compiler, self.target);
 
         // To actually run the tests, delegate to a copy of the `Crate` step.
         builder.ensure(Crate {
-            compiler: self.compiler,
+            build_compiler: self.build_compiler,
             target: self.target,
             mode: Mode::Rustc,
             crates: self.crates,
@@ -2638,7 +2730,7 @@ impl Step for CrateLibrustc {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::test("CrateLibrustc", self.target))
+        Some(StepMetadata::test("CrateLibrustc", self.target).built_by(self.build_compiler))
     }
 }
 
@@ -2652,12 +2744,14 @@ fn run_cargo_test<'a>(
     description: impl Into<Option<&'a str>>,
     target: TargetSelection,
     builder: &Builder<'_>,
+    mode: impl Into<Option<Mode>>,
 ) -> bool {
+    let mode = mode.into();
     let compiler = cargo.compiler();
     let mut cargo = prepare_cargo_test(cargo, libtest_args, crates, target, builder);
     let _time = helpers::timeit(builder);
     let _group =
-        description.into().and_then(|what| builder.msg(Kind::Test, what, None, compiler, target));
+        description.into().and_then(|what| builder.msg(Kind::Test, what, mode, compiler, target));
 
     #[cfg(feature = "build-metrics")]
     builder.metrics.begin_test_suite(
@@ -2755,10 +2849,11 @@ fn prepare_cargo_test(
 /// library crates and compiler crates.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Crate {
-    pub compiler: Compiler,
-    pub target: TargetSelection,
-    pub mode: Mode,
-    pub crates: Vec<String>,
+    /// The compiler that will *build* libstd or rustc in test mode.
+    build_compiler: Compiler,
+    target: TargetSelection,
+    mode: Mode,
+    crates: Vec<String>,
 }
 
 impl Step for Crate {
@@ -2772,14 +2867,14 @@ impl Step for Crate {
     fn make_run(run: RunConfig<'_>) {
         let builder = run.builder;
         let host = run.build_triple();
-        let compiler = builder.compiler_for(builder.top_stage, host, host);
+        let build_compiler = builder.compiler(builder.top_stage, host);
         let crates = run
             .paths
             .iter()
             .map(|p| builder.crate_paths[&p.assert_single_path().path].clone())
             .collect();
 
-        builder.ensure(Crate { compiler, target: run.target, mode: Mode::Std, crates });
+        builder.ensure(Crate { build_compiler, target: run.target, mode: Mode::Std, crates });
     }
 
     /// Runs all unit tests plus documentation tests for a given crate defined
@@ -2791,19 +2886,13 @@ impl Step for Crate {
     /// Currently this runs all tests for a DAG by passing a bunch of `-p foo`
     /// arguments, and those arguments are discovered from `cargo metadata`.
     fn run(self, builder: &Builder<'_>) {
-        let compiler = self.compiler;
+        let build_compiler = self.build_compiler;
         let target = self.target;
         let mode = self.mode;
 
         // Prepare sysroot
         // See [field@compile::Std::force_recompile].
-        builder.ensure(Std::new(compiler, compiler.host).force_recompile(true));
-
-        // If we're not doing a full bootstrap but we're testing a stage2
-        // version of libstd, then what we're actually testing is the libstd
-        // produced in stage1. Reflect that here by updating the compiler that
-        // we're working with automatically.
-        let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
+        builder.ensure(Std::new(build_compiler, build_compiler.host).force_recompile(true));
 
         let mut cargo = if builder.kind == Kind::Miri {
             if builder.top_stage == 0 {
@@ -2815,7 +2904,7 @@ impl Step for Crate {
             // (Implicitly prepares target sysroot)
             let mut cargo = builder::Cargo::new(
                 builder,
-                compiler,
+                build_compiler,
                 mode,
                 SourceType::InTree,
                 target,
@@ -2841,12 +2930,19 @@ impl Step for Crate {
         } else {
             // Also prepare a sysroot for the target.
             if !builder.config.is_host_target(target) {
-                builder.ensure(compile::Std::new(compiler, target).force_recompile(true));
-                builder.ensure(RemoteCopyLibs { compiler, target });
+                builder.ensure(compile::Std::new(build_compiler, target).force_recompile(true));
+                builder.ensure(RemoteCopyLibs { build_compiler, target });
             }
 
             // Build `cargo test` command
-            builder::Cargo::new(builder, compiler, mode, SourceType::InTree, target, builder.kind)
+            builder::Cargo::new(
+                builder,
+                build_compiler,
+                mode,
+                SourceType::InTree,
+                target,
+                builder.kind,
+            )
         };
 
         match mode {
@@ -2865,7 +2961,7 @@ impl Step for Crate {
                 }
             }
             Mode::Rustc => {
-                compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
+                compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
             }
             _ => panic!("can only test libraries"),
         };
@@ -2882,10 +2978,19 @@ impl Step for Crate {
             crates.push("alloctests".to_owned());
         }
 
-        run_cargo_test(cargo, &[], &crates, &*crate_description(&self.crates), target, builder);
+        run_cargo_test(
+            cargo,
+            &[],
+            &crates,
+            &*crate_description(&self.crates),
+            target,
+            builder,
+            mode,
+        );
     }
 }
 
+/// Run cargo tests for the rustdoc crate.
 /// Rustdoc is special in various ways, which is why this step is different from `Crate`.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CrateRustdoc {
@@ -2975,13 +3080,22 @@ impl Step for CrateRustdoc {
         dylib_path.insert(0, PathBuf::from(&*libdir));
         cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
 
-        run_cargo_test(cargo, &[], &["rustdoc:0.0.0".to_string()], "rustdoc", target, builder);
+        run_cargo_test(
+            cargo,
+            &[],
+            &["rustdoc:0.0.0".to_string()],
+            "rustdoc",
+            target,
+            builder,
+            Mode::ToolRustc,
+        );
     }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CrateRustdocJsonTypes {
-    host: TargetSelection,
+    build_compiler: Compiler,
+    target: TargetSelection,
 }
 
 impl Step for CrateRustdocJsonTypes {
@@ -2996,23 +3110,22 @@ impl Step for CrateRustdocJsonTypes {
     fn make_run(run: RunConfig<'_>) {
         let builder = run.builder;
 
-        builder.ensure(CrateRustdocJsonTypes { host: run.target });
+        builder.ensure(CrateRustdocJsonTypes {
+            build_compiler: get_tool_target_compiler(
+                builder,
+                ToolTargetBuildMode::Build(run.target),
+            ),
+            target: run.target,
+        });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let target = self.host;
-
-        // Use the previous stage compiler to reuse the artifacts that are
-        // created when running compiletest for tests/rustdoc. If this used
-        // `compiler`, then it would cause rustdoc to be built *again*, which
-        // isn't really necessary.
-        let compiler = builder.compiler_for(builder.top_stage, target, target);
-        builder.ensure(compile::Rustc::new(compiler, target));
+        let target = self.target;
 
         let cargo = tool::prepare_tool_cargo(
             builder,
-            compiler,
-            Mode::ToolRustc,
+            self.build_compiler,
+            Mode::ToolTarget,
             target,
             builder.kind,
             "src/rustdoc-json-types",
@@ -3021,7 +3134,7 @@ impl Step for CrateRustdocJsonTypes {
         );
 
         // FIXME: this looks very wrong, libtest doesn't accept `-C` arguments and the quotes are fishy.
-        let libtest_args = if self.host.contains("musl") {
+        let libtest_args = if target.contains("musl") {
             ["'-Ctarget-feature=-crt-static'"].as_slice()
         } else {
             &[]
@@ -3034,6 +3147,7 @@ impl Step for CrateRustdocJsonTypes {
             "rustdoc-json-types",
             target,
             builder,
+            Mode::ToolTarget,
         );
     }
 }
@@ -3049,7 +3163,7 @@ impl Step for CrateRustdocJsonTypes {
 /// the build target (us) and the server is built for the target.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct RemoteCopyLibs {
-    compiler: Compiler,
+    build_compiler: Compiler,
     target: TargetSelection,
 }
 
@@ -3061,18 +3175,17 @@ impl Step for RemoteCopyLibs {
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let compiler = self.compiler;
+        let build_compiler = self.build_compiler;
         let target = self.target;
         if !builder.remote_tested(target) {
             return;
         }
 
-        builder.std(compiler, target);
+        builder.std(build_compiler, target);
 
         builder.info(&format!("REMOTE copy libs to emulator ({target})"));
 
-        let remote_test_server =
-            builder.ensure(tool::RemoteTestServer { build_compiler: compiler, target });
+        let remote_test_server = builder.ensure(tool::RemoteTestServer { build_compiler, target });
 
         // Spawn the emulator and wait for it to come online
         let tool = builder.tool_exe(Tool::RemoteTestClient);
@@ -3087,7 +3200,7 @@ impl Step for RemoteCopyLibs {
         cmd.run(builder);
 
         // Push all our dylibs to the emulator
-        for f in t!(builder.sysroot_target_libdir(compiler, target).read_dir()) {
+        for f in t!(builder.sysroot_target_libdir(build_compiler, target).read_dir()) {
             let f = t!(f);
             if helpers::is_dylib(&f.path()) {
                 command(&tool).arg("push").arg(f.path()).run(builder);
@@ -3133,6 +3246,9 @@ impl Step for Distcheck {
             .map(|args| args.split(" ").map(|s| s.to_string()).collect::<Vec<String>>())
             .unwrap_or_default();
 
+        // FIXME: unpack the source tarballs into a directory outside the source checkout, to
+        // ensure that it cannot access any local state
+        // Also ensure that it doesn't use download-ci-llvm
         command("tar")
             .arg("-xf")
             .arg(plain_src_tarball.tarball())
@@ -3232,7 +3348,7 @@ impl Step for Bootstrap {
 
         // bootstrap tests are racy on directory creation so just run them one at a time.
         // Since there's not many this shouldn't be a problem.
-        run_cargo_test(cargo, &["--test-threads=1"], &[], None, host, builder);
+        run_cargo_test(cargo, &["--test-threads=1"], &[], None, host, builder, Mode::ToolBootstrap);
     }
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -3248,9 +3364,15 @@ impl Step for Bootstrap {
     }
 }
 
+fn get_compiler_to_test(builder: &Builder<'_>, target: TargetSelection) -> Compiler {
+    builder.compiler(builder.top_stage, target)
+}
+
+/// Tests the Platform Support page in the rustc book.
+/// `test_compiler` is used to query the actual targets that are checked.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct TierCheck {
-    pub compiler: Compiler,
+    test_compiler: Compiler,
 }
 
 impl Step for TierCheck {
@@ -3263,48 +3385,42 @@ impl Step for TierCheck {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let compiler = run.builder.compiler_for(
-            run.builder.top_stage,
-            run.builder.build.host_target,
-            run.target,
-        );
-        run.builder.ensure(TierCheck { compiler });
+        run.builder
+            .ensure(TierCheck { test_compiler: get_compiler_to_test(run.builder, run.target) });
     }
 
-    /// Tests the Platform Support page in the rustc book.
     fn run(self, builder: &Builder<'_>) {
-        builder.std(self.compiler, self.compiler.host);
+        let tool_build_compiler = builder.compiler(0, builder.host_target);
+
         let mut cargo = tool::prepare_tool_cargo(
             builder,
-            self.compiler,
-            Mode::ToolStd,
-            self.compiler.host,
+            tool_build_compiler,
+            Mode::ToolBootstrap,
+            tool_build_compiler.host,
             Kind::Run,
             "src/tools/tier-check",
             SourceType::InTree,
             &[],
         );
         cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md"));
-        cargo.arg(builder.rustc(self.compiler));
+        cargo.arg(builder.rustc(self.test_compiler));
         if builder.is_verbose() {
             cargo.arg("--verbose");
         }
 
-        let _guard = builder.msg(
-            Kind::Test,
-            "platform support check",
-            None,
-            self.compiler,
-            self.compiler.host,
-        );
+        let _guard = builder.msg_test("platform support check", self.test_compiler);
         BootstrapCommand::from(cargo).delay_failure().run(builder);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::test("tier-check", self.test_compiler.host))
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct LintDocs {
-    pub compiler: Compiler,
-    pub target: TargetSelection,
+    build_compiler: Compiler,
+    target: TargetSelection,
 }
 
 impl Step for LintDocs {
@@ -3313,12 +3429,23 @@ impl Step for LintDocs {
     const IS_HOST: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("src/tools/lint-docs")
+        let stage = run.builder.top_stage;
+        // Lint docs tests might not work with stage 1, so do not run this test by default in
+        // `x test` below stage 2.
+        run.path("src/tools/lint-docs").default_condition(stage > 1)
     }
 
     fn make_run(run: RunConfig<'_>) {
+        if run.builder.top_stage < 2 {
+            eprintln!("WARNING: lint-docs tests might not work below stage 2");
+        }
+
         run.builder.ensure(LintDocs {
-            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
+            build_compiler: prepare_doc_compiler(
+                run.builder,
+                run.builder.config.host_target,
+                run.builder.top_stage,
+            ),
             target: run.target,
         });
     }
@@ -3326,8 +3453,14 @@ impl Step for LintDocs {
     /// Tests that the lint examples in the rustc book generate the correct
     /// lints and have the expected format.
     fn run(self, builder: &Builder<'_>) {
-        builder
-            .ensure(crate::core::build_steps::doc::RustcBook::validate(self.compiler, self.target));
+        builder.ensure(crate::core::build_steps::doc::RustcBook::validate(
+            self.build_compiler,
+            self.target,
+        ));
+    }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::test("lint-docs", self.target).built_by(self.build_compiler))
     }
 }
 
@@ -3356,7 +3489,7 @@ impl Step for RustInstaller {
 
         let _guard =
             builder.msg(Kind::Test, "rust-installer", None, build_compiler, bootstrap_host);
-        run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder);
+        run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder, Mode::ToolBootstrap);
 
         // We currently don't support running the test.sh script outside linux(?) environments.
         // Eventually this should likely migrate to #[test]s in rust-installer proper rather than a
@@ -3449,7 +3582,7 @@ impl Step for TestHelpers {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CodegenCranelift {
-    compiler: Compiler,
+    compilers: RustcPrivateCompilers,
     target: TargetSelection,
 }
 
@@ -3465,7 +3598,7 @@ impl Step for CodegenCranelift {
     fn make_run(run: RunConfig<'_>) {
         let builder = run.builder;
         let host = run.build_triple();
-        let compiler = run.builder.compiler_for(run.builder.top_stage, host, host);
+        let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, host);
 
         if builder.doc_tests == DocTests::Only {
             return;
@@ -3495,71 +3628,50 @@ impl Step for CodegenCranelift {
             return;
         }
 
-        builder.ensure(CodegenCranelift { compiler, target: run.target });
+        builder.ensure(CodegenCranelift { compilers, target: run.target });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let compiler = self.compiler;
-        let target = self.target;
-
-        builder.std(compiler, target);
+        let compilers = self.compilers;
+        let build_compiler = compilers.build_compiler();
 
-        // If we're not doing a full bootstrap but we're testing a stage2
-        // version of libstd, then what we're actually testing is the libstd
-        // produced in stage1. Reflect that here by updating the compiler that
-        // we're working with automatically.
-        let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
+        // We need to run the cranelift tests with the compiler against cranelift links to, not with
+        // the build compiler.
+        let target_compiler = compilers.target_compiler();
+        let target = self.target;
 
-        let build_cargo = || {
-            let mut cargo = builder::Cargo::new(
-                builder,
-                compiler,
-                Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
-                SourceType::InTree,
-                target,
-                Kind::Run,
-            );
+        builder.std(target_compiler, target);
 
-            cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift"));
-            cargo
-                .arg("--manifest-path")
-                .arg(builder.src.join("compiler/rustc_codegen_cranelift/build_system/Cargo.toml"));
-            compile::rustc_cargo_env(builder, &mut cargo, target);
+        let mut cargo = builder::Cargo::new(
+            builder,
+            target_compiler,
+            Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
+            SourceType::InTree,
+            target,
+            Kind::Run,
+        );
 
-            // Avoid incremental cache issues when changing rustc
-            cargo.env("CARGO_BUILD_INCREMENTAL", "false");
+        cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift"));
+        cargo
+            .arg("--manifest-path")
+            .arg(builder.src.join("compiler/rustc_codegen_cranelift/build_system/Cargo.toml"));
+        compile::rustc_cargo_env(builder, &mut cargo, target);
 
-            cargo
-        };
+        // Avoid incremental cache issues when changing rustc
+        cargo.env("CARGO_BUILD_INCREMENTAL", "false");
 
-        builder.info(&format!(
-            "{} cranelift stage{} ({} -> {})",
-            Kind::Test.description(),
-            compiler.stage,
-            &compiler.host,
-            target
-        ));
-        let _time = helpers::timeit(builder);
+        let _guard = builder.msg_test("rustc_codegen_cranelift", target_compiler);
 
         // FIXME handle vendoring for source tarballs before removing the --skip-test below
         let download_dir = builder.out.join("cg_clif_download");
 
-        // FIXME: Uncomment the `prepare` command below once vendoring is implemented.
-        /*
-        let mut prepare_cargo = build_cargo();
-        prepare_cargo.arg("--").arg("prepare").arg("--download-dir").arg(&download_dir);
-        #[expect(deprecated)]
-        builder.config.try_run(&mut prepare_cargo.into()).unwrap();
-        */
-
-        let mut cargo = build_cargo();
         cargo
             .arg("--")
             .arg("test")
             .arg("--download-dir")
             .arg(&download_dir)
             .arg("--out-dir")
-            .arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_clif"))
+            .arg(builder.stage_out(build_compiler, Mode::Codegen).join("cg_clif"))
             .arg("--no-unstable-features")
             .arg("--use-backend")
             .arg("cranelift")
@@ -3573,11 +3685,18 @@ impl Step for CodegenCranelift {
 
         cargo.into_cmd().run(builder);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::test("rustc_codegen_cranelift", self.target)
+                .built_by(self.compilers.build_compiler()),
+        )
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CodegenGCC {
-    compiler: Compiler,
+    compilers: RustcPrivateCompilers,
     target: TargetSelection,
 }
 
@@ -3593,7 +3712,7 @@ impl Step for CodegenGCC {
     fn make_run(run: RunConfig<'_>) {
         let builder = run.builder;
         let host = run.build_triple();
-        let compiler = run.builder.compiler_for(run.builder.top_stage, host, host);
+        let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, host);
 
         if builder.doc_tests == DocTests::Only {
             return;
@@ -3622,68 +3741,41 @@ impl Step for CodegenGCC {
             return;
         }
 
-        builder.ensure(CodegenGCC { compiler, target: run.target });
+        builder.ensure(CodegenGCC { compilers, target: run.target });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let compiler = self.compiler;
+        let compilers = self.compilers;
         let target = self.target;
 
         let gcc = builder.ensure(Gcc { target });
 
         builder.ensure(
-            compile::Std::new(compiler, target)
+            compile::Std::new(compilers.build_compiler(), target)
                 .extra_rust_args(&["-Csymbol-mangling-version=v0", "-Cpanic=abort"]),
         );
 
-        // If we're not doing a full bootstrap but we're testing a stage2
-        // version of libstd, then what we're actually testing is the libstd
-        // produced in stage1. Reflect that here by updating the compiler that
-        // we're working with automatically.
-        let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
+        let _guard = builder.msg_test("rustc_codegen_gcc", compilers.build_compiler());
 
-        let build_cargo = || {
-            let mut cargo = builder::Cargo::new(
-                builder,
-                compiler,
-                Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
-                SourceType::InTree,
-                target,
-                Kind::Run,
-            );
-
-            cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc"));
-            cargo
-                .arg("--manifest-path")
-                .arg(builder.src.join("compiler/rustc_codegen_gcc/build_system/Cargo.toml"));
-            compile::rustc_cargo_env(builder, &mut cargo, target);
-            add_cg_gcc_cargo_flags(&mut cargo, &gcc);
-
-            // Avoid incremental cache issues when changing rustc
-            cargo.env("CARGO_BUILD_INCREMENTAL", "false");
-            cargo.rustflag("-Cpanic=abort");
-
-            cargo
-        };
-
-        builder.info(&format!(
-            "{} GCC stage{} ({} -> {})",
-            Kind::Test.description(),
-            compiler.stage,
-            &compiler.host,
-            target
-        ));
-        let _time = helpers::timeit(builder);
+        let mut cargo = builder::Cargo::new(
+            builder,
+            compilers.build_compiler(),
+            Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
+            SourceType::InTree,
+            target,
+            Kind::Run,
+        );
 
-        // FIXME: Uncomment the `prepare` command below once vendoring is implemented.
-        /*
-        let mut prepare_cargo = build_cargo();
-        prepare_cargo.arg("--").arg("prepare");
-        #[expect(deprecated)]
-        builder.config.try_run(&mut prepare_cargo.into()).unwrap();
-        */
+        cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc"));
+        cargo
+            .arg("--manifest-path")
+            .arg(builder.src.join("compiler/rustc_codegen_gcc/build_system/Cargo.toml"));
+        compile::rustc_cargo_env(builder, &mut cargo, target);
+        add_cg_gcc_cargo_flags(&mut cargo, &gcc);
 
-        let mut cargo = build_cargo();
+        // Avoid incremental cache issues when changing rustc
+        cargo.env("CARGO_BUILD_INCREMENTAL", "false");
+        cargo.rustflag("-Cpanic=abort");
 
         cargo
             // cg_gcc's build system ignores RUSTFLAGS. pass some flags through CG_RUSTFLAGS instead.
@@ -3695,7 +3787,7 @@ impl Step for CodegenGCC {
             .arg("--gcc-path")
             .arg(gcc.libgccjit.parent().unwrap())
             .arg("--out-dir")
-            .arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_gcc"))
+            .arg(builder.stage_out(compilers.build_compiler(), Mode::Codegen).join("cg_gcc"))
             .arg("--release")
             .arg("--mini-tests")
             .arg("--std-tests");
@@ -3703,6 +3795,13 @@ impl Step for CodegenGCC {
 
         cargo.into_cmd().run(builder);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::test("rustc_codegen_gcc", self.target)
+                .built_by(self.compilers.build_compiler()),
+        )
+    }
 }
 
 /// Test step that does two things:
@@ -3711,8 +3810,17 @@ impl Step for CodegenGCC {
 ///   float parsing routines.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct TestFloatParse {
-    path: PathBuf,
-    host: TargetSelection,
+    /// The build compiler which will build and run unit tests of `test-float-parse`, and which will
+    /// build the `test-float-parse` tool itself.
+    ///
+    /// Note that the staging is a bit funny here, because this step essentially tests std, but it
+    /// also needs to build the tool. So if we test stage1 std, we build:
+    /// 1) stage1 rustc
+    /// 2) Use that to build stage1 libstd
+    /// 3) Use that to build and run *stage2* test-float-parse
+    build_compiler: Compiler,
+    /// Target for which we build std and test that std.
+    target: TargetSelection,
 }
 
 impl Step for TestFloatParse {
@@ -3725,47 +3833,47 @@ impl Step for TestFloatParse {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        for path in run.paths {
-            let path = path.assert_single_path().path.clone();
-            run.builder.ensure(Self { path, host: run.target });
-        }
+        run.builder.ensure(Self {
+            build_compiler: get_compiler_to_test(run.builder, run.target),
+            target: run.target,
+        });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let bootstrap_host = builder.config.host_target;
-        let compiler = builder.compiler(builder.top_stage, bootstrap_host);
-        let path = self.path.to_str().unwrap();
-        let crate_name = self.path.iter().next_back().unwrap().to_str().unwrap();
+        let build_compiler = self.build_compiler;
+        let target = self.target;
 
-        builder.ensure(tool::TestFloatParse { host: self.host });
+        // Build the standard library that will be tested, and a stdlib for host code
+        builder.std(build_compiler, target);
+        builder.std(build_compiler, builder.host_target);
 
         // Run any unit tests in the crate
         let mut cargo_test = tool::prepare_tool_cargo(
             builder,
-            compiler,
+            build_compiler,
             Mode::ToolStd,
-            bootstrap_host,
+            target,
             Kind::Test,
-            path,
+            "src/tools/test-float-parse",
             SourceType::InTree,
             &[],
         );
-        cargo_test.allow_features(tool::TestFloatParse::ALLOW_FEATURES);
+        cargo_test.allow_features(TEST_FLOAT_PARSE_ALLOW_FEATURES);
 
-        run_cargo_test(cargo_test, &[], &[], crate_name, bootstrap_host, builder);
+        run_cargo_test(cargo_test, &[], &[], "test-float-parse", target, builder, Mode::ToolStd);
 
         // Run the actual parse tests.
         let mut cargo_run = tool::prepare_tool_cargo(
             builder,
-            compiler,
+            build_compiler,
             Mode::ToolStd,
-            bootstrap_host,
+            target,
             Kind::Run,
-            path,
+            "src/tools/test-float-parse",
             SourceType::InTree,
             &[],
         );
-        cargo_run.allow_features(tool::TestFloatParse::ALLOW_FEATURES);
+        cargo_run.allow_features(TEST_FLOAT_PARSE_ALLOW_FEATURES);
 
         if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) {
             cargo_run.args(["--", "--skip-huge"]);
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index b62c9a906b7..65c4c499086 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -1539,42 +1539,7 @@ tool_rustc_extended!(Rustfmt {
     add_bins_to_sysroot: ["rustfmt"]
 });
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct TestFloatParse {
-    pub host: TargetSelection,
-}
-
-impl TestFloatParse {
-    pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128";
-}
-
-impl Step for TestFloatParse {
-    type Output = ToolBuildResult;
-    const IS_HOST: bool = true;
-    const DEFAULT: bool = false;
-
-    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("src/tools/test-float-parse")
-    }
-
-    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
-        let bootstrap_host = builder.config.host_target;
-        let compiler = builder.compiler(builder.top_stage, bootstrap_host);
-
-        builder.ensure(ToolBuild {
-            build_compiler: compiler,
-            target: bootstrap_host,
-            tool: "test-float-parse",
-            mode: Mode::ToolStd,
-            path: "src/tools/test-float-parse",
-            source_type: SourceType::InTree,
-            extra_features: Vec::new(),
-            allow_features: Self::ALLOW_FEATURES,
-            cargo_args: Vec::new(),
-            artifact_kind: ToolArtifactKind::Binary,
-        })
-    }
-}
+pub const TEST_FLOAT_PARSE_ALLOW_FEATURES: &str = "f16,cfg_target_has_reliable_f16_f128";
 
 impl Builder<'_> {
     /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index b224a7e7322..627085df812 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -295,7 +295,7 @@ pub fn crate_description(crates: &[impl AsRef<str>]) -> String {
         return "".into();
     }
 
-    let mut descr = String::from(" {");
+    let mut descr = String::from("{");
     descr.push_str(crates[0].as_ref());
     for krate in &crates[1..] {
         descr.push_str(", ");
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index b079117c5a7..43e67756e74 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -2037,6 +2037,212 @@ mod snapshot {
                 .render_steps(), @"[check] rustc 0 <host> -> RunMakeSupport 1 <host>");
     }
 
+    fn prepare_test_config(ctx: &TestCtx) -> ConfigBuilder {
+        ctx.config("test")
+            // Bootstrap only runs by default on CI, so we have to emulate that also locally.
+            .args(&["--ci", "true"])
+            // These rustdoc tests requires nodejs to be present.
+            // We can't easily opt out of it, so if it is present on the local PC, the test
+            // would have different result on CI, where nodejs might be missing.
+            .args(&["--skip", "rustdoc-js-std"])
+            .args(&["--skip", "rustdoc-js"])
+            .args(&["--skip", "rustdoc-gui"])
+    }
+
+    #[test]
+    fn test_all_stage_1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            prepare_test_config(&ctx)
+                .render_steps(), @r"
+        [build] rustc 0 <host> -> Tidy 1 <host>
+        [test] tidy <>
+        [build] rustdoc 0 <host>
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 0 <host> -> Compiletest 1 <host>
+        [test] Ui <host>
+        [test] Crashes <host>
+        [build] rustc 0 <host> -> CoverageDump 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [test] CodegenLlvm <host>
+        [test] CodegenUnits <host>
+        [test] AssemblyLlvm <host>
+        [test] Incremental <host>
+        [test] Debuginfo <host>
+        [test] UiFullDeps <host>
+        [build] rustdoc 1 <host>
+        [test] Rustdoc <host>
+        [test] CoverageRunRustdoc <host>
+        [test] Pretty <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 0 <host> -> std 0 <host>
+        [test] rustc 0 <host> -> CrateLibrustc 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [test] crate-bootstrap <host> src/tools/coverage-dump
+        [test] crate-bootstrap <host> src/tools/jsondoclint
+        [test] crate-bootstrap <host> src/tools/replace-version-placeholder
+        [test] crate-bootstrap <host> tidyselftest
+        [build] rustc 0 <host> -> UnstableBookGen 1 <host>
+        [build] rustc 0 <host> -> Rustbook 1 <host>
+        [doc] unstable-book (book) <host>
+        [doc] book (book) <host>
+        [doc] book/first-edition (book) <host>
+        [doc] book/second-edition (book) <host>
+        [doc] book/2018-edition (book) <host>
+        [doc] rustc 0 <host> -> standalone 1 <host>
+        [doc] rustc 1 <host> -> std 1 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
+        [build] rustc 0 <host> -> error-index 1 <host>
+        [doc] rustc 0 <host> -> error-index 1 <host>
+        [doc] nomicon (book) <host>
+        [doc] rustc 1 <host> -> reference (book) 2 <host>
+        [doc] rustdoc (book) <host>
+        [doc] rust-by-example (book) <host>
+        [build] rustc 0 <host> -> LintDocs 1 <host>
+        [doc] rustc (book) <host>
+        [doc] cargo (book) <host>
+        [doc] clippy (book) <host>
+        [doc] embedded-book (book) <host>
+        [doc] edition-guide (book) <host>
+        [doc] style-guide (book) <host>
+        [doc] rustc 0 <host> -> releases 1 <host>
+        [build] rustc 0 <host> -> Linkchecker 1 <host>
+        [test] link-check <host>
+        [test] tier-check <host>
+        [test] rustc 0 <host> -> rust-analyzer 1 <host>
+        [build] rustc 0 <host> -> RustdocTheme 1 <host>
+        [test] rustdoc-theme 1 <host>
+        [test] RustdocUi <host>
+        [build] rustc 0 <host> -> JsonDocCk 1 <host>
+        [build] rustc 0 <host> -> JsonDocLint 1 <host>
+        [test] RustdocJson <host>
+        [doc] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 0 <host> -> HtmlChecker 1 <host>
+        [test] html-check <host>
+        [build] rustc 0 <host> -> RunMakeSupport 1 <host>
+        [build] rustc 1 <host> -> cargo 2 <host>
+        [test] RunMake <host>
+        ");
+    }
+
+    #[test]
+    fn test_all_stage_2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            prepare_test_config(&ctx)
+                .stage(2)
+                .render_steps(), @r"
+        [build] rustc 0 <host> -> Tidy 1 <host>
+        [test] tidy <>
+        [build] rustdoc 0 <host>
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 2 <host> -> std 2 <host>
+        [build] rustc 0 <host> -> Compiletest 1 <host>
+        [test] Ui <host>
+        [test] Crashes <host>
+        [build] rustc 0 <host> -> CoverageDump 1 <host>
+        [build] rustc 2 <host> -> std 2 <host>
+        [test] CodegenLlvm <host>
+        [test] CodegenUnits <host>
+        [test] AssemblyLlvm <host>
+        [test] Incremental <host>
+        [test] Debuginfo <host>
+        [build] rustc 2 <host> -> rustc 3 <host>
+        [test] UiFullDeps <host>
+        [build] rustdoc 2 <host>
+        [test] Rustdoc <host>
+        [test] CoverageRunRustdoc <host>
+        [test] Pretty <host>
+        [build] rustc 2 <host> -> std 2 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustdoc 1 <host>
+        [test] rustc 1 <host> -> CrateLibrustc 2 <host>
+        [test] crate-bootstrap <host> src/tools/coverage-dump
+        [test] crate-bootstrap <host> src/tools/jsondoclint
+        [test] crate-bootstrap <host> src/tools/replace-version-placeholder
+        [test] crate-bootstrap <host> tidyselftest
+        [build] rustc 0 <host> -> UnstableBookGen 1 <host>
+        [build] rustc 0 <host> -> Rustbook 1 <host>
+        [doc] unstable-book (book) <host>
+        [doc] book (book) <host>
+        [doc] book/first-edition (book) <host>
+        [doc] book/second-edition (book) <host>
+        [doc] book/2018-edition (book) <host>
+        [doc] rustc 1 <host> -> standalone 2 <host>
+        [doc] rustc 1 <host> -> std 1 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
+        [build] rustc 1 <host> -> error-index 2 <host>
+        [doc] rustc 1 <host> -> error-index 2 <host>
+        [doc] nomicon (book) <host>
+        [doc] rustc 1 <host> -> reference (book) 2 <host>
+        [doc] rustdoc (book) <host>
+        [doc] rust-by-example (book) <host>
+        [build] rustc 0 <host> -> LintDocs 1 <host>
+        [doc] rustc (book) <host>
+        [doc] cargo (book) <host>
+        [doc] clippy (book) <host>
+        [doc] embedded-book (book) <host>
+        [doc] edition-guide (book) <host>
+        [doc] style-guide (book) <host>
+        [doc] rustc 1 <host> -> releases 2 <host>
+        [build] rustc 0 <host> -> Linkchecker 1 <host>
+        [test] link-check <host>
+        [test] tier-check <host>
+        [test] rustc 1 <host> -> rust-analyzer 2 <host>
+        [doc] rustc (book) <host>
+        [test] rustc 1 <host> -> lint-docs 2 <host>
+        [build] rustc 0 <host> -> RustdocTheme 1 <host>
+        [test] rustdoc-theme 2 <host>
+        [test] RustdocUi <host>
+        [build] rustc 0 <host> -> JsonDocCk 1 <host>
+        [build] rustc 0 <host> -> JsonDocLint 1 <host>
+        [test] RustdocJson <host>
+        [doc] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 0 <host> -> HtmlChecker 1 <host>
+        [test] html-check <host>
+        [build] rustc 0 <host> -> RunMakeSupport 1 <host>
+        [build] rustc 2 <host> -> cargo 3 <host>
+        [test] RunMake <host>
+        ");
+    }
+
+    #[test]
+    fn test_compiler_stage_1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("test")
+                .path("compiler")
+                .stage(1)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 0 <host> -> std 0 <host>
+        [build] rustdoc 0 <host>
+        [test] rustc 0 <host> -> CrateLibrustc 1 <host>
+        ");
+    }
+
+    #[test]
+    fn test_compiler_stage_2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("test")
+                .path("compiler")
+                .stage(2)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustdoc 1 <host>
+        [test] rustc 1 <host> -> CrateLibrustc 2 <host>
+        ");
+    }
+
     #[test]
     fn test_exclude() {
         let ctx = TestCtx::new();
@@ -2054,13 +2260,15 @@ mod snapshot {
 
         let get_steps = |args: &[&str]| ctx.config("test").args(args).get_steps();
 
+        let rustc_metadata =
+            || StepMetadata::test("CrateLibrustc", host).built_by(Compiler::new(0, host));
         // Ensure our test is valid, and `test::Rustc` would be run without the exclude.
-        get_steps(&[]).assert_contains(StepMetadata::test("CrateLibrustc", host));
+        get_steps(&[]).assert_contains(rustc_metadata());
 
         let steps = get_steps(&["--skip", "compiler/rustc_data_structures"]);
 
         // Ensure tests for rustc are not skipped.
-        steps.assert_contains(StepMetadata::test("CrateLibrustc", host));
+        steps.assert_contains(rustc_metadata());
         steps.assert_contains_fuzzy(StepMetadata::build("rustc", host));
     }
 
@@ -2077,6 +2285,7 @@ mod snapshot {
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustdoc 1 <host>
         [build] rustdoc 0 <host>
+        [test] rustc 0 <host> -> cargo 1 <host>
         ");
     }
 
@@ -2096,6 +2305,7 @@ mod snapshot {
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustdoc 2 <host>
         [build] rustdoc 1 <host>
+        [test] rustc 1 <host> -> cargo 2 <host>
         ");
     }
 
@@ -2117,6 +2327,19 @@ mod snapshot {
     }
 
     #[test]
+    fn test_tier_check() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("test")
+                .path("tier-check")
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [test] tier-check <host>
+        ");
+    }
+
+    #[test]
     fn doc_all() {
         let ctx = TestCtx::new();
         insta::assert_snapshot!(
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index a8eb563015f..d12cc962187 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -1002,13 +1002,17 @@ impl Config {
             (0, Subcommand::Install) => {
                 check_stage0("install");
             }
+            (0, Subcommand::Test { .. }) if build_compiletest_allow_stage0 != Some(true) => {
+                eprintln!(
+                    "ERROR: cannot test anything on stage 0. Use at least stage 1. If you want to run compiletest with an external stage0 toolchain, enable `build.compiletest-allow-stage0`."
+                );
+                exit!(1);
+            }
             _ => {}
         }
 
         if flags_compile_time_deps && !matches!(flags_cmd, Subcommand::Check { .. }) {
-            eprintln!(
-                "WARNING: Can't use --compile-time-deps with any subcommand other than check."
-            );
+            eprintln!("ERROR: Can't use --compile-time-deps with any subcommand other than check.");
             exit!(1);
         }
 
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index b8ee83b20e4..9a882eae08e 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -1144,14 +1144,18 @@ impl Build {
         };
 
         let action = action.into().description();
-        let msg = |fmt| format!("{action} stage{actual_stage} {what}{fmt}");
+        let what = what.to_string();
+        let msg = |fmt| {
+            let space = if !what.is_empty() { " " } else { "" };
+            format!("{action} stage{actual_stage} {what}{space}{fmt}")
+        };
         let msg = if let Some(target) = target.into() {
             let build_stage = host_and_stage.stage;
             let host = host_and_stage.host;
             if host == target {
-                msg(format_args!(" (stage{build_stage} -> stage{actual_stage}, {target})"))
+                msg(format_args!("(stage{build_stage} -> stage{actual_stage}, {target})"))
             } else {
-                msg(format_args!(" (stage{build_stage}:{host} -> stage{actual_stage}:{target})"))
+                msg(format_args!("(stage{build_stage}:{host} -> stage{actual_stage}:{target})"))
             }
         } else {
             msg(format_args!(""))
@@ -1159,6 +1163,25 @@ impl Build {
         self.group(&msg)
     }
 
+    /// Return a `Group` guard for a [`Step`] that tests `what` with the given `stage` and `target`
+    /// (determined by `host_and_stage`).
+    /// Use this instead of [`Build::msg`] when there is no clear `build_compiler` to be
+    /// determined.
+    ///
+    /// [`Step`]: crate::core::builder::Step
+    #[must_use = "Groups should not be dropped until the Step finishes running"]
+    #[track_caller]
+    fn msg_test(
+        &self,
+        what: impl Display,
+        host_and_stage: impl Into<HostAndStage>,
+    ) -> Option<gha::Group> {
+        let HostAndStage { host, stage } = host_and_stage.into();
+        let action = Kind::Test.description();
+        let msg = format!("{action} stage{stage} {what} ({host})");
+        self.group(&msg)
+    }
+
     /// Return a `Group` guard for a [`Step`] that is only built once and isn't affected by `--stage`.
     ///
     /// [`Step`]: crate::core::builder::Step
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 606d88d3db4..2cc2fb486fa 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -531,4 +531,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "It is now possible to `check/build/dist` the standard stage 0 library if you use a stage0 rustc built from in-tree sources. This is useful for quickly cross-compiling the standard library. You have to enable build.local-rebuild for this to work.",
     },
+    ChangeInfo {
+        change_id: 145663,
+        severity: ChangeSeverity::Warning,
+        summary: "It is no longer possible to `x test` with stage 0, except for running compiletest and opting into `build.compiletest-allow-stage0`.",
+    },
 ];
diff --git a/src/ci/docker/host-x86_64/tidy/Dockerfile b/src/ci/docker/host-x86_64/tidy/Dockerfile
index c8558689d3b..2dda51b155e 100644
--- a/src/ci/docker/host-x86_64/tidy/Dockerfile
+++ b/src/ci/docker/host-x86_64/tidy/Dockerfile
@@ -44,5 +44,5 @@ RUN bash -c 'npm install -g eslint@$(cat /tmp/eslint.version)'
 
 # NOTE: intentionally uses python2 for x.py so we can test it still works.
 # validate-toolstate only runs in our CI, so it's ok for it to only support python3.
-ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 \
+ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
   src/tools/tidy tidyselftest --extra-checks=py,cpp,js,spellcheck
diff --git a/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md b/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md
deleted file mode 100644
index b20c30ec8f1..00000000000
--- a/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# `extended_varargs_abi_support`
-
-The tracking issue for this feature is: [#100189]
-
-[#100189]: https://github.com/rust-lang/rust/issues/100189
-
-------------------------
-
-This feature adds the possibility of using `sysv64`, `win64` or `efiapi` calling
-conventions on functions with varargs.
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 2985971a053..5d36ffc2d3a 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -12,20 +12,20 @@ path = "lib.rs"
 arrayvec = { version = "0.7", default-features = false }
 askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] }
 base64 = "0.21.7"
-indexmap.workspace = true
-itertools.workspace = true
+indexmap = "2"
+itertools = "0.12"
 minifier = { version = "0.3.5", default-features = false }
 pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] }
 regex = "1"
 rustdoc-json-types = { path = "../rustdoc-json-types" }
 serde = { version = "1.0", features = ["derive"] }
-serde_json.workspace = true
+serde_json = "1.0"
 smallvec = "1.8.1"
 stringdex = { version = "0.0.1-alpha4" }
-tempfile.workspace = true
+tempfile = "3"
 threadpool = "1.8.1"
+tracing = "0.1"
 tracing-tree = "0.3.0"
-tracing.workspace = true
 unicode-segmentation = "1.9"
 # tidy-alphabetical-end
 
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index dddc087d124..2984f3ab50e 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -289,8 +289,26 @@ impl SerializedSearchIndex {
                             (Some(self_type_data), None) => Some(self_type_data),
                             (None, Some(other_type_data)) => Some(TypeData {
                                 search_unbox: other_type_data.search_unbox,
-                                inverted_function_signature_index: other_type_data
-                                    .inverted_function_signature_index
+                                inverted_function_inputs_index: other_type_data
+                                    .inverted_function_inputs_index
+                                    .iter()
+                                    .cloned()
+                                    .map(|mut list: Vec<u32>| {
+                                        for fnid in &mut list {
+                                            assert!(
+                                                other.function_data
+                                                    [usize::try_from(*fnid).unwrap()]
+                                                .is_some(),
+                                            );
+                                            // this is valid because we call `self.push()` once, exactly, for every entry,
+                                            // even if we're just pushing a tombstone
+                                            *fnid += u32::try_from(other_entryid_offset).unwrap();
+                                        }
+                                        list
+                                    })
+                                    .collect(),
+                                inverted_function_output_index: other_type_data
+                                    .inverted_function_output_index
                                     .iter()
                                     .cloned()
                                     .map(|mut list: Vec<u32>| {
@@ -310,18 +328,42 @@ impl SerializedSearchIndex {
                             }),
                             (Some(mut self_type_data), Some(other_type_data)) => {
                                 for (size, other_list) in other_type_data
-                                    .inverted_function_signature_index
+                                    .inverted_function_inputs_index
+                                    .iter()
+                                    .enumerate()
+                                {
+                                    while self_type_data.inverted_function_inputs_index.len()
+                                        <= size
+                                    {
+                                        self_type_data
+                                            .inverted_function_inputs_index
+                                            .push(Vec::new());
+                                    }
+                                    self_type_data.inverted_function_inputs_index[size].extend(
+                                        other_list.iter().copied().map(|fnid| {
+                                            assert!(
+                                                other.function_data[usize::try_from(fnid).unwrap()]
+                                                    .is_some(),
+                                            );
+                                            // this is valid because we call `self.push()` once, exactly, for every entry,
+                                            // even if we're just pushing a tombstone
+                                            fnid + u32::try_from(other_entryid_offset).unwrap()
+                                        }),
+                                    )
+                                }
+                                for (size, other_list) in other_type_data
+                                    .inverted_function_output_index
                                     .iter()
                                     .enumerate()
                                 {
-                                    while self_type_data.inverted_function_signature_index.len()
+                                    while self_type_data.inverted_function_output_index.len()
                                         <= size
                                     {
                                         self_type_data
-                                            .inverted_function_signature_index
+                                            .inverted_function_output_index
                                             .push(Vec::new());
                                     }
-                                    self_type_data.inverted_function_signature_index[size].extend(
+                                    self_type_data.inverted_function_output_index[size].extend(
                                         other_list.iter().copied().map(|fnid| {
                                             assert!(
                                                 other.function_data[usize::try_from(fnid).unwrap()]
@@ -443,8 +485,25 @@ impl SerializedSearchIndex {
                         param_names: function_data.param_names.clone(),
                     }),
                     other.type_data[other_entryid].as_ref().map(|type_data| TypeData {
-                        inverted_function_signature_index: type_data
-                            .inverted_function_signature_index
+                        inverted_function_inputs_index: type_data
+                            .inverted_function_inputs_index
+                            .iter()
+                            .cloned()
+                            .map(|mut list| {
+                                for fnid in &mut list {
+                                    assert!(
+                                        other.function_data[usize::try_from(*fnid).unwrap()]
+                                            .is_some(),
+                                    );
+                                    // this is valid because we call `self.push()` once, exactly, for every entry,
+                                    // even if we're just pushing a tombstone
+                                    *fnid += u32::try_from(other_entryid_offset).unwrap();
+                                }
+                                list
+                            })
+                            .collect(),
+                        inverted_function_output_index: type_data
+                            .inverted_function_output_index
                             .iter()
                             .cloned()
                             .map(|mut list| {
@@ -599,9 +658,13 @@ impl SerializedSearchIndex {
                     },
                 ),
                 self.type_data[id].as_ref().map(
-                    |TypeData { search_unbox, inverted_function_signature_index }| {
-                        let inverted_function_signature_index: Vec<Vec<u32>> =
-                            inverted_function_signature_index
+                    |TypeData {
+                         search_unbox,
+                         inverted_function_inputs_index,
+                         inverted_function_output_index,
+                     }| {
+                        let inverted_function_inputs_index: Vec<Vec<u32>> =
+                            inverted_function_inputs_index
                                 .iter()
                                 .cloned()
                                 .map(|mut list| {
@@ -615,7 +678,26 @@ impl SerializedSearchIndex {
                                     list
                                 })
                                 .collect();
-                        TypeData { search_unbox: *search_unbox, inverted_function_signature_index }
+                        let inverted_function_output_index: Vec<Vec<u32>> =
+                            inverted_function_output_index
+                                .iter()
+                                .cloned()
+                                .map(|mut list| {
+                                    for id in &mut list {
+                                        *id = u32::try_from(
+                                            *map.get(&usize::try_from(*id).unwrap()).unwrap(),
+                                        )
+                                        .unwrap();
+                                    }
+                                    list.sort();
+                                    list
+                                })
+                                .collect();
+                        TypeData {
+                            search_unbox: *search_unbox,
+                            inverted_function_inputs_index,
+                            inverted_function_output_index,
+                        }
                     },
                 ),
                 self.alias_pointers[id].and_then(|alias| map.get(&alias).copied()),
@@ -934,18 +1016,20 @@ struct TypeData {
     /// | `Unboxable<Inner>` | yes                | no      | no                 |
     /// | `Inner<Unboxable>` | no                 | no      | yes                |
     search_unbox: bool,
-    /// List of functions that mention this type in their type signature.
+    /// List of functions that mention this type in their type signature,
+    /// on the left side of the `->` arrow.
     ///
-    /// - The outermost list has one entry per alpha-normalized generic.
-    ///
-    /// - The second layer is sorted by number of types that appear in the
+    /// - The outer layer is sorted by number of types that appear in the
     ///   type signature. The search engine iterates over these in order from
     ///   smallest to largest. Functions with less stuff in their type
     ///   signature are more likely to be what the user wants, because we never
     ///   show functions that are *missing* parts of the query, so removing..
     ///
-    /// - The final layer is the list of functions.
-    inverted_function_signature_index: Vec<Vec<u32>>,
+    /// - The inner layer is the list of functions.
+    inverted_function_inputs_index: Vec<Vec<u32>>,
+    /// List of functions that mention this type in their type signature,
+    /// on the right side of the `->` arrow.
+    inverted_function_output_index: Vec<Vec<u32>>,
 }
 
 impl Serialize for TypeData {
@@ -953,15 +1037,21 @@ impl Serialize for TypeData {
     where
         S: Serializer,
     {
-        if self.search_unbox || !self.inverted_function_signature_index.is_empty() {
+        if self.search_unbox
+            || !self.inverted_function_inputs_index.is_empty()
+            || !self.inverted_function_output_index.is_empty()
+        {
             let mut seq = serializer.serialize_seq(None)?;
-            if !self.inverted_function_signature_index.is_empty() {
-                let mut buf = Vec::new();
-                encode::write_postings_to_string(&self.inverted_function_signature_index, &mut buf);
-                let mut serialized_result = Vec::new();
-                stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result);
-                seq.serialize_element(&String::from_utf8(serialized_result).unwrap())?;
-            }
+            let mut buf = Vec::new();
+            encode::write_postings_to_string(&self.inverted_function_inputs_index, &mut buf);
+            let mut serialized_result = Vec::new();
+            stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result);
+            seq.serialize_element(&str::from_utf8(&serialized_result).unwrap())?;
+            buf.clear();
+            serialized_result.clear();
+            encode::write_postings_to_string(&self.inverted_function_output_index, &mut buf);
+            stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result);
+            seq.serialize_element(&str::from_utf8(&serialized_result).unwrap())?;
             if self.search_unbox {
                 seq.serialize_element(&1)?;
             }
@@ -984,21 +1074,39 @@ impl<'de> Deserialize<'de> for TypeData {
                 write!(formatter, "type data")
             }
             fn visit_none<E>(self) -> Result<TypeData, E> {
-                Ok(TypeData { inverted_function_signature_index: vec![], search_unbox: false })
+                Ok(TypeData {
+                    inverted_function_inputs_index: vec![],
+                    inverted_function_output_index: vec![],
+                    search_unbox: false,
+                })
             }
             fn visit_seq<A: de::SeqAccess<'de>>(self, mut v: A) -> Result<TypeData, A::Error> {
-                let inverted_function_signature_index: String =
+                let inverted_function_inputs_index: String =
+                    v.next_element()?.unwrap_or(String::new());
+                let inverted_function_output_index: String =
                     v.next_element()?.unwrap_or(String::new());
                 let search_unbox: u32 = v.next_element()?.unwrap_or(0);
                 let mut idx: Vec<u8> = Vec::new();
                 stringdex_internals::decode::read_base64_from_bytes(
-                    inverted_function_signature_index.as_bytes(),
+                    inverted_function_inputs_index.as_bytes(),
                     &mut idx,
                 )
                 .unwrap();
-                let mut inverted_function_signature_index = Vec::new();
-                encode::read_postings_from_string(&mut inverted_function_signature_index, &idx);
-                Ok(TypeData { inverted_function_signature_index, search_unbox: search_unbox == 1 })
+                let mut inverted_function_inputs_index = Vec::new();
+                encode::read_postings_from_string(&mut inverted_function_inputs_index, &idx);
+                idx.clear();
+                stringdex_internals::decode::read_base64_from_bytes(
+                    inverted_function_output_index.as_bytes(),
+                    &mut idx,
+                )
+                .unwrap();
+                let mut inverted_function_output_index = Vec::new();
+                encode::read_postings_from_string(&mut inverted_function_output_index, &idx);
+                Ok(TypeData {
+                    inverted_function_inputs_index,
+                    inverted_function_output_index,
+                    search_unbox: search_unbox == 1,
+                })
             }
         }
         deserializer.deserialize_any(TypeDataVisitor)
@@ -1222,8 +1330,16 @@ pub(crate) fn build_index(
                 let index = *index.get();
                 serialized_index.descs[index] = crate_doc;
                 for type_data in serialized_index.type_data.iter_mut() {
-                    if let Some(TypeData { inverted_function_signature_index, .. }) = type_data {
-                        for list in &mut inverted_function_signature_index[..] {
+                    if let Some(TypeData {
+                        inverted_function_inputs_index,
+                        inverted_function_output_index,
+                        ..
+                    }) = type_data
+                    {
+                        for list in inverted_function_inputs_index
+                            .iter_mut()
+                            .chain(inverted_function_output_index.iter_mut())
+                        {
                             list.retain(|fnid| {
                                 serialized_index.entry_data[usize::try_from(*fnid).unwrap()]
                                     .as_ref()
@@ -1449,7 +1565,8 @@ pub(crate) fn build_index(
                     if serialized_index.type_data[id].as_mut().is_none() {
                         serialized_index.type_data[id] = Some(TypeData {
                             search_unbox,
-                            inverted_function_signature_index: Vec::new(),
+                            inverted_function_inputs_index: Vec::new(),
+                            inverted_function_output_index: Vec::new(),
                         });
                     } else if search_unbox {
                         serialized_index.type_data[id].as_mut().unwrap().search_unbox = true;
@@ -1473,7 +1590,11 @@ pub(crate) fn build_index(
                                 None
                             },
                         },
-                        TypeData { search_unbox, inverted_function_signature_index: Vec::new() },
+                        TypeData {
+                            inverted_function_inputs_index: Vec::new(),
+                            inverted_function_output_index: Vec::new(),
+                            search_unbox,
+                        },
                     );
                     pathid
                 }
@@ -1695,13 +1816,14 @@ pub(crate) fn build_index(
             }
         }
         if let Some(search_type) = &mut item.search_type {
-            let mut used_in_function_signature = BTreeSet::new();
+            let mut used_in_function_inputs = BTreeSet::new();
+            let mut used_in_function_output = BTreeSet::new();
             for item in &mut search_type.inputs {
                 convert_render_type(
                     item,
                     cache,
                     &mut serialized_index,
-                    &mut used_in_function_signature,
+                    &mut used_in_function_inputs,
                     tcx,
                 );
             }
@@ -1710,20 +1832,44 @@ pub(crate) fn build_index(
                     item,
                     cache,
                     &mut serialized_index,
-                    &mut used_in_function_signature,
+                    &mut used_in_function_output,
                     tcx,
                 );
             }
+            let mut used_in_constraints = Vec::new();
             for constraint in &mut search_type.where_clause {
+                let mut used_in_constraint = BTreeSet::new();
                 for trait_ in &mut constraint[..] {
                     convert_render_type(
                         trait_,
                         cache,
                         &mut serialized_index,
-                        &mut used_in_function_signature,
+                        &mut used_in_constraint,
                         tcx,
                     );
                 }
+                used_in_constraints.push(used_in_constraint);
+            }
+            loop {
+                let mut inserted_any = false;
+                for (i, used_in_constraint) in used_in_constraints.iter().enumerate() {
+                    let id = !(i as isize);
+                    if used_in_function_inputs.contains(&id)
+                        && !used_in_function_inputs.is_superset(&used_in_constraint)
+                    {
+                        used_in_function_inputs.extend(used_in_constraint.iter().copied());
+                        inserted_any = true;
+                    }
+                    if used_in_function_output.contains(&id)
+                        && !used_in_function_output.is_superset(&used_in_constraint)
+                    {
+                        used_in_function_output.extend(used_in_constraint.iter().copied());
+                        inserted_any = true;
+                    }
+                }
+                if !inserted_any {
+                    break;
+                }
             }
             let search_type_size = search_type.size() +
                 // Artificially give struct fields a size of 8 instead of their real
@@ -1746,13 +1892,34 @@ pub(crate) fn build_index(
                     .map(|sym| sym.map(|sym| sym.to_string()).unwrap_or(String::new()))
                     .collect::<Vec<String>>(),
             });
-            for index in used_in_function_signature {
+            for index in used_in_function_inputs {
+                let postings = if index >= 0 {
+                    assert!(serialized_index.path_data[index as usize].is_some());
+                    &mut serialized_index.type_data[index as usize]
+                        .as_mut()
+                        .unwrap()
+                        .inverted_function_inputs_index
+                } else {
+                    let generic_id = usize::try_from(-index).unwrap() - 1;
+                    for _ in serialized_index.generic_inverted_index.len()..=generic_id {
+                        serialized_index.generic_inverted_index.push(Vec::new());
+                    }
+                    &mut serialized_index.generic_inverted_index[generic_id]
+                };
+                while postings.len() <= search_type_size {
+                    postings.push(Vec::new());
+                }
+                if postings[search_type_size].last() != Some(&(new_entry_id as u32)) {
+                    postings[search_type_size].push(new_entry_id as u32);
+                }
+            }
+            for index in used_in_function_output {
                 let postings = if index >= 0 {
                     assert!(serialized_index.path_data[index as usize].is_some());
                     &mut serialized_index.type_data[index as usize]
                         .as_mut()
                         .unwrap()
-                        .inverted_function_signature_index
+                        .inverted_function_output_index
                 } else {
                     let generic_id = usize::try_from(-index).unwrap() - 1;
                     for _ in serialized_index.generic_inverted_index.len()..=generic_id {
@@ -1763,7 +1930,9 @@ pub(crate) fn build_index(
                 while postings.len() <= search_type_size {
                     postings.push(Vec::new());
                 }
-                postings[search_type_size].push(new_entry_id as u32);
+                if postings[search_type_size].last() != Some(&(new_entry_id as u32)) {
+                    postings[search_type_size].push(new_entry_id as u32);
+                }
             }
         }
     }
diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts
index 3ac10742e41..74f646008eb 100644
--- a/src/librustdoc/html/static/js/rustdoc.d.ts
+++ b/src/librustdoc/html/static/js/rustdoc.d.ts
@@ -270,9 +270,12 @@ declare namespace rustdoc {
      */
     interface TypeData {
         searchUnbox: boolean,
-        invertedFunctionSignatureIndex: RoaringBitmap[],
+        invertedFunctionInputsIndex: RoaringBitmap[],
+        invertedFunctionOutputIndex: RoaringBitmap[],
     }
 
+    type TypeInvertedIndexPolarity = "invertedFunctionInputsIndex" | "invertedFunctionOutputIndex";
+
     /**
      * A search entry of some sort.
      */
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index b003bcc7bf9..fa812a2b67b 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1696,7 +1696,7 @@ class DocSearch {
         }
         /**
          * function_signature, param_names
-         * @type {[string, number] | [number] | [string] | [] | null}
+         * @type {[string, string, number] | [string, string] | [] | null}
          */
         const raw = JSON.parse(encoded);
 
@@ -1705,32 +1705,46 @@ class DocSearch {
         }
 
         let searchUnbox = false;
-        const invertedFunctionSignatureIndex = [];
+        const invertedFunctionInputsIndex = [];
+        const invertedFunctionOutputIndex = [];
 
         if (typeof raw[0] === "string") {
-            if (raw[1]) {
+            if (raw[2]) {
                 searchUnbox = true;
             }
             // the inverted function signature index is a list of bitmaps,
             // by number of types that appear in the function
             let i = 0;
-            const pb = makeUint8ArrayFromBase64(raw[0]);
-            const l = pb.length;
+            let pb = makeUint8ArrayFromBase64(raw[0]);
+            let l = pb.length;
             while (i < l) {
                 if (pb[i] === 0) {
-                    invertedFunctionSignatureIndex.push(RoaringBitmap.empty());
+                    invertedFunctionInputsIndex.push(RoaringBitmap.empty());
                     i += 1;
                 } else {
                     const bitmap = new RoaringBitmap(pb, i);
                     i += bitmap.consumed_len_bytes;
-                    invertedFunctionSignatureIndex.push(bitmap);
+                    invertedFunctionInputsIndex.push(bitmap);
+                }
+            }
+            i = 0;
+            pb = makeUint8ArrayFromBase64(raw[1]);
+            l = pb.length;
+            while (i < l) {
+                if (pb[i] === 0) {
+                    invertedFunctionOutputIndex.push(RoaringBitmap.empty());
+                    i += 1;
+                } else {
+                    const bitmap = new RoaringBitmap(pb, i);
+                    i += bitmap.consumed_len_bytes;
+                    invertedFunctionOutputIndex.push(bitmap);
                 }
             }
         } else if (raw[0]) {
             searchUnbox = true;
         }
 
-        return { searchUnbox, invertedFunctionSignatureIndex };
+        return { searchUnbox, invertedFunctionInputsIndex, invertedFunctionOutputIndex };
     }
 
     /**
@@ -4009,14 +4023,19 @@ class DocSearch {
                  * or anything else. This function returns all possible permutations.
                  *
                  * @param {rustdoc.ParserQueryElement|null} elem
+                 * @param {rustdoc.TypeInvertedIndexPolarity} polarity
                  * @returns {Promise<PostingsList<rustdoc.QueryElement>[]>}
                  */
-                const unpackPostingsList = async elem => {
+                const unpackPostingsList = async(elem, polarity) => {
                     if (!elem) {
                         return empty_postings_list;
                     }
                     const typeFilter = itemTypeFromName(elem.typeFilter);
-                    const searchResults = await index.search(elem.normalizedPathLast);
+                    const [searchResults, upla, uplb] = await Promise.all([
+                        index.search(elem.normalizedPathLast),
+                        unpackPostingsListAll(elem.generics, polarity),
+                        unpackPostingsListBindings(elem.bindings, polarity),
+                    ]);
                     /**
                      * @type {Promise<[
                      *     number,
@@ -4039,7 +4058,7 @@ class DocSearch {
                     const types = (await Promise.all(typePromises))
                         .filter(([_id, name, ty, path]) =>
                             name !== null && name.toLowerCase() === elem.pathLast &&
-                            ty && !ty.invertedFunctionSignatureIndex.every(bitmap => {
+                            ty && !ty[polarity].every(bitmap => {
                                 return bitmap.isEmpty();
                             }) &&
                             path && path.ty !== TY_ASSOCTYPE &&
@@ -4078,7 +4097,7 @@ class DocSearch {
                                         this.getPathData(id),
                                     ]);
                                     if (name !== null && ty !== null && path !== null &&
-                                        !ty.invertedFunctionSignatureIndex.every(bitmap => {
+                                        !ty[polarity].every(bitmap => {
                                             return bitmap.isEmpty();
                                         }) &&
                                         path.ty !== TY_ASSOCTYPE
@@ -4176,18 +4195,16 @@ class DocSearch {
                     /** @type {PostingsList<rustdoc.QueryElement>[]} */
                     const results = [];
                     for (const [id, _name, typeData] of types) {
-                        if (!typeData || typeData.invertedFunctionSignatureIndex.every(bitmap => {
+                        if (!typeData || typeData[polarity].every(bitmap => {
                             return bitmap.isEmpty();
                         })) {
                             continue;
                         }
-                        const upla = await unpackPostingsListAll(elem.generics);
-                        const uplb = await unpackPostingsListBindings(elem.bindings);
                         for (const {invertedIndex: genericsIdx, queryElem: generics} of upla) {
                             for (const {invertedIndex: bindingsIdx, queryElem: bindings} of uplb) {
                                 results.push({
                                     invertedIndex: intersectInvertedIndexes(
-                                        typeData.invertedFunctionSignatureIndex,
+                                        typeData[polarity],
                                         genericsIdx,
                                         bindingsIdx,
                                     ),
@@ -4219,15 +4236,16 @@ class DocSearch {
                  * take the intersection of this bitmap.
                  *
                  * @param {(rustdoc.ParserQueryElement|null)[]|null} elems
+                 * @param {rustdoc.TypeInvertedIndexPolarity} polarity
                  * @returns {Promise<PostingsList<rustdoc.QueryElement[]>[]>}
                  */
-                const unpackPostingsListAll = async elems => {
+                const unpackPostingsListAll = async(elems, polarity) => {
                     if (!elems || elems.length === 0) {
                         return nested_everything_postings_list;
                     }
                     const [firstPostingsList, remainingAll] = await Promise.all([
-                        unpackPostingsList(elems[0]),
-                        unpackPostingsListAll(elems.slice(1)),
+                        unpackPostingsList(elems[0], polarity),
+                        unpackPostingsListAll(elems.slice(1), polarity),
                     ]);
                     /** @type {PostingsList<rustdoc.QueryElement[]>[]} */
                     const results = [];
@@ -4261,11 +4279,12 @@ class DocSearch {
                  * Before passing an actual parser item to it, make sure to clone the map.
                  *
                  * @param {Map<string, rustdoc.ParserQueryElement[]>} elems
+                 * @param {rustdoc.TypeInvertedIndexPolarity} polarity
                  * @returns {Promise<PostingsList<
                  *     Map<number, rustdoc.QueryElement[]>,
                  * >[]>}
                  */
-                const unpackPostingsListBindings = async elems => {
+                const unpackPostingsListBindings = async(elems, polarity) => {
                     if (!elems) {
                         return [{
                             invertedIndex: everything_inverted_index,
@@ -4286,19 +4305,23 @@ class DocSearch {
                             queryElem: new Map(),
                         }];
                     }
-                    const firstKeyIds = await index.search(firstKey);
+                    // HEADS UP!
+                    // We must put this map back the way we found it before returning,
+                    // otherwise things break.
+                    elems.delete(firstKey);
+                    const [firstKeyIds, firstPostingsList, remainingAll] = await Promise.all([
+                        index.search(firstKey),
+                        unpackPostingsListAll(firstList, polarity),
+                        unpackPostingsListBindings(elems, polarity),
+                    ]);
                     if (!firstKeyIds) {
+                        elems.set(firstKey, firstList);
                         // User specified a non-existent key.
                         return [{
                             invertedIndex: empty_inverted_index,
                             queryElem: new Map(),
                         }];
                     }
-                    elems.delete(firstKey);
-                    const [firstPostingsList, remainingAll] = await Promise.all([
-                        unpackPostingsListAll(firstList),
-                        unpackPostingsListBindings(elems),
-                    ]);
                     /** @type {PostingsList<Map<number, rustdoc.QueryElement[]>>[]} */
                     const results = [];
                     for (const keyId of firstKeyIds.matches().entries()) {
@@ -4335,8 +4358,8 @@ class DocSearch {
 
                 // finally, we can do the actual unification loop
                 const [allInputs, allOutput] = await Promise.all([
-                    unpackPostingsListAll(inputs),
-                    unpackPostingsListAll(output),
+                    unpackPostingsListAll(inputs, "invertedFunctionInputsIndex"),
+                    unpackPostingsListAll(output, "invertedFunctionOutputIndex"),
                 ]);
                 let checkCounter = 0;
                 /**
diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml
index 05d5f21c12c..efa99f181b3 100644
--- a/src/tools/build-manifest/Cargo.toml
+++ b/src/tools/build-manifest/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 [dependencies]
 toml = "0.7"
 serde = { version = "1.0", features = ["derive"] }
-serde_json.workspace = true
+serde_json = "1.0"
 anyhow = "1.0.32"
 flate2 = "1.0.26"
 xz2 = "0.1.7"
diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
index 72f5eaf8a4b..c3fc09343db 100644
--- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
+++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
@@ -53,7 +53,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
         | PatKind::Never
         | PatKind::Or(_)
         | PatKind::Err(_) => false,
-        PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
+        PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| unary_pattern(x.pat)),
         PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
         PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
         PatKind::Expr(_) => true,
diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
index 5a7967bbf94..2705ef20b79 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -287,7 +287,7 @@ fn replace_in_pattern(
                 }
                 return or_pat;
             },
-            PatKind::Struct(path, fields, has_dot_dot) => {
+            PatKind::Struct(path, fields, dot_dot) => {
                 let fields = fields
                     .iter()
                     .map(|fld| {
@@ -311,7 +311,7 @@ fn replace_in_pattern(
                     .collect::<Vec<_>>();
                 let fields_string = fields.join(", ");
 
-                let dot_dot_str = if has_dot_dot { " .." } else { "" };
+                let dot_dot_str = if dot_dot.is_some() { " .." } else { "" };
                 let (sn_pth, _) = snippet_with_context(cx, path.span(), span.ctxt(), "", app);
                 return format!("{sn_pth} {{ {fields_string}{dot_dot_str} }}");
             },
diff --git a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
index 2154cd5b24a..ae09c2e87d6 100644
--- a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
@@ -7,7 +7,7 @@ use super::REST_PAT_IN_FULLY_BOUND_STRUCTS;
 
 pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
     if !pat.span.from_expansion()
-        && let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind
+        && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(_)) = pat.kind
         && let Some(def_id) = path.res.opt_def_id()
         && let ty = cx.tcx.type_of(def_id).instantiate_identity()
         && let ty::Adt(def, _) = ty.kind()
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 2113cb92137..ece29362a39 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -754,7 +754,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                 self.ident(name);
                 sub.if_some(|p| self.pat(p));
             },
-            PatKind::Struct(ref qpath, fields, ignore) => {
+            PatKind::Struct(ref qpath, fields, etc) => {
+                let ignore = etc.is_some();
                 bind!(self, qpath, fields);
                 kind!("Struct(ref {qpath}, {fields}, {ignore})");
                 self.qpath(qpath, pat);
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 8533fa85541..011c9b2f931 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -2011,7 +2011,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<
                 false
             }
         },
-        (PatKind::Struct(pat_ident, field_pats, false), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
+        (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
             if field_pats.len() == fields.len() =>
         {
             // check ident
diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml
index 7f2e57ced05..edf9e5c5393 100644
--- a/src/tools/collect-license-metadata/Cargo.toml
+++ b/src/tools/collect-license-metadata/Cargo.toml
@@ -8,5 +8,5 @@ license = "MIT OR Apache-2.0"
 [dependencies]
 anyhow = "1.0.65"
 serde = { version = "1.0.147", features = ["derive"] }
-serde_json.workspace = true
+serde_json = "1.0.85"
 spdx-rs = "0.5.1"
diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml
index fb71275b03c..cdada5a2230 100644
--- a/src/tools/compiletest/Cargo.toml
+++ b/src/tools/compiletest/Cargo.toml
@@ -20,22 +20,22 @@ diff = "0.1.10"
 getopts = "0.2"
 glob = "0.3.0"
 home = "0.5.5"
-indexmap.workspace = true
+indexmap = "2.0.0"
 miropt-test-tools = { path = "../miropt-test-tools" }
 rayon = "1.10.0"
 regex = "1.0"
 rustfix = "0.8.1"
 semver = { version = "1.0.23", features = ["serde"] }
 serde = { version = "1.0", features = ["derive"] }
-serde_json.workspace = true
+serde_json = "1.0"
+tracing = "0.1"
 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["ansi", "env-filter", "fmt", "parking_lot", "smallvec"] }
-tracing.workspace = true
 unified-diff = "0.2.1"
 walkdir = "2"
 # tidy-alphabetical-end
 
 [target.'cfg(unix)'.dependencies]
-libc.workspace = true
+libc = "0.2"
 
 [target.'cfg(windows)'.dependencies]
 miow = "0.6"
diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs
index fdd7155c21f..5519ef1af1f 100644
--- a/src/tools/compiletest/src/executor.rs
+++ b/src/tools/compiletest/src/executor.rs
@@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex, mpsc};
 use std::{env, hint, io, mem, panic, thread};
 
 use crate::common::{Config, TestPaths};
+use crate::panic_hook;
 
 mod deadline;
 mod json;
@@ -120,6 +121,11 @@ fn run_test_inner(
     completion_sender: mpsc::Sender<TestCompletion>,
 ) {
     let is_capture = !runnable_test.config.nocapture;
+
+    // Install a panic-capture buffer for use by the custom panic hook.
+    if is_capture {
+        panic_hook::set_capture_buf(Default::default());
+    }
     let capture_buf = is_capture.then(|| Arc::new(Mutex::new(vec![])));
 
     if let Some(capture_buf) = &capture_buf {
@@ -128,6 +134,13 @@ fn run_test_inner(
 
     let panic_payload = panic::catch_unwind(move || runnable_test.run()).err();
 
+    if let Some(panic_buf) = panic_hook::take_capture_buf() {
+        let panic_buf = panic_buf.lock().unwrap_or_else(|e| e.into_inner());
+        // For now, forward any captured panic message to (captured) stderr.
+        // FIXME(Zalathar): Once we have our own output-capture buffer for
+        // non-panic output, append the panic message to that buffer instead.
+        eprint!("{panic_buf}");
+    }
     if is_capture {
         io::set_output_capture(None);
     }
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 8737fec80bb..fa84691a46f 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -15,6 +15,7 @@ pub mod directives;
 pub mod errors;
 mod executor;
 mod json;
+mod panic_hook;
 mod raise_fd_limit;
 mod read2;
 pub mod runtest;
@@ -493,6 +494,8 @@ pub fn opt_str2(maybestr: Option<String>) -> String {
 pub fn run_tests(config: Arc<Config>) {
     debug!(?config, "run_tests");
 
+    panic_hook::install_panic_hook();
+
     // If we want to collect rustfix coverage information,
     // we first make sure that the coverage file does not exist.
     // It will be created later on.
diff --git a/src/tools/compiletest/src/panic_hook.rs b/src/tools/compiletest/src/panic_hook.rs
new file mode 100644
index 00000000000..1661ca6dabe
--- /dev/null
+++ b/src/tools/compiletest/src/panic_hook.rs
@@ -0,0 +1,136 @@
+use std::backtrace::{Backtrace, BacktraceStatus};
+use std::cell::Cell;
+use std::fmt::{Display, Write};
+use std::panic::PanicHookInfo;
+use std::sync::{Arc, LazyLock, Mutex};
+use std::{env, mem, panic, thread};
+
+type PanicHook = Box<dyn Fn(&PanicHookInfo<'_>) + Sync + Send + 'static>;
+type CaptureBuf = Arc<Mutex<String>>;
+
+thread_local!(
+    static CAPTURE_BUF: Cell<Option<CaptureBuf>> = const { Cell::new(None) };
+);
+
+/// Installs a custom panic hook that will divert panic output to a thread-local
+/// capture buffer, but only for threads that have a capture buffer set.
+///
+/// Otherwise, the custom hook delegates to a copy of the default panic hook.
+pub(crate) fn install_panic_hook() {
+    let default_hook = panic::take_hook();
+    panic::set_hook(Box::new(move |info| custom_panic_hook(&default_hook, info)));
+}
+
+pub(crate) fn set_capture_buf(buf: CaptureBuf) {
+    CAPTURE_BUF.set(Some(buf));
+}
+
+pub(crate) fn take_capture_buf() -> Option<CaptureBuf> {
+    CAPTURE_BUF.take()
+}
+
+fn custom_panic_hook(default_hook: &PanicHook, info: &panic::PanicHookInfo<'_>) {
+    // Temporarily taking the capture buffer means that if a panic occurs in
+    // the subsequent code, that panic will fall back to the default hook.
+    let Some(buf) = take_capture_buf() else {
+        // There was no capture buffer, so delegate to the default hook.
+        default_hook(info);
+        return;
+    };
+
+    let mut out = buf.lock().unwrap_or_else(|e| e.into_inner());
+
+    let thread = thread::current().name().unwrap_or("(test runner)").to_owned();
+    let location = get_location(info);
+    let payload = payload_as_str(info).unwrap_or("Box<dyn Any>");
+    let backtrace = Backtrace::capture();
+
+    writeln!(out, "\nthread '{thread}' panicked at {location}:\n{payload}").unwrap();
+    match backtrace.status() {
+        BacktraceStatus::Captured => {
+            let bt = trim_backtrace(backtrace.to_string());
+            write!(out, "stack backtrace:\n{bt}",).unwrap();
+        }
+        BacktraceStatus::Disabled => {
+            writeln!(
+                out,
+                "note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace",
+            )
+            .unwrap();
+        }
+        _ => {}
+    }
+
+    drop(out);
+    set_capture_buf(buf);
+}
+
+fn get_location<'a>(info: &'a PanicHookInfo<'_>) -> &'a dyn Display {
+    match info.location() {
+        Some(location) => location,
+        None => &"(unknown)",
+    }
+}
+
+/// FIXME(Zalathar): Replace with `PanicHookInfo::payload_as_str` when that's
+/// stable in beta.
+fn payload_as_str<'a>(info: &'a PanicHookInfo<'_>) -> Option<&'a str> {
+    let payload = info.payload();
+    if let Some(s) = payload.downcast_ref::<&str>() {
+        Some(s)
+    } else if let Some(s) = payload.downcast_ref::<String>() {
+        Some(s)
+    } else {
+        None
+    }
+}
+
+fn rust_backtrace_full() -> bool {
+    static RUST_BACKTRACE_FULL: LazyLock<bool> =
+        LazyLock::new(|| matches!(env::var("RUST_BACKTRACE").as_deref(), Ok("full")));
+    *RUST_BACKTRACE_FULL
+}
+
+/// On stable, short backtraces are only available to the default panic hook,
+/// so if we want something similar we have to resort to string processing.
+fn trim_backtrace(full_backtrace: String) -> String {
+    if rust_backtrace_full() {
+        return full_backtrace;
+    }
+
+    let mut buf = String::with_capacity(full_backtrace.len());
+    // Don't print any frames until after the first `__rust_end_short_backtrace`.
+    let mut on = false;
+    // After the short-backtrace state is toggled, skip its associated "at" if present.
+    let mut skip_next_at = false;
+
+    let mut lines = full_backtrace.lines();
+    while let Some(line) = lines.next() {
+        if mem::replace(&mut skip_next_at, false) && line.trim_start().starts_with("at ") {
+            continue;
+        }
+
+        if line.contains("__rust_end_short_backtrace") {
+            on = true;
+            skip_next_at = true;
+            continue;
+        }
+        if line.contains("__rust_begin_short_backtrace") {
+            on = false;
+            skip_next_at = true;
+            continue;
+        }
+
+        if on {
+            writeln!(buf, "{line}").unwrap();
+        }
+    }
+
+    writeln!(
+        buf,
+        "note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace."
+    )
+    .unwrap();
+
+    buf
+}
diff --git a/src/tools/coverage-dump/Cargo.toml b/src/tools/coverage-dump/Cargo.toml
index e491804c257..36a66f16030 100644
--- a/src/tools/coverage-dump/Cargo.toml
+++ b/src/tools/coverage-dump/Cargo.toml
@@ -7,9 +7,9 @@ edition = "2021"
 
 [dependencies]
 anyhow = "1.0.71"
-itertools.workspace = true
+itertools = "0.12"
 leb128 = "0.2.5"
 md5 = { package = "md-5" , version = "0.10.5" }
 miniz_oxide = "0.8.8"
 regex = "1.8.4"
-rustc-demangle.workspace = true
+rustc-demangle = "0.1.23"
diff --git a/src/tools/features-status-dump/Cargo.toml b/src/tools/features-status-dump/Cargo.toml
index d72555da486..b2976f14a01 100644
--- a/src/tools/features-status-dump/Cargo.toml
+++ b/src/tools/features-status-dump/Cargo.toml
@@ -8,5 +8,5 @@ edition = "2021"
 anyhow = { version = "1" }
 clap = { version = "4", features = ["derive"] }
 serde = { version = "1.0.125", features = [ "derive" ] }
-serde_json.workspace = true
+serde_json = "1.0.59"
 tidy = { path = "../tidy", features = ["build-metrics"] }
diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml
index 5edf1f3d88b..bcb3165de45 100644
--- a/src/tools/generate-copyright/Cargo.toml
+++ b/src/tools/generate-copyright/Cargo.toml
@@ -11,5 +11,5 @@ anyhow = "1.0.65"
 askama = "0.14.0"
 cargo_metadata = "0.21"
 serde = { version = "1.0.147", features = ["derive"] }
-serde_json.workspace = true
+serde_json = "1.0.85"
 thiserror = "1"
diff --git a/src/tools/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml
index 92fde363882..80fc26cbe66 100644
--- a/src/tools/jsondocck/Cargo.toml
+++ b/src/tools/jsondocck/Cargo.toml
@@ -8,5 +8,5 @@ jsonpath-rust = "1.0.0"
 getopts = "0.2"
 regex = "1.4"
 shlex = "1.0"
-serde_json.workspace = true
+serde_json = "1.0"
 fs-err = "2.5.0"
diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml
index 44beaf2ddfd..cc8ecefd530 100644
--- a/src/tools/jsondoclint/Cargo.toml
+++ b/src/tools/jsondoclint/Cargo.toml
@@ -9,7 +9,7 @@ edition = "2021"
 anyhow = "1.0.62"
 clap = { version = "4.0.15", features = ["derive"] }
 fs-err = "2.8.1"
-rustc-hash.workspace = true
+rustc-hash = "2.0.0"
 rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" }
 serde = { version = "1.0", features = ["derive"] }
-serde_json.workspace = true
+serde_json = "1.0.85"
diff --git a/src/tools/lint-docs/Cargo.toml b/src/tools/lint-docs/Cargo.toml
index acafe17cb0c..6e1ab84ed18 100644
--- a/src/tools/lint-docs/Cargo.toml
+++ b/src/tools/lint-docs/Cargo.toml
@@ -8,6 +8,6 @@ description = "A script to extract the lint documentation for the rustc book."
 
 [dependencies]
 rustc-literal-escaper = "0.0.5"
-serde_json.workspace = true
-tempfile.workspace = true
+serde_json = "1.0.57"
+tempfile = "3.1.0"
 walkdir = "2.3.1"
diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs
index b33344ca5dd..bc38e931fe5 100644
--- a/src/tools/lint-docs/src/lib.rs
+++ b/src/tools/lint-docs/src/lib.rs
@@ -59,6 +59,8 @@ pub struct LintExtractor<'a> {
     pub rustc_target: &'a str,
     /// The target linker overriding `rustc`'s default
     pub rustc_linker: Option<&'a str>,
+    /// Stage of the compiler that builds the docs (the stage of `rustc_path`).
+    pub build_rustc_stage: u32,
     /// Verbose output.
     pub verbose: bool,
     /// Validate the style and the code example.
@@ -216,14 +218,7 @@ impl<'a> LintExtractor<'a> {
                         if let Some(text) = line.strip_prefix("/// ") {
                             doc_lines.push(text.to_string());
                         } else if let Some(text) = line.strip_prefix("#[doc = \"") {
-                            let escaped = text.strip_suffix("\"]").unwrap();
-                            let mut buf = String::new();
-                            unescape_str(escaped, |_, res| match res {
-                                Ok(c) => buf.push(c),
-                                Err(err) => {
-                                    assert!(!err.is_fatal(), "failed to unescape string literal")
-                                }
-                            });
+                            let buf = parse_doc_string(text);
                             doc_lines.push(buf);
                         } else if line == "///" {
                             doc_lines.push("".to_string());
@@ -234,6 +229,20 @@ impl<'a> LintExtractor<'a> {
                             // Ignore allow of lints (useful for
                             // invalid_rust_codeblocks).
                             continue;
+                        } else if let Some(text) =
+                            line.strip_prefix("#[cfg_attr(not(bootstrap), doc = \"")
+                        {
+                            if self.build_rustc_stage >= 1 {
+                                let buf = parse_doc_string(text);
+                                doc_lines.push(buf);
+                            }
+                        } else if let Some(text) =
+                            line.strip_prefix("#[cfg_attr(bootstrap, doc = \"")
+                        {
+                            if self.build_rustc_stage == 0 {
+                                let buf = parse_doc_string(text);
+                                doc_lines.push(buf);
+                            }
                         } else {
                             let name = lint_name(line).map_err(|e| {
                                 format!(
@@ -580,6 +589,23 @@ impl<'a> LintExtractor<'a> {
     }
 }
 
+/// Parses a doc string that follows `#[doc = "`.
+fn parse_doc_string(text: &str) -> String {
+    let escaped = text.strip_suffix("]").unwrap_or(text);
+    let escaped = escaped.strip_suffix(")").unwrap_or(escaped).strip_suffix("\"");
+    let Some(escaped) = escaped else {
+        panic!("Cannot extract docstring content from {text}");
+    };
+    let mut buf = String::new();
+    unescape_str(escaped, |_, res| match res {
+        Ok(c) => buf.push(c),
+        Err(err) => {
+            assert!(!err.is_fatal(), "failed to unescape string literal")
+        }
+    });
+    buf
+}
+
 /// Adds `Lint`s that have been renamed.
 fn add_renamed_lints(lints: &mut Vec<Lint>) {
     for (level, names) in RENAMES {
diff --git a/src/tools/lint-docs/src/main.rs b/src/tools/lint-docs/src/main.rs
index e377283b1a4..1933ce4d2f1 100644
--- a/src/tools/lint-docs/src/main.rs
+++ b/src/tools/lint-docs/src/main.rs
@@ -25,6 +25,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
     let mut args = std::env::args().skip(1);
     let mut src_path = None;
     let mut out_path = None;
+    let mut build_rustc_stage = None;
     let mut rustc_path = None;
     let mut rustc_target = None;
     let mut rustc_linker = None;
@@ -32,6 +33,14 @@ fn doit() -> Result<(), Box<dyn Error>> {
     let mut validate = false;
     while let Some(arg) = args.next() {
         match arg.as_str() {
+            "--build-rustc-stage" => {
+                build_rustc_stage = match args.next() {
+                    Some(s) => {
+                        Some(s.parse::<u32>().expect("build rustc stage has to be an integer"))
+                    }
+                    None => return Err("--build-rustc-stage requires a value".into()),
+                };
+            }
             "--src" => {
                 src_path = match args.next() {
                     Some(s) => Some(PathBuf::from(s)),
@@ -67,6 +76,9 @@ fn doit() -> Result<(), Box<dyn Error>> {
             s => return Err(format!("unexpected argument `{}`", s).into()),
         }
     }
+    if build_rustc_stage.is_none() {
+        return Err("--build-rustc-stage must be specified to the stage of the compiler that generates the docs".into());
+    }
     if src_path.is_none() {
         return Err("--src must be specified to the directory with the compiler source".into());
     }
@@ -85,6 +97,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
         rustc_path: &rustc_path.unwrap(),
         rustc_target: &rustc_target.unwrap(),
         rustc_linker: rustc_linker.as_deref(),
+        build_rustc_stage: build_rustc_stage.unwrap(),
         verbose,
         validate,
     };
diff --git a/src/tools/llvm-bitcode-linker/Cargo.toml b/src/tools/llvm-bitcode-linker/Cargo.toml
index f78f8b618d3..a9210b562f3 100644
--- a/src/tools/llvm-bitcode-linker/Cargo.toml
+++ b/src/tools/llvm-bitcode-linker/Cargo.toml
@@ -8,7 +8,7 @@ publish = false
 
 [dependencies]
 anyhow = "1.0"
-tracing.workspace = true
-tracing-subscriber = { version = "0.3.0", features = ["std"] }
+tracing = "0.1"
+tracing-subscriber = {version = "0.3.0", features = ["std"] }
 clap = { version = "4.3", features = ["derive"] }
 thiserror = "1.0.24"
diff --git a/src/tools/nix-dev-shell/shell.nix b/src/tools/nix-dev-shell/shell.nix
index ad33b121f97..6ca8a7c4652 100644
--- a/src/tools/nix-dev-shell/shell.nix
+++ b/src/tools/nix-dev-shell/shell.nix
@@ -14,6 +14,7 @@ pkgs.mkShell {
   packages = [
     pkgs.git
     pkgs.nix
+    pkgs.glibc.out
     pkgs.glibc.static
     x
     # Get the runtime deps of the x wrapper
@@ -23,5 +24,7 @@ pkgs.mkShell {
     # Avoid creating text files for ICEs.
     RUSTC_ICE = 0;
     SSL_CERT_FILE = cacert;
+    # cargo seems to dlopen libcurl, so we need it in the ld library path
+    LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath [pkgs.stdenv.cc.cc.lib pkgs.curl]}";
   };
 }
diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml
index b2833a9d7f1..f4051ae67d7 100644
--- a/src/tools/opt-dist/Cargo.toml
+++ b/src/tools/opt-dist/Cargo.toml
@@ -15,9 +15,9 @@ fs_extra = "1"
 camino = "1"
 tar = "0.4"
 xz = { version = "0.1", package = "xz2" }
-serde_json.workspace = true
+serde_json = "1"
 glob = "0.3"
-tempfile.workspace = true
+tempfile = "3.5"
 derive_builder = "0.20"
 clap = { version = "4", features = ["derive"] }
 tabled = { version = "0.15", default-features = false, features = ["std"] }
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index 86ac4b9d7b4..250e0f65a9f 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -12,10 +12,10 @@ edition = "2024"
 # tidy-alphabetical-start
 bstr = "1.12"
 gimli = "0.32"
-libc.workspace = true
+libc = "0.2"
 object = "0.37"
 regex = "1.11"
-serde_json.workspace = true
+serde_json = "1.0"
 similar = "2.7"
 wasmparser = { version = "0.236", default-features = false, features = ["std", "features", "validate"] }
 # tidy-alphabetical-end
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index d212ecf392a..848bd0766e7 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -303,7 +303,7 @@ impl Rewrite for Pat {
                 qself,
                 path,
                 fields,
-                rest == ast::PatFieldsRest::Rest,
+                matches!(rest, ast::PatFieldsRest::Rest(_)),
                 self.span,
                 context,
                 shape,
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index f43733665ed..c1f27de7ed4 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -14,7 +14,7 @@ ignore = "0.4.18"
 semver = "1.0"
 serde = { version = "1.0.125", features = ["derive"], optional = true }
 termcolor = "1.1.3"
-rustc-hash.workspace = true
+rustc-hash = "2.0.0"
 fluent-syntax = "0.12"
 similar = "2.5.0"
 toml = "0.7.8"
diff --git a/tests/codegen-llvm/async-fn-debug-awaitee-field.rs b/tests/codegen-llvm/async-fn-debug-awaitee-field.rs
index 50860c90662..b9d3d9ee66e 100644
--- a/tests/codegen-llvm/async-fn-debug-awaitee-field.rs
+++ b/tests/codegen-llvm/async-fn-debug-awaitee-field.rs
@@ -18,11 +18,11 @@ pub async fn async_fn_test() {
 
 pub async fn foo() {}
 
+// NONMSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]],
+// MSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>",
+// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo",
 // NONMSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[GEN_SCOPE:![0-9]*]],
 // MSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::async_fn_test::async_fn_env$0>",
 // NONMSVC: [[GEN_SCOPE:!.*]] = !DINamespace(name: "async_fn_test",
 // CHECK: [[SUSPEND_STRUCT:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend0", scope: [[GEN]],
-// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE:![0-9]*]],
-// NONMSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]],
-// MSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>",
-// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo",
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE]],
diff --git a/tests/codegen-llvm/cffi/c-variadic-ffi.rs b/tests/codegen-llvm/cffi/c-variadic-ffi.rs
index 3e99c9fb84e..1dee477e9ed 100644
--- a/tests/codegen-llvm/cffi/c-variadic-ffi.rs
+++ b/tests/codegen-llvm/cffi/c-variadic-ffi.rs
@@ -14,7 +14,7 @@
 //@[arm32] needs-llvm-components: arm
 #![crate_type = "lib"]
 #![feature(no_core)]
-#![feature(extended_varargs_abi_support, extern_system_varargs)]
+#![feature(extern_system_varargs)]
 #![no_core]
 
 extern crate minicore;
diff --git a/tests/codegen-units/item-collection/async-fn-impl.rs b/tests/codegen-units/item-collection/async-fn-impl.rs
new file mode 100644
index 00000000000..540fa0b3228
--- /dev/null
+++ b/tests/codegen-units/item-collection/async-fn-impl.rs
@@ -0,0 +1,10 @@
+//@ edition: 2024
+// When pub async fn is monomorphized, its implementation coroutine is also monomorphized
+//@ compile-flags: --crate-type=lib
+
+//~ MONO_ITEM fn async_fn @@
+//~ MONO_ITEM fn async_fn::{closure#0} @@
+#[unsafe(no_mangle)]
+pub async fn async_fn(x: u64) -> bool {
+    true
+}
diff --git a/tests/codegen-units/item-collection/opaque-return-impls.rs b/tests/codegen-units/item-collection/opaque-return-impls.rs
new file mode 100644
index 00000000000..7d5f4f5b669
--- /dev/null
+++ b/tests/codegen-units/item-collection/opaque-return-impls.rs
@@ -0,0 +1,89 @@
+//@ only-x86_64-unknown-linux-gnu
+//@ compile-flags: -C panic=abort -Zinline-mir=no -Copt-level=0 -Zcross-crate-inline-threshold=never -Zmir-opt-level=0 -Cno-prepopulate-passes
+//@ no-prefer-dynamic
+//@ edition:2024
+#![crate_type = "lib"]
+
+trait TestTrait {
+    fn test_func(&self);
+}
+
+struct TestStruct {}
+
+impl TestTrait for TestStruct {
+    fn test_func(&self) {
+        println!("TestStruct::test_func");
+    }
+}
+
+#[inline(never)]
+pub fn foo() -> impl TestTrait {
+    TestStruct {}
+}
+
+//~ MONO_ITEM fn foo
+//~ MONO_ITEM fn <TestStruct as TestTrait>::test_func
+
+trait TestTrait2 {
+    fn test_func2(&self);
+}
+
+struct TestStruct2 {}
+
+impl TestTrait2 for TestStruct2 {
+    fn test_func2(&self) {
+        println!("TestStruct2::test_func2");
+    }
+}
+
+#[inline(never)]
+pub fn foo2() -> Box<dyn TestTrait2> {
+    Box::new(TestStruct2 {})
+}
+
+//~ MONO_ITEM fn <TestStruct2 as TestTrait2>::test_func2
+//~ MONO_ITEM fn alloc::alloc::exchange_malloc
+//~ MONO_ITEM fn foo2
+//~ MONO_ITEM fn std::alloc::Global::alloc_impl
+//~ MONO_ITEM fn std::boxed::Box::<TestStruct2>::new
+//~ MONO_ITEM fn std::alloc::Layout::from_size_align_unchecked::precondition_check
+//~ MONO_ITEM fn std::ptr::NonNull::<T>::new_unchecked::precondition_check
+
+struct Counter {
+    count: usize,
+}
+
+impl Counter {
+    fn new() -> Counter {
+        Counter { count: 0 }
+    }
+}
+
+impl Iterator for Counter {
+    type Item = usize;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.count += 1;
+        if self.count < 6 { Some(self.count) } else { None }
+    }
+}
+
+#[inline(never)]
+pub fn foo3() -> Box<dyn Iterator<Item = usize>> {
+    Box::new(Counter::new())
+}
+
+//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by
+//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0}
+//~ MONO_ITEM fn <Counter as std::iter::Iterator>::advance_by
+//~ MONO_ITEM fn <Counter as std::iter::Iterator>::next
+//~ MONO_ITEM fn <Counter as std::iter::Iterator>::nth
+//~ MONO_ITEM fn <Counter as std::iter::Iterator>::size_hint
+//~ MONO_ITEM fn <Counter as std::iter::Iterator>::try_fold::<std::num::NonZero<usize>, {closure@<Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0}}, std::option::Option<std::num::NonZero<usize>>>
+//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::FromResidual<std::option::Option<std::convert::Infallible>>>::from_residual
+//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::branch
+//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::from_output
+//~ MONO_ITEM fn foo3
+//~ MONO_ITEM fn std::boxed::Box::<Counter>::new
+//~ MONO_ITEM fn Counter::new
+//~ MONO_ITEM fn core::fmt::rt::<impl std::fmt::Arguments<'_>>::new_const::<1>
diff --git a/tests/coverage/async.cov-map b/tests/coverage/async.cov-map
index c528ad525b5..d75c4b5981a 100644
--- a/tests/coverage/async.cov-map
+++ b/tests/coverage/async.cov-map
@@ -103,21 +103,21 @@ Number of file 0 mappings: 3
 Highest counter ID seen: (none)
 
 Function name: async::g
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 16]
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 12]
 Number of files: 1
 - file 0 => $DIR/async.rs
 Number of expressions: 0
 Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 22)
+- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 18)
 Highest counter ID seen: c0
 
 Function name: async::g::{closure#0} (unused)
-Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 17, 00, 18, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
+Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 13, 00, 14, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => $DIR/async.rs
 Number of expressions: 0
 Number of file 0 mappings: 12
-- Code(Zero) at (prev + 27, 23) to (start + 0, 24)
+- Code(Zero) at (prev + 27, 19) to (start + 0, 20)
 - Code(Zero) at (prev + 1, 11) to (start + 0, 12)
 - Code(Zero) at (prev + 1, 9) to (start + 0, 10)
 - Code(Zero) at (prev + 0, 14) to (start + 0, 23)
diff --git a/tests/coverage/async.coverage b/tests/coverage/async.coverage
index 9fca1b6997d..9409e6b1deb 100644
--- a/tests/coverage/async.coverage
+++ b/tests/coverage/async.coverage
@@ -24,8 +24,8 @@
    LL|       |
    LL|      0|async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()`
    LL|       |
-   LL|      1|pub async fn g(x: u8) {
-                                    ^0
+   LL|      1|async fn g(x: u8) {
+                                ^0
    LL|      0|    match x {
    LL|      0|        y if e().await == y => (),
    LL|      0|        y if f().await == y => (),
diff --git a/tests/coverage/async.rs b/tests/coverage/async.rs
index da0a1c0b6f0..777ad7ce7c0 100644
--- a/tests/coverage/async.rs
+++ b/tests/coverage/async.rs
@@ -24,7 +24,7 @@ async fn f() -> u8 { 1 }
 
 async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()`
 
-pub async fn g(x: u8) {
+async fn g(x: u8) {
     match x {
         y if e().await == y => (),
         y if f().await == y => (),
diff --git a/tests/coverage/closure.cov-map b/tests/coverage/closure.cov-map
index 5f7e4ce58e9..ee8934f0a84 100644
--- a/tests/coverage/closure.cov-map
+++ b/tests/coverage/closure.cov-map
@@ -118,21 +118,23 @@ Number of file 0 mappings: 4
 Highest counter ID seen: (none)
 
 Function name: closure::main::{closure#12} (unused)
-Raw bytes (10): 0x[01, 01, 00, 01, 00, a7, 01, 0a, 00, 16]
+Raw bytes (15): 0x[01, 01, 00, 02, 00, a7, 01, 01, 00, 09, 00, 00, 0a, 00, 16]
 Number of files: 1
 - file 0 => $DIR/closure.rs
 Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Zero) at (prev + 167, 10) to (start + 0, 22)
+Number of file 0 mappings: 2
+- Code(Zero) at (prev + 167, 1) to (start + 0, 9)
+- Code(Zero) at (prev + 0, 10) to (start + 0, 22)
 Highest counter ID seen: (none)
 
 Function name: closure::main::{closure#13} (unused)
-Raw bytes (10): 0x[01, 01, 00, 01, 00, ad, 01, 11, 00, 1d]
+Raw bytes (15): 0x[01, 01, 00, 02, 00, ac, 01, 0d, 00, 15, 00, 01, 11, 00, 1d]
 Number of files: 1
 - file 0 => $DIR/closure.rs
 Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Zero) at (prev + 173, 17) to (start + 0, 29)
+Number of file 0 mappings: 2
+- Code(Zero) at (prev + 172, 13) to (start + 0, 21)
+- Code(Zero) at (prev + 1, 17) to (start + 0, 29)
 Highest counter ID seen: (none)
 
 Function name: closure::main::{closure#14}
@@ -289,30 +291,33 @@ Number of file 0 mappings: 7
 Highest counter ID seen: (none)
 
 Function name: closure::main::{closure#5}
-Raw bytes (10): 0x[01, 01, 00, 01, 01, 8c, 01, 46, 00, 4e]
+Raw bytes (15): 0x[01, 01, 00, 02, 01, 8c, 01, 3d, 00, 45, 01, 00, 46, 00, 4e]
 Number of files: 1
 - file 0 => $DIR/closure.rs
 Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 140, 70) to (start + 0, 78)
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 140, 61) to (start + 0, 69)
+- Code(Counter(0)) at (prev + 0, 70) to (start + 0, 78)
 Highest counter ID seen: c0
 
 Function name: closure::main::{closure#6}
-Raw bytes (10): 0x[01, 01, 00, 01, 01, 8d, 01, 4a, 00, 56]
+Raw bytes (15): 0x[01, 01, 00, 02, 01, 8d, 01, 41, 00, 49, 01, 00, 4a, 00, 56]
 Number of files: 1
 - file 0 => $DIR/closure.rs
 Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 141, 74) to (start + 0, 86)
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 141, 65) to (start + 0, 73)
+- Code(Counter(0)) at (prev + 0, 74) to (start + 0, 86)
 Highest counter ID seen: c0
 
 Function name: closure::main::{closure#7} (unused)
-Raw bytes (10): 0x[01, 01, 00, 01, 00, 8e, 01, 44, 00, 50]
+Raw bytes (15): 0x[01, 01, 00, 02, 00, 8e, 01, 3b, 00, 43, 00, 00, 44, 00, 50]
 Number of files: 1
 - file 0 => $DIR/closure.rs
 Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Zero) at (prev + 142, 68) to (start + 0, 80)
+Number of file 0 mappings: 2
+- Code(Zero) at (prev + 142, 59) to (start + 0, 67)
+- Code(Zero) at (prev + 0, 68) to (start + 0, 80)
 Highest counter ID seen: (none)
 
 Function name: closure::main::{closure#8} (unused)
diff --git a/tests/coverage/closure.coverage b/tests/coverage/closure.coverage
index 14285269228..d44ecf5a69e 100644
--- a/tests/coverage/closure.coverage
+++ b/tests/coverage/closure.coverage
@@ -139,9 +139,9 @@
    LL|       |
    LL|      1|    let short_used_covered_closure_macro = | used_arg: u8 | println!("called");
    LL|      1|    let short_used_not_covered_closure_macro = | used_arg: u8 | println!("not called");
-                                                                                       ^0
+                                                                              ^0       ^0
    LL|      1|    let _short_unused_closure_macro = | _unused_arg: u8 | println!("not called");
-                                                                                 ^0
+                                                                        ^0       ^0
    LL|       |
    LL|       |
    LL|       |
@@ -173,7 +173,7 @@
    LL|       |
    LL|      1|    let _short_unused_closure_line_break_no_block2 =
    LL|       |        | _unused_arg: u8 |
-   LL|       |            println!(
+   LL|      0|            println!(
    LL|      0|                "not called"
    LL|       |            )
    LL|       |    ;
diff --git a/tests/coverage/macro_in_closure.cov-map b/tests/coverage/macro_in_closure.cov-map
index 4544aa50143..3529d0c4c32 100644
--- a/tests/coverage/macro_in_closure.cov-map
+++ b/tests/coverage/macro_in_closure.cov-map
@@ -1,10 +1,11 @@
 Function name: macro_in_closure::NO_BLOCK::{closure#0}
-Raw bytes (9): 0x[01, 01, 00, 01, 01, 07, 25, 00, 2c]
+Raw bytes (14): 0x[01, 01, 00, 02, 01, 07, 1c, 00, 24, 01, 00, 25, 00, 2c]
 Number of files: 1
 - file 0 => $DIR/macro_in_closure.rs
 Number of expressions: 0
-Number of file 0 mappings: 1
-- Code(Counter(0)) at (prev + 7, 37) to (start + 0, 44)
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 7, 28) to (start + 0, 36)
+- Code(Counter(0)) at (prev + 0, 37) to (start + 0, 44)
 Highest counter ID seen: c0
 
 Function name: macro_in_closure::WITH_BLOCK::{closure#0}
diff --git a/tests/coverage/rustfmt-skip.cov-map b/tests/coverage/rustfmt-skip.cov-map
new file mode 100644
index 00000000000..bb673a411bf
--- /dev/null
+++ b/tests/coverage/rustfmt-skip.cov-map
@@ -0,0 +1,12 @@
+Function name: rustfmt_skip::main
+Raw bytes (24): 0x[01, 01, 00, 04, 01, 0a, 01, 00, 0a, 01, 02, 05, 00, 0d, 01, 03, 09, 00, 10, 01, 02, 01, 00, 02]
+Number of files: 1
+- file 0 => $DIR/rustfmt-skip.rs
+Number of expressions: 0
+Number of file 0 mappings: 4
+- Code(Counter(0)) at (prev + 10, 1) to (start + 0, 10)
+- Code(Counter(0)) at (prev + 2, 5) to (start + 0, 13)
+- Code(Counter(0)) at (prev + 3, 9) to (start + 0, 16)
+- Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2)
+Highest counter ID seen: c0
+
diff --git a/tests/coverage/rustfmt-skip.coverage b/tests/coverage/rustfmt-skip.coverage
new file mode 100644
index 00000000000..b7276cf0ee8
--- /dev/null
+++ b/tests/coverage/rustfmt-skip.coverage
@@ -0,0 +1,18 @@
+   LL|       |//@ edition: 2024
+   LL|       |
+   LL|       |// The presence of `#[rustfmt::skip]` on a function should not cause macros
+   LL|       |// within that function to mysteriously not be instrumented.
+   LL|       |//
+   LL|       |// This test detects problems that can occur when building an expansion tree
+   LL|       |// based on `ExpnData::parent` instead of `ExpnData::call_site`, for example.
+   LL|       |
+   LL|       |#[rustfmt::skip]
+   LL|      1|fn main() {
+   LL|       |    // Ensure a gap between the body start and the first statement.
+   LL|      1|    println!(
+   LL|       |        // Keep this on a separate line, to distinguish instrumentation of
+   LL|       |        // `println!` from instrumentation of its arguments.
+   LL|      1|        "hello"
+   LL|       |    );
+   LL|      1|}
+
diff --git a/tests/coverage/rustfmt-skip.rs b/tests/coverage/rustfmt-skip.rs
new file mode 100644
index 00000000000..6f6874c9aa0
--- /dev/null
+++ b/tests/coverage/rustfmt-skip.rs
@@ -0,0 +1,17 @@
+//@ edition: 2024
+
+// The presence of `#[rustfmt::skip]` on a function should not cause macros
+// within that function to mysteriously not be instrumented.
+//
+// This test detects problems that can occur when building an expansion tree
+// based on `ExpnData::parent` instead of `ExpnData::call_site`, for example.
+
+#[rustfmt::skip]
+fn main() {
+    // Ensure a gap between the body start and the first statement.
+    println!(
+        // Keep this on a separate line, to distinguish instrumentation of
+        // `println!` from instrumentation of its arguments.
+        "hello"
+    );
+}
diff --git a/tests/mir-opt/building/loop_match_diverges.break_to_block_unit.built.after.mir b/tests/mir-opt/building/loop_match_diverges.break_to_block_unit.built.after.mir
new file mode 100644
index 00000000000..6d779e46146
--- /dev/null
+++ b/tests/mir-opt/building/loop_match_diverges.break_to_block_unit.built.after.mir
@@ -0,0 +1,63 @@
+// MIR for `break_to_block_unit` after built
+
+fn break_to_block_unit() -> u8 {
+    let mut _0: u8;
+    let mut _1: i32;
+    let mut _2: !;
+    scope 1 {
+        debug state => _1;
+    }
+
+    bb0: {
+        StorageLive(_1);
+        _1 = const 0_i32;
+        FakeRead(ForLet(None), _1);
+        StorageLive(_2);
+        goto -> bb1;
+    }
+
+    bb1: {
+        falseUnwind -> [real: bb2, unwind: bb10];
+    }
+
+    bb2: {
+        PlaceMention(_1);
+        _1 = const 2_i32;
+        goto -> bb5;
+    }
+
+    bb3: {
+        FakeRead(ForMatchedPlace(None), _1);
+        unreachable;
+    }
+
+    bb4: {
+        goto -> bb6;
+    }
+
+    bb5: {
+        goto -> bb6;
+    }
+
+    bb6: {
+        goto -> bb7;
+    }
+
+    bb7: {
+        goto -> bb1;
+    }
+
+    bb8: {
+        unreachable;
+    }
+
+    bb9: {
+        StorageDead(_2);
+        StorageDead(_1);
+        return;
+    }
+
+    bb10 (cleanup): {
+        resume;
+    }
+}
diff --git a/tests/mir-opt/building/loop_match_diverges.infinite_a.built.after.mir b/tests/mir-opt/building/loop_match_diverges.infinite_a.built.after.mir
new file mode 100644
index 00000000000..e3766744790
--- /dev/null
+++ b/tests/mir-opt/building/loop_match_diverges.infinite_a.built.after.mir
@@ -0,0 +1,51 @@
+// MIR for `infinite_a` after built
+
+fn infinite_a(_1: u8) -> () {
+    debug state => _1;
+    let mut _0: ();
+    let mut _2: !;
+    let _3: u8;
+    scope 1 {
+        debug a => _3;
+    }
+
+    bb0: {
+        StorageLive(_2);
+        goto -> bb1;
+    }
+
+    bb1: {
+        falseUnwind -> [real: bb2, unwind: bb7];
+    }
+
+    bb2: {
+        PlaceMention(_1);
+        StorageLive(_3);
+        _3 = copy _1;
+        _1 = copy _3;
+        StorageDead(_3);
+        goto -> bb4;
+    }
+
+    bb3: {
+        FakeRead(ForMatchedPlace(None), _1);
+        unreachable;
+    }
+
+    bb4: {
+        goto -> bb1;
+    }
+
+    bb5: {
+        unreachable;
+    }
+
+    bb6: {
+        StorageDead(_2);
+        return;
+    }
+
+    bb7 (cleanup): {
+        resume;
+    }
+}
diff --git a/tests/mir-opt/building/loop_match_diverges.rs b/tests/mir-opt/building/loop_match_diverges.rs
new file mode 100644
index 00000000000..774e195c33c
--- /dev/null
+++ b/tests/mir-opt/building/loop_match_diverges.rs
@@ -0,0 +1,68 @@
+// skip-filecheck
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+// Test that a #[loop_match] without an explicit break from the loop generates valid MIR.
+
+enum State {
+    A,
+    B,
+    C,
+}
+
+// EMIT_MIR loop_match_diverges.simple.built.after.mir
+fn simple(mut state: State) -> State {
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    break 'blk State::B;
+                }
+                State::B => {
+                    if true {
+                        #[const_continue]
+                        break 'blk State::C;
+                    } else {
+                        #[const_continue]
+                        break 'blk State::A;
+                    }
+                }
+                State::C => break 'a,
+            }
+        };
+    }
+
+    state
+}
+
+// EMIT_MIR loop_match_diverges.break_to_block_unit.built.after.mir
+#[unsafe(no_mangle)]
+fn break_to_block_unit() -> u8 {
+    let mut state = 0;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                _ => 'b: {
+                    break 'b 2;
+                }
+            }
+        }
+    }
+}
+
+// EMIT_MIR loop_match_diverges.infinite_a.built.after.mir
+#[unsafe(no_mangle)]
+fn infinite_a(mut state: u8) {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                a => a,
+            }
+        }
+    }
+}
diff --git a/tests/mir-opt/building/loop_match_diverges.simple.built.after.mir b/tests/mir-opt/building/loop_match_diverges.simple.built.after.mir
new file mode 100644
index 00000000000..26476ad7762
--- /dev/null
+++ b/tests/mir-opt/building/loop_match_diverges.simple.built.after.mir
@@ -0,0 +1,187 @@
+// MIR for `simple` after built
+
+fn simple(_1: State) -> State {
+    debug state => _1;
+    let mut _0: State;
+    let _2: ();
+    let mut _3: isize;
+    let mut _4: !;
+    let mut _5: isize;
+    let mut _6: bool;
+    let mut _7: !;
+    let mut _8: isize;
+    let mut _9: !;
+    let mut _10: isize;
+    let mut _11: !;
+
+    bb0: {
+        StorageLive(_2);
+        goto -> bb1;
+    }
+
+    bb1: {
+        falseUnwind -> [real: bb2, unwind: bb37];
+    }
+
+    bb2: {
+        PlaceMention(_1);
+        _3 = discriminant(_1);
+        switchInt(move _3) -> [0: bb4, 1: bb6, 2: bb8, otherwise: bb3];
+    }
+
+    bb3: {
+        FakeRead(ForMatchedPlace(None), _1);
+        unreachable;
+    }
+
+    bb4: {
+        falseEdge -> [real: bb11, imaginary: bb6];
+    }
+
+    bb5: {
+        goto -> bb3;
+    }
+
+    bb6: {
+        falseEdge -> [real: bb10, imaginary: bb8];
+    }
+
+    bb7: {
+        goto -> bb3;
+    }
+
+    bb8: {
+        _2 = const ();
+        goto -> bb36;
+    }
+
+    bb9: {
+        goto -> bb3;
+    }
+
+    bb10: {
+        StorageLive(_6);
+        _6 = const true;
+        switchInt(move _6) -> [0: bb17, otherwise: bb16];
+    }
+
+    bb11: {
+        _1 = State::B;
+        _5 = discriminant(_1);
+        falseEdge -> [real: bb12, imaginary: bb13];
+    }
+
+    bb12: {
+        goto -> bb10;
+    }
+
+    bb13: {
+        goto -> bb34;
+    }
+
+    bb14: {
+        unreachable;
+    }
+
+    bb15: {
+        goto -> bb32;
+    }
+
+    bb16: {
+        _1 = State::C;
+        _8 = discriminant(_1);
+        falseEdge -> [real: bb18, imaginary: bb19];
+    }
+
+    bb17: {
+        goto -> bb23;
+    }
+
+    bb18: {
+        goto -> bb20;
+    }
+
+    bb19: {
+        goto -> bb33;
+    }
+
+    bb20: {
+        StorageDead(_6);
+        goto -> bb8;
+    }
+
+    bb21: {
+        unreachable;
+    }
+
+    bb22: {
+        goto -> bb29;
+    }
+
+    bb23: {
+        _1 = State::A;
+        _10 = discriminant(_1);
+        falseEdge -> [real: bb24, imaginary: bb25];
+    }
+
+    bb24: {
+        goto -> bb26;
+    }
+
+    bb25: {
+        goto -> bb33;
+    }
+
+    bb26: {
+        StorageDead(_6);
+        goto -> bb11;
+    }
+
+    bb27: {
+        unreachable;
+    }
+
+    bb28: {
+        goto -> bb29;
+    }
+
+    bb29: {
+        StorageDead(_6);
+        goto -> bb32;
+    }
+
+    bb30: {
+        unreachable;
+    }
+
+    bb31: {
+        goto -> bb32;
+    }
+
+    bb32: {
+        goto -> bb35;
+    }
+
+    bb33: {
+        StorageDead(_6);
+        goto -> bb34;
+    }
+
+    bb34: {
+        goto -> bb35;
+    }
+
+    bb35: {
+        goto -> bb1;
+    }
+
+    bb36: {
+        StorageDead(_2);
+        _0 = move _1;
+        return;
+    }
+
+    bb37 (cleanup): {
+        resume;
+    }
+}
diff --git a/tests/ui/abi/unsupported-varargs-fnptr.rs b/tests/ui/abi/unsupported-varargs-fnptr.rs
index 1d23916d039..ac3913b3ef8 100644
--- a/tests/ui/abi/unsupported-varargs-fnptr.rs
+++ b/tests/ui/abi/unsupported-varargs-fnptr.rs
@@ -6,8 +6,6 @@
 // We have to use this flag to force ABI computation of an invalid ABI
 //@ compile-flags: -Clink-dead-code
 
-#![feature(extended_varargs_abi_support)]
-
 // sometimes fn ptrs with varargs make layout and ABI computation ICE
 // as found in https://github.com/rust-lang/rust/issues/142107
 
diff --git a/tests/ui/abi/unsupported-varargs-fnptr.stderr b/tests/ui/abi/unsupported-varargs-fnptr.stderr
index 238f2b31330..c05c55df82e 100644
--- a/tests/ui/abi/unsupported-varargs-fnptr.stderr
+++ b/tests/ui/abi/unsupported-varargs-fnptr.stderr
@@ -1,5 +1,5 @@
 error[E0570]: "aapcs" is not a supported ABI for the current target
-  --> $DIR/unsupported-varargs-fnptr.rs:14:20
+  --> $DIR/unsupported-varargs-fnptr.rs:12:20
    |
 LL | fn aapcs(f: extern "aapcs" fn(usize, ...)) {
    |                    ^^^^^^^
diff --git a/tests/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr b/tests/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr
index 1faaf4ddce2..aa2343e48a6 100644
--- a/tests/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr
+++ b/tests/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr
@@ -3,6 +3,11 @@ error[E0599]: no method named `test` found for opaque type `impl Future<Output =
    |
 LL |     let x: u32 = foo().test();
    |                        ^^^^ method not found in `impl Future<Output = A>`
+   |
+help: consider `await`ing on the `Future` and calling the method on its `Output`
+   |
+LL |     let x: u32 = foo().await.test();
+   |                        ++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.rs b/tests/ui/async-await/future-sizes/async-awaiting-fut.rs
index a3f0bdc8514..b5f59069f85 100644
--- a/tests/ui/async-await/future-sizes/async-awaiting-fut.rs
+++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.rs
@@ -1,7 +1,9 @@
-//@ compile-flags: -Z print-type-sizes --crate-type lib
+//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib
+//@ needs-deterministic-layouts
 //@ edition:2021
 //@ build-pass
 //@ ignore-pass
+//@ only-x86_64
 
 async fn wait() {}
 
diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout
index 642e27b2a57..b30c15bcbe6 100644
--- a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout
+++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout
@@ -48,6 +48,39 @@ print-type-size     variant `Returned`: 1024 bytes
 print-type-size         upvar `.arg`: 1024 bytes
 print-type-size     variant `Panicked`: 1024 bytes
 print-type-size         upvar `.arg`: 1024 bytes
+print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
+print-type-size     field `.waker`: 8 bytes
+print-type-size     field `.local_waker`: 8 bytes
+print-type-size     field `.ext`: 16 bytes
+print-type-size     field `._marker`: 0 bytes
+print-type-size     field `._marker2`: 0 bytes
+print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
+print-type-size     field `.filename`: 16 bytes
+print-type-size     field `.line`: 4 bytes
+print-type-size     field `.col`: 4 bytes
+print-type-size     field `._filename`: 0 bytes
+print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
+print-type-size     variant `Some`: 16 bytes
+print-type-size         field `.0`: 16 bytes
+print-type-size     variant `None`: 0 bytes
+print-type-size         field `.0`: 0 bytes
+print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
+print-type-size     field `.0`: 16 bytes
+print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 16 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of big_fut()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of wait()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `._vtable_ptr`: 8 bytes
+print-type-size     field `._phantom`: 0 bytes
+print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
 print-type-size type: `std::mem::ManuallyDrop<bool>`: 1 bytes, alignment: 1 bytes
 print-type-size     field `.value`: 1 bytes
 print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes
@@ -70,3 +103,7 @@ print-type-size     discriminant: 1 bytes
 print-type-size     variant `Unresumed`: 0 bytes
 print-type-size     variant `Returned`: 0 bytes
 print-type-size     variant `Panicked`: 0 bytes
+print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes
diff --git a/tests/ui/async-await/future-sizes/large-arg.rs b/tests/ui/async-await/future-sizes/large-arg.rs
index 5fbae22a771..809f7cf1f93 100644
--- a/tests/ui/async-await/future-sizes/large-arg.rs
+++ b/tests/ui/async-await/future-sizes/large-arg.rs
@@ -1,7 +1,9 @@
-//@ compile-flags: -Z print-type-sizes --crate-type=lib
+//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type=lib
+//@ needs-deterministic-layouts
 //@ edition: 2021
 //@ build-pass
 //@ ignore-pass
+//@ only-x86_64
 
 pub async fn test() {
     let _ = a([0u8; 1024]).await;
diff --git a/tests/ui/async-await/future-sizes/large-arg.stdout b/tests/ui/async-await/future-sizes/large-arg.stdout
index 67168a3d6ef..e00420d1493 100644
--- a/tests/ui/async-await/future-sizes/large-arg.stdout
+++ b/tests/ui/async-await/future-sizes/large-arg.stdout
@@ -58,3 +58,45 @@ print-type-size     variant `Returned`: 1024 bytes
 print-type-size         upvar `.t`: 1024 bytes
 print-type-size     variant `Panicked`: 1024 bytes
 print-type-size         upvar `.t`: 1024 bytes
+print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
+print-type-size     field `.waker`: 8 bytes
+print-type-size     field `.local_waker`: 8 bytes
+print-type-size     field `.ext`: 16 bytes
+print-type-size     field `._marker`: 0 bytes
+print-type-size     field `._marker2`: 0 bytes
+print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
+print-type-size     field `.filename`: 16 bytes
+print-type-size     field `.line`: 4 bytes
+print-type-size     field `.col`: 4 bytes
+print-type-size     field `._filename`: 0 bytes
+print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
+print-type-size     variant `Some`: 16 bytes
+print-type-size         field `.0`: 16 bytes
+print-type-size     variant `None`: 0 bytes
+print-type-size         field `.0`: 0 bytes
+print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
+print-type-size     field `.0`: 16 bytes
+print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 16 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of a<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of b<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of c<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `._vtable_ptr`: 8 bytes
+print-type-size     field `._phantom`: 0 bytes
+print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes
+print-type-size     discriminant: 1 bytes
+print-type-size     variant `Ready`: 0 bytes
+print-type-size         field `.0`: 0 bytes
+print-type-size     variant `Pending`: 0 bytes
+print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes
diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs
deleted file mode 100644
index 1e0576b2186..00000000000
--- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//@ only-x86_64
-
-fn efiapi(f: extern "efiapi" fn(usize, ...)) {
-    //~^ ERROR: unstable
-    f(22, 44);
-}
-fn sysv(f: extern "sysv64" fn(usize, ...)) {
-    //~^ ERROR: unstable
-    f(22, 44);
-}
-fn win(f: extern "win64" fn(usize, ...)) {
-    //~^ ERROR: unstable
-    f(22, 44);
-}
-
-fn main() {}
diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr
deleted file mode 100644
index 7ef54b639ad..00000000000
--- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr
+++ /dev/null
@@ -1,33 +0,0 @@
-error[E0658]: C-variadic functions with the "efiapi" calling convention are unstable
-  --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
-   |
-LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
-   = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: C-variadic functions with the "sysv64" calling convention are unstable
-  --> $DIR/feature-gate-extended_varargs_abi_support.rs:7:12
-   |
-LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
-   = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: C-variadic functions with the "win64" calling convention are unstable
-  --> $DIR/feature-gate-extended_varargs_abi_support.rs:11:11
-   |
-LL | fn win(f: extern "win64" fn(usize, ...)) {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
-   = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/c-variadic/issue-86053-1.rs b/tests/ui/c-variadic/issue-86053-1.rs
index 8eeb99a4cc1..537d0263adf 100644
--- a/tests/ui/c-variadic/issue-86053-1.rs
+++ b/tests/ui/c-variadic/issue-86053-1.rs
@@ -13,6 +13,6 @@ fn ordering4 < 'a , 'b     > ( a :            ,   self , self ,   self ,
     //~| ERROR unexpected `self` parameter in function
     //~| ERROR unexpected `self` parameter in function
     //~| ERROR `...` must be the last argument of a C-variadic function
-    //~| ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     //~| ERROR cannot find type `F` in this scope
 }
diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr
index 3bf8b7ba5d8..675dfd335c4 100644
--- a/tests/ui/c-variadic/issue-86053-1.stderr
+++ b/tests/ui/c-variadic/issue-86053-1.stderr
@@ -46,7 +46,7 @@ error: `...` must be the last argument of a C-variadic function
 LL |     self , ... ,   self ,   self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
    |            ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/issue-86053-1.rs:11:12
    |
 LL |     self , ... ,   self ,   self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
diff --git a/tests/ui/c-variadic/same-program-multiple-abis-arm.rs b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs
index 1b0bdecabfb..1445bbb47bb 100644
--- a/tests/ui/c-variadic/same-program-multiple-abis-arm.rs
+++ b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs
@@ -1,4 +1,3 @@
-#![feature(extended_varargs_abi_support)]
 //@ run-pass
 //@ only-arm
 //@ ignore-thumb (this test uses arm assembly)
diff --git a/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs b/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs
index b21accb999e..aff8ab613c4 100644
--- a/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs
+++ b/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs
@@ -1,4 +1,3 @@
-#![feature(extended_varargs_abi_support)]
 //@ run-pass
 //@ only-x86_64
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs
index 800dd580af2..ad4fca8252d 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs
@@ -71,6 +71,6 @@ extern "cmse-nonsecure-entry" fn wrapped_trait_object(
 }
 
 extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     //~| ERROR requires `va_list` lang_item
 }
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr
index f0190671b5a..7aeb6969feb 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr
@@ -1,4 +1,4 @@
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/generics.rs:73:53
    |
 LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.rs b/tests/ui/feature-gates/feature-gate-c_variadic.rs
index f189f02a26d..45c68842093 100644
--- a/tests/ui/feature-gates/feature-gate-c_variadic.rs
+++ b/tests/ui/feature-gates/feature-gate-c_variadic.rs
@@ -2,3 +2,9 @@
 
 pub unsafe extern "C" fn test(_: i32, ap: ...) { }
 //~^ ERROR C-variadic functions are unstable
+
+trait Trait {
+    unsafe extern "C" fn trait_test(_: i32, ap: ...) { }
+    //~^ ERROR C-variadic functions are unstable
+    //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
+}
diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.stderr b/tests/ui/feature-gates/feature-gate-c_variadic.stderr
index 1b6a8c92af5..e30a2f1ede3 100644
--- a/tests/ui/feature-gates/feature-gate-c_variadic.stderr
+++ b/tests/ui/feature-gates/feature-gate-c_variadic.stderr
@@ -1,3 +1,9 @@
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
+  --> $DIR/feature-gate-c_variadic.rs:7:45
+   |
+LL |     unsafe extern "C" fn trait_test(_: i32, ap: ...) { }
+   |                                             ^^^^^^^
+
 error[E0658]: C-variadic functions are unstable
   --> $DIR/feature-gate-c_variadic.rs:3:1
    |
@@ -8,6 +14,16 @@ LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { }
    = help: add `#![feature(c_variadic)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: aborting due to 1 previous error
+error[E0658]: C-variadic functions are unstable
+  --> $DIR/feature-gate-c_variadic.rs:7:5
+   |
+LL |     unsafe extern "C" fn trait_test(_: i32, ap: ...) { }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #44930 <https://github.com/rust-lang/rust/issues/44930> for more information
+   = help: add `#![feature(c_variadic)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-1.rs b/tests/ui/frontmatter/frontmatter-whitespace-1.rs
index 8b6e2d1af84..3b7f762d26e 100644
--- a/tests/ui/frontmatter/frontmatter-whitespace-1.rs
+++ b/tests/ui/frontmatter/frontmatter-whitespace-1.rs
@@ -1,7 +1,7 @@
   ---
 //~^ ERROR: invalid preceding whitespace for frontmatter opening
+//~^^ ERROR: unclosed frontmatter
   ---
-//~^ ERROR: invalid preceding whitespace for frontmatter close
 
 #![feature(frontmatter)]
 
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-1.stderr b/tests/ui/frontmatter/frontmatter-whitespace-1.stderr
index 37ece27acb2..f16788fa399 100644
--- a/tests/ui/frontmatter/frontmatter-whitespace-1.stderr
+++ b/tests/ui/frontmatter/frontmatter-whitespace-1.stderr
@@ -10,17 +10,21 @@ note: frontmatter opening should not be preceded by whitespace
 LL |   ---
    | ^^
 
-error: invalid preceding whitespace for frontmatter close
-  --> $DIR/frontmatter-whitespace-1.rs:3:1
+error: unclosed frontmatter
+  --> $DIR/frontmatter-whitespace-1.rs:1:3
    |
-LL |   ---
-   | ^^^^^
+LL | /   ---
+LL | |
+LL | |
+LL | |   ---
+LL | |
+   | |_^
    |
-note: frontmatter close should not be preceded by whitespace
-  --> $DIR/frontmatter-whitespace-1.rs:3:1
+note: frontmatter opening here was not closed
+  --> $DIR/frontmatter-whitespace-1.rs:1:3
    |
 LL |   ---
-   | ^^
+   |   ^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-2.rs b/tests/ui/frontmatter/frontmatter-whitespace-2.rs
index e8c100849b4..7a28e5c1b85 100644
--- a/tests/ui/frontmatter/frontmatter-whitespace-2.rs
+++ b/tests/ui/frontmatter/frontmatter-whitespace-2.rs
@@ -1,4 +1,5 @@
 ---cargo
+//~^ ERROR: unclosed frontmatter
 
 //@ compile-flags: --crate-type lib
 
@@ -6,10 +7,8 @@
 
 fn foo(x: i32) -> i32 {
     ---x
-    //~^ ERROR: invalid preceding whitespace for frontmatter close
-    //~| ERROR: extra characters after frontmatter close are not allowed
+     //~^ WARNING: use of a double negation [double_negations]
 }
-//~^ ERROR: unexpected closing delimiter: `}`
 
 // this test is for the weird case that valid Rust code can have three dashes
 // within them and get treated as a frontmatter close.
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-2.stderr b/tests/ui/frontmatter/frontmatter-whitespace-2.stderr
index ada6af0ec04..2ae63cdc6fe 100644
--- a/tests/ui/frontmatter/frontmatter-whitespace-2.stderr
+++ b/tests/ui/frontmatter/frontmatter-whitespace-2.stderr
@@ -1,26 +1,30 @@
-error: invalid preceding whitespace for frontmatter close
-  --> $DIR/frontmatter-whitespace-2.rs:8:1
+error: unclosed frontmatter
+  --> $DIR/frontmatter-whitespace-2.rs:1:1
    |
-LL |     ---x
-   | ^^^^^^^^
+LL | / ---cargo
+...  |
+LL | |
+   | |_^
    |
-note: frontmatter close should not be preceded by whitespace
-  --> $DIR/frontmatter-whitespace-2.rs:8:1
+note: frontmatter opening here was not closed
+  --> $DIR/frontmatter-whitespace-2.rs:1:1
    |
-LL |     ---x
-   | ^^^^
+LL | ---cargo
+   | ^^^
 
-error: extra characters after frontmatter close are not allowed
-  --> $DIR/frontmatter-whitespace-2.rs:8:1
+warning: use of a double negation
+  --> $DIR/frontmatter-whitespace-2.rs:9:6
    |
 LL |     ---x
-   | ^^^^^^^^
-
-error: unexpected closing delimiter: `}`
-  --> $DIR/frontmatter-whitespace-2.rs:11:1
+   |      ^^^
+   |
+   = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
+   = note: use `-= 1` if you meant to decrement the value
+   = note: `#[warn(double_negations)]` on by default
+help: add parentheses for clarity
    |
-LL | }
-   | ^ unexpected closing delimiter
+LL |     --(-x)
+   |       +  +
 
-error: aborting due to 3 previous errors
+error: aborting due to 1 previous error; 1 warning emitted
 
diff --git a/tests/ui/frontmatter/multifrontmatter-2.rs b/tests/ui/frontmatter/multifrontmatter-2.rs
index 33cc30cb465..8e5b45a0bf7 100644
--- a/tests/ui/frontmatter/multifrontmatter-2.rs
+++ b/tests/ui/frontmatter/multifrontmatter-2.rs
@@ -1,12 +1,12 @@
 ---
  ---
-//~^ ERROR: invalid preceding whitespace for frontmatter close
 
  ---
-//~^ ERROR: expected item, found `-`
-// FIXME(frontmatter): make this diagnostic better
 ---
 
+// hyphens only need to be escaped when at the start of a line
+//@ check-pass
+
 #![feature(frontmatter)]
 
 fn main() {}
diff --git a/tests/ui/frontmatter/multifrontmatter-2.stderr b/tests/ui/frontmatter/multifrontmatter-2.stderr
deleted file mode 100644
index ed9ac4029e2..00000000000
--- a/tests/ui/frontmatter/multifrontmatter-2.stderr
+++ /dev/null
@@ -1,22 +0,0 @@
-error: invalid preceding whitespace for frontmatter close
-  --> $DIR/multifrontmatter-2.rs:2:1
-   |
-LL |  ---
-   | ^^^^
-   |
-note: frontmatter close should not be preceded by whitespace
-  --> $DIR/multifrontmatter-2.rs:2:1
-   |
-LL |  ---
-   | ^
-
-error: expected item, found `-`
-  --> $DIR/multifrontmatter-2.rs:5:2
-   |
-LL |  ---
-   |  ^ expected item
-   |
-   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
-
-error: aborting due to 2 previous errors
-
diff --git a/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr b/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr
deleted file mode 100644
index 96e242d5d48..00000000000
--- a/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: non-defining use of `impl Sized + '_` in the defining scope
-  --> $DIR/as-projection-term.rs:14:19
-   |
-LL |     prove_proj(|| recur());
-   |                   ^^^^^^^
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs b/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs
index f0cf333b6a1..19f983bab70 100644
--- a/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs
+++ b/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs
@@ -1,7 +1,7 @@
 //@ revisions: current next
 //@[next] compile-flags: -Znext-solver
 //@ ignore-compare-mode-next-solver (explicit revisions)
-//@[current] check-pass
+//@ check-pass
 
 fn prove_proj<R>(_: impl FnOnce() -> R) {}
 fn recur<'a>() -> impl Sized + 'a {
@@ -12,6 +12,6 @@ fn recur<'a>() -> impl Sized + 'a {
     // inference variable at this point, we unify it with `opaque<'1>` and
     // end up ignoring that defining use as the hidden type is equal to its key.
     prove_proj(|| recur());
-    //[next]~^ ERROR non-defining use of `impl Sized + '_` in the defining scope
 }
+
 fn main() {}
diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr
new file mode 100644
index 00000000000..30424ec58f9
--- /dev/null
+++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr
@@ -0,0 +1,17 @@
+error[E0792]: expected generic type parameter, found `impl Foo`
+  --> $DIR/double-wrap-with-defining-use.rs:12:26
+   |
+LL | fn a<T: Foo>(x: T) -> impl Foo {
+   |      - this generic parameter must be used with a generic type parameter
+LL |     if true { x } else { a(a(x)) }
+   |                          ^^^^^^^
+
+error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+  --> $DIR/double-wrap-with-defining-use.rs:12:26
+   |
+LL |     if true { x } else { a(a(x)) }
+   |                          ^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0792`.
diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs
index 339277fec37..734b1920772 100644
--- a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs
+++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs
@@ -1,12 +1,17 @@
 // Regression test for ICE from issue #140545
 // The error message is confusing and wrong, but that's a different problem (#139350)
+
 //@ edition:2018
+//@ revisions: current next
+//@[next] compile-flags: -Znext-solver
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] check-pass
 
 trait Foo {}
-fn a(x: impl Foo) -> impl Foo {
+fn a<T: Foo>(x: T) -> impl Foo {
     if true { x } else { a(a(x)) }
-    //~^ ERROR: expected generic type parameter, found `impl Foo` [E0792]
-    //~| ERROR: type parameter `impl Foo` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+    //[current]~^ ERROR: expected generic type parameter, found `impl Foo` [E0792]
+    //[current]~| ERROR: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
 }
 
 fn main(){}
diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr
deleted file mode 100644
index 1b02811e31b..00000000000
--- a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr
+++ /dev/null
@@ -1,17 +0,0 @@
-error[E0792]: expected generic type parameter, found `impl Foo`
-  --> $DIR/double-wrap-with-defining-use.rs:7:26
-   |
-LL | fn a(x: impl Foo) -> impl Foo {
-   |         -------- this generic parameter must be used with a generic type parameter
-LL |     if true { x } else { a(a(x)) }
-   |                          ^^^^^^^
-
-error: type parameter `impl Foo` is part of concrete type but not used in parameter list for the `impl Trait` type alias
-  --> $DIR/double-wrap-with-defining-use.rs:7:26
-   |
-LL |     if true { x } else { a(a(x)) }
-   |                          ^^^^^^^
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0792`.
diff --git a/tests/ui/impl-trait/non-defining-uses/recursive-call.rs b/tests/ui/impl-trait/non-defining-uses/recursive-call.rs
new file mode 100644
index 00000000000..ecddf2cec47
--- /dev/null
+++ b/tests/ui/impl-trait/non-defining-uses/recursive-call.rs
@@ -0,0 +1,30 @@
+//@ revisions: current next
+//@[next] compile-flags: -Znext-solver
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@ check-pass
+
+// Regression test for the non-defining use error in `gll`.
+
+struct Foo;
+impl Foo {
+    fn recur(&self, b: bool) -> impl Sized + '_ {
+        if b {
+            let temp = Foo;
+            temp.recur(false);
+            // desugars to `Foo::recur(&temp);`
+        }
+
+        self
+    }
+
+    fn in_closure(&self) -> impl Sized + '_ {
+        let _ = || {
+            let temp = Foo;
+            temp.in_closure();
+            // desugars to `Foo::in_closure(&temp);`
+        };
+
+        self
+    }
+}
+fn main() {}
diff --git a/tests/ui/loop-match/diverges.rs b/tests/ui/loop-match/diverges.rs
new file mode 100644
index 00000000000..f1b3ffb2076
--- /dev/null
+++ b/tests/ui/loop-match/diverges.rs
@@ -0,0 +1,44 @@
+//@ build-pass
+//@ compile-flags: -Zvalidate-mir
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+// Test that a #[loop_match] without an explicit break from the loop generates valid MIR.
+
+fn break_to_block_unit() -> u8 {
+    let mut state = 0;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                _ => 'b: {
+                    break 'b 2;
+                }
+            }
+        }
+    }
+}
+
+fn break_to_block_value() -> u8 {
+    let mut state = 0u8;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                _ => break 'blk state,
+            }
+        }
+    }
+}
+
+fn infinite_a(mut state: u8) {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                a => a,
+            }
+        }
+    }
+}
diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs
index 78e5c961802..9277994d9b3 100644
--- a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs
+++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs
@@ -5,6 +5,6 @@
 fn main() {}
 
 fn foo(_: Bar, ...) -> impl {}
-//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 //~| ERROR cannot find type `Bar` in this scope
 //~| ERROR at least one trait must be specified
diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr
index 80a8a94aea4..4a1aa49eb6e 100644
--- a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr
+++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr
@@ -1,4 +1,4 @@
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/issue-83499-input-output-iteration-ice.rs:7:16
    |
 LL | fn foo(_: Bar, ...) -> impl {}
diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs
index 1cd6d13d56b..e7a0248cffa 100644
--- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs
+++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs
@@ -4,29 +4,29 @@
 fn main() {}
 
 fn f1_1(x: isize, ...) {}
-//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 
 fn f1_2(...) {}
-//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 
 extern "C" fn f2_1(x: isize, ...) {}
-//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 
 extern "C" fn f2_2(...) {}
-//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 
 extern "C" fn f2_3(..., x: isize) {}
-//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 //~| ERROR `...` must be the last argument of a C-variadic function
 
 extern "C" fn f3_1(x: isize, ...) {}
-//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 
 extern "C" fn f3_2(...) {}
-//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 
 extern "C" fn f3_3(..., x: isize) {}
-//~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 //~| ERROR `...` must be the last argument of a C-variadic function
 
 const unsafe extern "C" fn f4_1(x: isize, ...) {}
@@ -35,12 +35,12 @@ const unsafe extern "C" fn f4_1(x: isize, ...) {}
 
 const extern "C" fn f4_2(x: isize, ...) {}
 //~^ ERROR functions cannot be both `const` and C-variadic
-//~| ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time
 
 const extern "C" fn f4_3(..., x: isize, ...) {}
 //~^ ERROR functions cannot be both `const` and C-variadic
-//~| ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
 //~| ERROR `...` must be the last argument of a C-variadic function
 
 extern "C" {
@@ -52,34 +52,34 @@ struct X;
 
 impl X {
     fn i_f1(x: isize, ...) {}
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     fn i_f2(...) {}
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     fn i_f3(..., x: isize, ...) {}
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     //~| ERROR `...` must be the last argument of a C-variadic function
     fn i_f4(..., x: isize, ...) {}
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     //~| ERROR `...` must be the last argument of a C-variadic function
     const fn i_f5(x: isize, ...) {}
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     //~| ERROR functions cannot be both `const` and C-variadic
     //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time
 }
 
 trait T {
     fn t_f1(x: isize, ...) {}
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     fn t_f2(x: isize, ...);
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     fn t_f3(...) {}
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     fn t_f4(...);
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     fn t_f5(..., x: isize) {}
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     //~| ERROR `...` must be the last argument of a C-variadic function
     fn t_f6(..., x: isize);
-    //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+    //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
     //~| ERROR `...` must be the last argument of a C-variadic function
 }
diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr
index b740cef0200..5379045967a 100644
--- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr
+++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr
@@ -1,22 +1,22 @@
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:6:19
    |
 LL | fn f1_1(x: isize, ...) {}
    |                   ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9
    |
 LL | fn f1_2(...) {}
    |         ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30
    |
 LL | extern "C" fn f2_1(x: isize, ...) {}
    |                              ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20
    |
 LL | extern "C" fn f2_2(...) {}
@@ -28,19 +28,19 @@ error: `...` must be the last argument of a C-variadic function
 LL | extern "C" fn f2_3(..., x: isize) {}
    |                    ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20
    |
 LL | extern "C" fn f2_3(..., x: isize) {}
    |                    ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:22:30
    |
 LL | extern "C" fn f3_1(x: isize, ...) {}
    |                              ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:25:20
    |
 LL | extern "C" fn f3_2(...) {}
@@ -52,7 +52,7 @@ error: `...` must be the last argument of a C-variadic function
 LL | extern "C" fn f3_3(..., x: isize) {}
    |                    ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:28:20
    |
 LL | extern "C" fn f3_3(..., x: isize) {}
@@ -70,7 +70,7 @@ error: functions cannot be both `const` and C-variadic
 LL | const extern "C" fn f4_2(x: isize, ...) {}
    | ^^^^^ `const` because of this      ^^^ C-variadic because of this
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36
    |
 LL | const extern "C" fn f4_2(x: isize, ...) {}
@@ -91,7 +91,7 @@ LL | const extern "C" fn f4_3(..., x: isize, ...) {}
    | |                        C-variadic because of this
    | `const` because of this
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:41:26
    |
 LL | const extern "C" fn f4_3(..., x: isize, ...) {}
@@ -103,13 +103,13 @@ error: `...` must be the last argument of a C-variadic function
 LL |     fn e_f2(..., x: isize);
    |             ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:54:23
    |
 LL |     fn i_f1(x: isize, ...) {}
    |                       ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13
    |
 LL |     fn i_f2(...) {}
@@ -121,7 +121,7 @@ error: `...` must be the last argument of a C-variadic function
 LL |     fn i_f3(..., x: isize, ...) {}
    |             ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:58:13
    |
 LL |     fn i_f3(..., x: isize, ...) {}
@@ -133,7 +133,7 @@ error: `...` must be the last argument of a C-variadic function
 LL |     fn i_f4(..., x: isize, ...) {}
    |             ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13
    |
 LL |     fn i_f4(..., x: isize, ...) {}
@@ -147,31 +147,31 @@ LL |     const fn i_f5(x: isize, ...) {}
    |     |
    |     `const` because of this
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:64:29
    |
 LL |     const fn i_f5(x: isize, ...) {}
    |                             ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23
    |
 LL |     fn t_f1(x: isize, ...) {}
    |                       ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:73:23
    |
 LL |     fn t_f2(x: isize, ...);
    |                       ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13
    |
 LL |     fn t_f3(...) {}
    |             ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13
    |
 LL |     fn t_f4(...);
@@ -183,7 +183,7 @@ error: `...` must be the last argument of a C-variadic function
 LL |     fn t_f5(..., x: isize) {}
    |             ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13
    |
 LL |     fn t_f5(..., x: isize) {}
@@ -195,7 +195,7 @@ error: `...` must be the last argument of a C-variadic function
 LL |     fn t_f6(..., x: isize);
    |             ^^^
 
-error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
+error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
   --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13
    |
 LL |     fn t_f6(..., x: isize);
diff --git a/tests/ui/print_type_sizes/async.rs b/tests/ui/print_type_sizes/async.rs
index 805bccbcf63..951e7cd1012 100644
--- a/tests/ui/print_type_sizes/async.rs
+++ b/tests/ui/print_type_sizes/async.rs
@@ -1,7 +1,9 @@
-//@ compile-flags: -Z print-type-sizes --crate-type lib
+//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib
+//@ needs-deterministic-layouts
 //@ edition:2021
 //@ build-pass
 //@ ignore-pass
+//@ only-x86_64
 
 #![allow(dropping_copy_types)]
 
diff --git a/tests/ui/print_type_sizes/async.stdout b/tests/ui/print_type_sizes/async.stdout
index 83a6962e4cd..d3d6b6471c6 100644
--- a/tests/ui/print_type_sizes/async.stdout
+++ b/tests/ui/print_type_sizes/async.stdout
@@ -16,6 +16,35 @@ print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment
 print-type-size     variant `MaybeUninit`: 8192 bytes
 print-type-size         field `.uninit`: 0 bytes
 print-type-size         field `.value`: 8192 bytes
+print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
+print-type-size     field `.waker`: 8 bytes
+print-type-size     field `.local_waker`: 8 bytes
+print-type-size     field `.ext`: 16 bytes
+print-type-size     field `._marker`: 0 bytes
+print-type-size     field `._marker2`: 0 bytes
+print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
+print-type-size     field `.filename`: 16 bytes
+print-type-size     field `.line`: 4 bytes
+print-type-size     field `.col`: 4 bytes
+print-type-size     field `._filename`: 0 bytes
+print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
+print-type-size     variant `Some`: 16 bytes
+print-type-size         field `.0`: 16 bytes
+print-type-size     variant `None`: 0 bytes
+print-type-size         field `.0`: 0 bytes
+print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
+print-type-size     field `.0`: 16 bytes
+print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 16 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::pin::Pin<&mut {async fn body of wait()}>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
+print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `._vtable_ptr`: 8 bytes
+print-type-size     field `._phantom`: 0 bytes
+print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
+print-type-size     field `.pointer`: 8 bytes
 print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes
 print-type-size     field `.value`: 1 bytes
 print-type-size type: `std::mem::MaybeUninit<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes
@@ -32,3 +61,7 @@ print-type-size     discriminant: 1 bytes
 print-type-size     variant `Unresumed`: 0 bytes
 print-type-size     variant `Returned`: 0 bytes
 print-type-size     variant `Panicked`: 0 bytes
+print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes
diff --git a/tests/ui/privacy/private-field-ty-err.stderr b/tests/ui/privacy/private-field-ty-err.stderr
index 17d50f24a94..0eecad14cfc 100644
--- a/tests/ui/privacy/private-field-ty-err.stderr
+++ b/tests/ui/privacy/private-field-ty-err.stderr
@@ -3,6 +3,11 @@ error[E0616]: field `len` of struct `Foo` is private
    |
 LL |     if x.len {
    |          ^^^ private field
+   |
+help: a method `len` also exists, call it with parentheses
+   |
+LL |     if x.len() {
+   |             ++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr
index 72a9820bb64..4a73a4747ad 100644
--- a/tests/ui/stats/input-stats.stderr
+++ b/tests/ui/stats/input-stats.stderr
@@ -23,10 +23,10 @@ ast-stats - Path                      72 (NN.N%)             1
 ast-stats - Struct                    72 (NN.N%)             1
 ast-stats - Lit                      144 (NN.N%)             2
 ast-stats - Block                    216 (NN.N%)             3
-ast-stats Pat                      504 (NN.N%)             7            72
-ast-stats - Struct                    72 (NN.N%)             1
-ast-stats - Wild                      72 (NN.N%)             1
-ast-stats - Ident                    360 (NN.N%)             5
+ast-stats Pat                      560 (NN.N%)             7            80
+ast-stats - Struct                    80 (NN.N%)             1
+ast-stats - Wild                      80 (NN.N%)             1
+ast-stats - Ident                    400 (NN.N%)             5
 ast-stats GenericParam             480 (NN.N%)             5            96
 ast-stats GenericBound             352 (NN.N%)             4            88
 ast-stats - Trait                    352 (NN.N%)             4
@@ -57,7 +57,7 @@ ast-stats GenericArgs               40 (NN.N%)             1            40
 ast-stats - AngleBracketed            40 (NN.N%)             1
 ast-stats Crate                     40 (NN.N%)             1            40
 ast-stats ----------------------------------------------------------------
-ast-stats Total                  7_472                   129
+ast-stats Total                  7_528                   129
 ast-stats ================================================================
 hir-stats ================================================================
 hir-stats HIR STATS: input_stats
@@ -85,11 +85,11 @@ hir-stats - Ptr                       48 (NN.N%)             1
 hir-stats - Ref                       48 (NN.N%)             1
 hir-stats - Path                     624 (NN.N%)            13
 hir-stats Generics                 560 (NN.N%)            10            56
+hir-stats Pat                      400 (NN.N%)             5            80
+hir-stats - Struct                    80 (NN.N%)             1
+hir-stats - Wild                      80 (NN.N%)             1
+hir-stats - Binding                  240 (NN.N%)             3
 hir-stats GenericParam             400 (NN.N%)             5            80
-hir-stats Pat                      360 (NN.N%)             5            72
-hir-stats - Struct                    72 (NN.N%)             1
-hir-stats - Wild                      72 (NN.N%)             1
-hir-stats - Binding                  216 (NN.N%)             3
 hir-stats Block                    288 (NN.N%)             6            48
 hir-stats GenericBound             256 (NN.N%)             4            64
 hir-stats - Trait                    256 (NN.N%)             4
@@ -119,5 +119,5 @@ hir-stats TraitItemId                8 (NN.N%)             2             4
 hir-stats ImplItemId                 8 (NN.N%)             2             4
 hir-stats ForeignItemId              4 (NN.N%)             1             4
 hir-stats ----------------------------------------------------------------
-hir-stats Total                  8_584                   173
+hir-stats Total                  8_624                   173
 hir-stats ================================================================
diff --git a/tests/ui/suggestions/negative-literal-infered-to-unsigned.rs b/tests/ui/suggestions/negative-literal-infered-to-unsigned.rs
new file mode 100644
index 00000000000..39797574b97
--- /dev/null
+++ b/tests/ui/suggestions/negative-literal-infered-to-unsigned.rs
@@ -0,0 +1,13 @@
+fn main() {
+    for x in -5..5 {
+        //~^ ERROR: the trait bound `usize: Neg` is not satisfied
+        //~| HELP: consider specifying an integer type that can be negative
+        do_something(x);
+    }
+    let x = -5;
+    //~^ ERROR: the trait bound `usize: Neg` is not satisfied
+    //~| HELP: consider specifying an integer type that can be negative
+    do_something(x);
+}
+
+fn do_something(_val: usize) {}
diff --git a/tests/ui/suggestions/negative-literal-infered-to-unsigned.stderr b/tests/ui/suggestions/negative-literal-infered-to-unsigned.stderr
new file mode 100644
index 00000000000..b49ea224d2b
--- /dev/null
+++ b/tests/ui/suggestions/negative-literal-infered-to-unsigned.stderr
@@ -0,0 +1,25 @@
+error[E0277]: the trait bound `usize: Neg` is not satisfied
+  --> $DIR/negative-literal-infered-to-unsigned.rs:2:14
+   |
+LL |     for x in -5..5 {
+   |              ^^ the trait `Neg` is not implemented for `usize`
+   |
+help: consider specifying an integer type that can be negative
+   |
+LL |     for x in -5isize..5 {
+   |                +++++
+
+error[E0277]: the trait bound `usize: Neg` is not satisfied
+  --> $DIR/negative-literal-infered-to-unsigned.rs:7:13
+   |
+LL |     let x = -5;
+   |             ^^ the trait `Neg` is not implemented for `usize`
+   |
+help: consider specifying an integer type that can be negative
+   |
+LL |     let x = -5isize;
+   |               +++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/type-alias-impl-trait/split-paths-may-dangle.rs b/tests/ui/type-alias-impl-trait/split-paths-may-dangle.rs
new file mode 100644
index 00000000000..61c5342cdbb
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/split-paths-may-dangle.rs
@@ -0,0 +1,11 @@
+// Regression test for issue #146045 - ensure that the TAIT `SplitPaths` does not
+// require the borrowed string to be live.
+//@ check-pass
+//@ edition:2015
+
+pub fn repro() -> Option<std::path::PathBuf> {
+    let unparsed = std::ffi::OsString::new();
+    std::env::split_paths(&unparsed).next()
+}
+
+fn main() {}
diff --git a/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.rs b/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.rs
new file mode 100644
index 00000000000..671f280e814
--- /dev/null
+++ b/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.rs
@@ -0,0 +1,14 @@
+struct LlamaModel;
+
+impl LlamaModel {
+    fn chat_template(&self) -> Result<&str, ()> {
+        todo!()
+    }
+}
+
+fn template_from_str(_x: &str) {}
+
+fn main() {
+    let model = LlamaModel;
+    template_from_str(&model.chat_template); //~ ERROR attempted to take value of method `chat_template` on type `LlamaModel`
+}
diff --git a/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.stderr b/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.stderr
new file mode 100644
index 00000000000..7f5dd0617b1
--- /dev/null
+++ b/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.stderr
@@ -0,0 +1,14 @@
+error[E0615]: attempted to take value of method `chat_template` on type `LlamaModel`
+  --> $DIR/suggest-method-name-with-maybe-ty-mismatch-146008.rs:13:30
+   |
+LL |     template_from_str(&model.chat_template);
+   |                              ^^^^^^^^^^^^^ method, not a field
+   |
+help: use parentheses to call the method
+   |
+LL |     template_from_str(&model.chat_template());
+   |                                           ++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0615`.