about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--compiler/rustc_middle/src/query/mod.rs5
-rw-r--r--compiler/rustc_passes/src/dead.rs467
-rw-r--r--compiler/rustc_passes/src/lib.rs1
-rw-r--r--compiler/rustc_thread_pool/Cargo.toml1
-rw-r--r--library/alloc/src/collections/btree/map.rs2
-rw-r--r--library/std/src/sys/args/common.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs63
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs99
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs16
-rw-r--r--src/bootstrap/src/core/build_steps/install.rs10
-rw-r--r--src/bootstrap/src/core/build_steps/perf.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs14
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs244
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs399
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs4
-rw-r--r--src/bootstrap/src/core/builder/mod.rs38
-rw-r--r--src/bootstrap/src/core/builder/tests.rs288
-rw-r--r--src/bootstrap/src/lib.rs20
-rwxr-xr-xsrc/ci/docker/scripts/build-clang.sh6
-rw-r--r--src/doc/rustc-dev-guide/src/stability.md3
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css4
m---------src/llvm-project0
-rw-r--r--src/tools/compiletest/src/runtest.rs2
-rw-r--r--tests/rustdoc-gui/notable-trait.goml4
-rw-r--r--tests/rustdoc-gui/src/test_docs/lib.rs14
-rw-r--r--tests/rustdoc-gui/tooltips.goml15
-rw-r--r--tests/ui/abi/extern-c-two-doubles-x86_64-5754.rs (renamed from tests/ui/issues/issue-5754.rs)2
-rw-r--r--tests/ui/array-slice-vec/fixed-size-arrays-zero-size-types-8898.rs (renamed from tests/ui/issues/issue-8898.rs)1
-rw-r--r--tests/ui/associated-consts/trait-associated-const-usage-43483.rs (renamed from tests/ui/issues/issue-43483.rs)1
-rw-r--r--tests/ui/associated-types/recursive-associated-type-overflow-21946.rs (renamed from tests/ui/issues/issue-21946.rs)1
-rw-r--r--tests/ui/associated-types/recursive-associated-type-overflow-21946.stderr (renamed from tests/ui/issues/issue-21946.stderr)2
-rw-r--r--tests/ui/borrowck/mismatched-pointer-type-in-self-7061.rs (renamed from tests/ui/issues/issue-7061.rs)1
-rw-r--r--tests/ui/borrowck/mismatched-pointer-type-in-self-7061.stderr (renamed from tests/ui/issues/issue-7061.stderr)2
-rw-r--r--tests/ui/borrowck/region-checker-map-closure-13665.rs (renamed from tests/ui/issues/issue-13665.rs)1
-rw-r--r--tests/ui/closures/aliasability-violation-with-closure-21600.rs (renamed from tests/ui/issues/issue-21600.rs)1
-rw-r--r--tests/ui/closures/aliasability-violation-with-closure-21600.stderr (renamed from tests/ui/issues/issue-21600.stderr)4
-rw-r--r--tests/ui/consts/const-pattern-rewrite-error-32086.rs (renamed from tests/ui/issues/issue-32086.rs)1
-rw-r--r--tests/ui/consts/const-pattern-rewrite-error-32086.stderr (renamed from tests/ui/issues/issue-32086.stderr)4
-rw-r--r--tests/ui/cross-crate/auxiliary/aux-5521.rs (renamed from tests/ui/issues/auxiliary/issue-5521.rs)0
-rw-r--r--tests/ui/cross-crate/cross-crate-map-usage-5521.rs (renamed from tests/ui/issues/issue-5521.rs)7
-rw-r--r--tests/ui/did_you_mean/typo-suggestion-improvement-46332.rs (renamed from tests/ui/issues/issue-46332.rs)1
-rw-r--r--tests/ui/did_you_mean/typo-suggestion-improvement-46332.stderr (renamed from tests/ui/issues/issue-46332.stderr)2
-rw-r--r--tests/ui/drop/destructor-run-for-let-ignore-6892.rs (renamed from tests/ui/issues/issue-6892.rs)1
-rw-r--r--tests/ui/drop/drop-count-assertion-16151.rs (renamed from tests/ui/issues/issue-16151.rs)1
-rw-r--r--tests/ui/dyn-compatibility/dyn-compatible-trait-implementation-20939.rs (renamed from tests/ui/issues/issue-20939.rs)1
-rw-r--r--tests/ui/dyn-compatibility/dyn-compatible-trait-implementation-20939.stderr (renamed from tests/ui/issues/issue-20939.stderr)2
-rw-r--r--tests/ui/generics/unused-type-parameter-regression-36075.rs (renamed from tests/ui/issues/issue-36075.rs)1
-rw-r--r--tests/ui/issues/issue-9188.rs11
-rw-r--r--tests/ui/iterators/fold-iterator-error-23966.rs (renamed from tests/ui/issues/issue-23966.rs)1
-rw-r--r--tests/ui/iterators/fold-iterator-error-23966.stderr (renamed from tests/ui/issues/issue-23966.stderr)2
-rw-r--r--tests/ui/lifetimes/lifetime-inference-miss-15735.rs (renamed from tests/ui/issues/issue-15735.rs)1
-rw-r--r--tests/ui/loops/infinite-loop-simplify-cfg-75704.rs (renamed from tests/ui/issues/issue-75704.rs)1
-rw-r--r--tests/ui/match/unreachable-pattern-if-variant-not-imported-19100.fixed (renamed from tests/ui/issues/issue-19100.fixed)1
-rw-r--r--tests/ui/match/unreachable-pattern-if-variant-not-imported-19100.rs (renamed from tests/ui/issues/issue-19100.rs)1
-rw-r--r--tests/ui/match/unreachable-pattern-if-variant-not-imported-19100.stderr (renamed from tests/ui/issues/issue-19100.stderr)4
-rw-r--r--tests/ui/methods/trait-method-resolution-over-inherent-22684.rs (renamed from tests/ui/issues/issue-22684.rs)1
-rw-r--r--tests/ui/methods/trait-method-resolution-over-inherent-22684.stderr (renamed from tests/ui/issues/issue-22684.stderr)2
-rw-r--r--tests/ui/mismatched_types/float-integer-subtraction-error-24352.rs (renamed from tests/ui/issues/issue-24352.rs)1
-rw-r--r--tests/ui/mismatched_types/float-integer-subtraction-error-24352.stderr (renamed from tests/ui/issues/issue-24352.stderr)2
-rw-r--r--tests/ui/mismatched_types/for-loop-in-vec-type-mismatchrs-50585.rs (renamed from tests/ui/issues/issue-50585.rs)1
-rw-r--r--tests/ui/mismatched_types/for-loop-in-vec-type-mismatchrs-50585.stderr (renamed from tests/ui/issues/issue-50585.stderr)2
-rw-r--r--tests/ui/mismatched_types/starts-with-stringify-type-mismatch-48364.rs (renamed from tests/ui/issues/issue-48364.rs)1
-rw-r--r--tests/ui/mismatched_types/starts-with-stringify-type-mismatch-48364.stderr (renamed from tests/ui/issues/issue-48364.stderr)2
-rw-r--r--tests/ui/modules/circular-module-imports-32797.rs (renamed from tests/ui/issues/issue-32797.rs)1
-rw-r--r--tests/ui/moves/matching-partially-moved-value-17385.rs (renamed from tests/ui/issues/issue-17385.rs)1
-rw-r--r--tests/ui/moves/matching-partially-moved-value-17385.stderr (renamed from tests/ui/issues/issue-17385.stderr)8
-rw-r--r--tests/ui/numbers-arithmetic/f32-literal-rounding-32805.rs (renamed from tests/ui/issues/issue-32805.rs)1
-rw-r--r--tests/ui/privacy/private-field-access-violation-3763.rs (renamed from tests/ui/issues/issue-3763.rs)1
-rw-r--r--tests/ui/privacy/private-field-access-violation-3763.stderr (renamed from tests/ui/issues/issue-3763.stderr)10
-rw-r--r--tests/ui/resolve/cannot-find-value-in-scope-22811.rs (renamed from tests/ui/issues/issue-2281-part1.rs)1
-rw-r--r--tests/ui/resolve/cannot-find-value-in-scope-22811.stderr (renamed from tests/ui/issues/issue-2281-part1.stderr)2
-rw-r--r--tests/ui/resolve/unresolved-module-error-33293.rs (renamed from tests/ui/issues/issue-33293.rs)1
-rw-r--r--tests/ui/resolve/unresolved-module-error-33293.stderr (renamed from tests/ui/issues/issue-33293.stderr)2
-rw-r--r--tests/ui/structs/tuple-struct-field-naming-47073.rs (renamed from tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.rs)1
-rw-r--r--tests/ui/structs/tuple-struct-field-naming-47073.stderr (renamed from tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr)4
-rw-r--r--tests/ui/symbol-names/auxiliary/aux-9188.rs (renamed from tests/ui/issues/auxiliary/issue-9188.rs)0
-rw-r--r--tests/ui/symbol-names/same-symbol-name-for-inner-statics-9188.rs11
-rw-r--r--tests/ui/trait-bounds/trait-selection-overflow-prevention-72839.rs (renamed from tests/ui/issues/issue-72839-error-overflow.rs)1
-rw-r--r--tests/ui/trait-bounds/trait-selection-overflow-prevention-72839.stderr (renamed from tests/ui/issues/issue-72839-error-overflow.stderr)2
-rw-r--r--tests/ui/trait-objects/trait-object-lifetime-conversion-47638.rs (renamed from tests/ui/issues/issue-47638.rs)1
-rw-r--r--tests/ui/traits/const-traits/const-closure-issue-125866-error.rs24
-rw-r--r--tests/ui/traits/const-traits/const-closure-issue-125866-error.stderr11
-rw-r--r--tests/ui/traits/const-traits/const-closure-issue-125866-pass.rs25
-rw-r--r--tests/ui/traits/const-traits/const-fn-trait-bound-issue-104314.rs13
-rw-r--r--tests/ui/traits/trait-impl-overflow-with-where-clause-20413.rs (renamed from tests/ui/issues/issue-20413.rs)1
-rw-r--r--tests/ui/traits/trait-impl-overflow-with-where-clause-20413.stderr (renamed from tests/ui/issues/issue-20413.stderr)24
-rw-r--r--tests/ui/try-trait/incompatible-types-with-question-mark-51632.rs (renamed from tests/ui/issues/issue-51632-try-desugar-incompatible-types.rs)1
-rw-r--r--tests/ui/try-trait/incompatible-types-with-question-mark-51632.stderr (renamed from tests/ui/issues/issue-51632-try-desugar-incompatible-types.stderr)2
-rw-r--r--triagebot.toml5
90 files changed, 1156 insertions, 794 deletions
diff --git a/.gitmodules b/.gitmodules
index 439fde6d766..8617643a120 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -25,7 +25,7 @@
 [submodule "src/llvm-project"]
 	path = src/llvm-project
 	url = https://github.com/rust-lang/llvm-project.git
-	branch = rustc/20.1-2025-07-13
+	branch = rustc/21.1-2025-08-01
 	shallow = true
 [submodule "src/doc/embedded-book"]
 	path = src/doc/embedded-book
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 2941808e806..a4589576594 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1178,11 +1178,10 @@ rustc_queries! {
 
     /// Return the live symbols in the crate for dead code check.
     ///
-    /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone) and
-    /// their respective impl (i.e., part of the derive macro)
+    /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone).
     query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx (
         LocalDefIdSet,
-        LocalDefIdMap<FxIndexSet<(DefId, DefId)>>
+        LocalDefIdMap<FxIndexSet<DefId>>,
     ) {
         arena_cache
         desc { "finding live symbols in crate" }
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index a90d1af87ca..fa9d0c7b1b7 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -5,7 +5,6 @@
 
 use std::mem;
 
-use hir::ItemKind;
 use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
 use rustc_abi::FieldIdx;
 use rustc_data_structures::fx::FxIndexSet;
@@ -14,7 +13,7 @@ use rustc_errors::MultiSpan;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{self as hir, ImplItem, ImplItemKind, Node, PatKind, QPath, TyKind};
+use rustc_hir::{self as hir, Node, PatKind, QPath};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::privacy::Level;
 use rustc_middle::query::Providers;
@@ -28,37 +27,43 @@ use crate::errors::{
     ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment,
 };
 
-// Any local node that may call something in its body block should be
-// explored. For example, if it's a live Node::Item that is a
-// function, then we should explore its block to check for codes that
-// may need to be marked as live.
+/// Any local definition that may call something in its body block should be explored. For example,
+/// if it's a live function, then we should explore its block to check for codes that may need to
+/// be marked as live.
 fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
-    matches!(
-        tcx.hir_node_by_def_id(def_id),
-        Node::Item(..)
-            | Node::ImplItem(..)
-            | Node::ForeignItem(..)
-            | Node::TraitItem(..)
-            | Node::Variant(..)
-            | Node::AnonConst(..)
-            | Node::OpaqueTy(..)
-    )
-}
-
-/// Returns the local def id of the ADT if the given ty refers to a local one.
-fn local_adt_def_of_ty<'tcx>(ty: &hir::Ty<'tcx>) -> Option<LocalDefId> {
-    match ty.kind {
-        TyKind::Path(QPath::Resolved(_, path)) => {
-            if let Res::Def(def_kind, def_id) = path.res
-                && let Some(local_def_id) = def_id.as_local()
-                && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
-            {
-                Some(local_def_id)
-            } else {
-                None
-            }
-        }
-        _ => None,
+    match tcx.def_kind(def_id) {
+        DefKind::Mod
+        | DefKind::Struct
+        | DefKind::Union
+        | DefKind::Enum
+        | DefKind::Variant
+        | DefKind::Trait
+        | DefKind::TyAlias
+        | DefKind::ForeignTy
+        | DefKind::TraitAlias
+        | DefKind::AssocTy
+        | DefKind::Fn
+        | DefKind::Const
+        | DefKind::Static { .. }
+        | DefKind::AssocFn
+        | DefKind::AssocConst
+        | DefKind::Macro(_)
+        | DefKind::GlobalAsm
+        | DefKind::Impl { .. }
+        | DefKind::OpaqueTy
+        | DefKind::AnonConst
+        | DefKind::InlineConst
+        | DefKind::ExternCrate
+        | DefKind::Use
+        | DefKind::Ctor(..)
+        | DefKind::ForeignMod => true,
+
+        DefKind::TyParam
+        | DefKind::ConstParam
+        | DefKind::Field
+        | DefKind::LifetimeParam
+        | DefKind::Closure
+        | DefKind::SyntheticCoroutineBody => false,
     }
 }
 
@@ -74,17 +79,16 @@ struct MarkSymbolVisitor<'tcx> {
     worklist: Vec<(LocalDefId, ComesFromAllowExpect)>,
     tcx: TyCtxt<'tcx>,
     maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
+    scanned: UnordSet<(LocalDefId, ComesFromAllowExpect)>,
     live_symbols: LocalDefIdSet,
     repr_unconditionally_treats_fields_as_live: bool,
     repr_has_repr_simd: bool,
     in_pat: bool,
     ignore_variant_stack: Vec<DefId>,
-    // maps from tuple struct constructors to tuple struct items
-    struct_constructors: LocalDefIdMap<LocalDefId>,
     // maps from ADTs to ignored derived traits (e.g. Debug and Clone)
     // and the span of their respective impl (i.e., part of the derive
     // macro)
-    ignored_derived_traits: LocalDefIdMap<FxIndexSet<(DefId, DefId)>>,
+    ignored_derived_traits: LocalDefIdMap<FxIndexSet<DefId>>,
 }
 
 impl<'tcx> MarkSymbolVisitor<'tcx> {
@@ -99,7 +103,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
 
     fn check_def_id(&mut self, def_id: DefId) {
         if let Some(def_id) = def_id.as_local() {
-            if should_explore(self.tcx, def_id) || self.struct_constructors.contains_key(&def_id) {
+            if should_explore(self.tcx, def_id) {
                 self.worklist.push((def_id, ComesFromAllowExpect::No));
             }
             self.live_symbols.insert(def_id);
@@ -318,13 +322,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
     }
 
     fn mark_live_symbols(&mut self) {
-        let mut scanned = UnordSet::default();
         while let Some(work) = self.worklist.pop() {
-            if !scanned.insert(work) {
+            if !self.scanned.insert(work) {
                 continue;
             }
 
-            let (id, comes_from_allow_expect) = work;
+            let (mut id, comes_from_allow_expect) = work;
 
             // Avoid accessing the HIR for the synthesized associated type generated for RPITITs.
             if self.tcx.is_impl_trait_in_trait(id.to_def_id()) {
@@ -332,9 +335,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 continue;
             }
 
-            // in the case of tuple struct constructors we want to check the item, not the generated
-            // tuple struct constructor function
-            let id = self.struct_constructors.get(&id).copied().unwrap_or(id);
+            // in the case of tuple struct constructors we want to check the item,
+            // not the generated tuple struct constructor function
+            if let DefKind::Ctor(..) = self.tcx.def_kind(id) {
+                id = self.tcx.local_parent(id);
+            }
 
             // When using `#[allow]` or `#[expect]` of `dead_code`, we do a QOL improvement
             // by declaring fn calls, statics, ... within said items as live, as well as
@@ -380,10 +385,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind()
                     && let Some(adt_def_id) = adt_def.did().as_local()
                 {
-                    self.ignored_derived_traits
-                        .entry(adt_def_id)
-                        .or_default()
-                        .insert((trait_of, impl_of));
+                    self.ignored_derived_traits.entry(adt_def_id).or_default().insert(trait_of);
                 }
                 return true;
             }
@@ -478,24 +480,24 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
     /// `local_def_id` points to an impl or an impl item,
     /// both impl and impl item that may be passed to this function are of a trait,
     /// and added into the unsolved_items during `create_and_seed_worklist`
-    fn check_impl_or_impl_item_live(
-        &mut self,
-        impl_id: hir::ItemId,
-        local_def_id: LocalDefId,
-    ) -> bool {
-        let trait_def_id = match self.tcx.def_kind(local_def_id) {
+    fn check_impl_or_impl_item_live(&mut self, local_def_id: LocalDefId) -> bool {
+        let (impl_block_id, trait_def_id) = match self.tcx.def_kind(local_def_id) {
             // assoc impl items of traits are live if the corresponding trait items are live
-            DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => self
-                .tcx
-                .associated_item(local_def_id)
-                .trait_item_def_id
-                .and_then(|def_id| def_id.as_local()),
+            DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => (
+                self.tcx.local_parent(local_def_id),
+                self.tcx
+                    .associated_item(local_def_id)
+                    .trait_item_def_id
+                    .and_then(|def_id| def_id.as_local()),
+            ),
             // impl items are live if the corresponding traits are live
-            DefKind::Impl { of_trait: true } => self
-                .tcx
-                .impl_trait_ref(impl_id.owner_id.def_id)
-                .and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()),
-            _ => None,
+            DefKind::Impl { of_trait: true } => (
+                local_def_id,
+                self.tcx
+                    .impl_trait_ref(local_def_id)
+                    .and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()),
+            ),
+            _ => bug!(),
         };
 
         if let Some(trait_def_id) = trait_def_id
@@ -505,9 +507,9 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         }
 
         // The impl or impl item is used if the corresponding trait or trait item is used and the ty is used.
-        if let Some(local_def_id) =
-            local_adt_def_of_ty(self.tcx.hir_item(impl_id).expect_impl().self_ty)
-            && !self.live_symbols.contains(&local_def_id)
+        if let ty::Adt(adt, _) = self.tcx.type_of(impl_block_id).instantiate_identity().kind()
+            && let Some(adt_def_id) = adt.did().as_local()
+            && !self.live_symbols.contains(&adt_def_id)
         {
             return false;
         }
@@ -714,139 +716,86 @@ fn has_allow_dead_code_or_lang_attr(
     }
 }
 
-// These check_* functions seeds items that
-//   1) We want to explicitly consider as live:
-//     * Item annotated with #[allow(dead_code)]
-//         - This is done so that if we want to suppress warnings for a
-//           group of dead functions, we only have to annotate the "root".
-//           For example, if both `f` and `g` are dead and `f` calls `g`,
-//           then annotating `f` with `#[allow(dead_code)]` will suppress
-//           warning for both `f` and `g`.
-//     * Item annotated with #[lang=".."]
-//         - This is because lang items are always callable from elsewhere.
-//   or
-//   2) We are not sure to be live or not
-//     * Implementations of traits and trait methods
-fn check_item<'tcx>(
+/// Examine the given definition and record it in the worklist if it should be considered live.
+///
+/// We want to explicitly consider as live:
+/// * Item annotated with #[allow(dead_code)]
+///       This is done so that if we want to suppress warnings for a
+///       group of dead functions, we only have to annotate the "root".
+///       For example, if both `f` and `g` are dead and `f` calls `g`,
+///       then annotating `f` with `#[allow(dead_code)]` will suppress
+///       warning for both `f` and `g`.
+///
+/// * Item annotated with #[lang=".."]
+///       Lang items are always callable from elsewhere.
+///
+/// For trait methods and implementations of traits, we are not certain that the definitions are
+/// live at this stage. We record them in `unsolved_items` for later examination.
+fn maybe_record_as_seed<'tcx>(
     tcx: TyCtxt<'tcx>,
+    owner_id: hir::OwnerId,
     worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
-    struct_constructors: &mut LocalDefIdMap<LocalDefId>,
-    unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>,
-    id: hir::ItemId,
+    unsolved_items: &mut Vec<LocalDefId>,
 ) {
-    let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
+    let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, owner_id.def_id);
     if let Some(comes_from_allow) = allow_dead_code {
-        worklist.push((id.owner_id.def_id, comes_from_allow));
+        worklist.push((owner_id.def_id, comes_from_allow));
     }
 
-    match tcx.def_kind(id.owner_id) {
+    match tcx.def_kind(owner_id) {
         DefKind::Enum => {
-            let item = tcx.hir_item(id);
-            if let hir::ItemKind::Enum(_, _, ref enum_def) = item.kind {
-                if let Some(comes_from_allow) = allow_dead_code {
-                    worklist.extend(
-                        enum_def.variants.iter().map(|variant| (variant.def_id, comes_from_allow)),
-                    );
-                }
-
-                for variant in enum_def.variants {
-                    if let Some(ctor_def_id) = variant.data.ctor_def_id() {
-                        struct_constructors.insert(ctor_def_id, variant.def_id);
-                    }
-                }
+            if let Some(comes_from_allow) = allow_dead_code {
+                let adt = tcx.adt_def(owner_id);
+                worklist.extend(
+                    adt.variants()
+                        .iter()
+                        .map(|variant| (variant.def_id.expect_local(), comes_from_allow)),
+                );
             }
         }
-        DefKind::Impl { of_trait } => {
-            if let Some(comes_from_allow) =
-                has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id)
-            {
-                worklist.push((id.owner_id.def_id, comes_from_allow));
-            } else if of_trait {
-                unsolved_items.push((id, id.owner_id.def_id));
-            }
-
-            for def_id in tcx.associated_item_def_ids(id.owner_id) {
-                let local_def_id = def_id.expect_local();
-
-                if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id)
-                {
-                    worklist.push((local_def_id, comes_from_allow));
-                } else if of_trait {
-                    // We only care about associated items of traits,
-                    // because they cannot be visited directly,
-                    // so we later mark them as live if their corresponding traits
-                    // or trait items and self types are both live,
-                    // but inherent associated items can be visited and marked directly.
-                    unsolved_items.push((id, local_def_id));
+        DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy => {
+            if allow_dead_code.is_none() {
+                let parent = tcx.local_parent(owner_id.def_id);
+                match tcx.def_kind(parent) {
+                    DefKind::Impl { of_trait: false } | DefKind::Trait => {}
+                    DefKind::Impl { of_trait: true } => {
+                        // We only care about associated items of traits,
+                        // because they cannot be visited directly,
+                        // so we later mark them as live if their corresponding traits
+                        // or trait items and self types are both live,
+                        // but inherent associated items can be visited and marked directly.
+                        unsolved_items.push(owner_id.def_id);
+                    }
+                    _ => bug!(),
                 }
             }
         }
-        DefKind::Struct => {
-            let item = tcx.hir_item(id);
-            if let hir::ItemKind::Struct(_, _, ref variant_data) = item.kind
-                && let Some(ctor_def_id) = variant_data.ctor_def_id()
-            {
-                struct_constructors.insert(ctor_def_id, item.owner_id.def_id);
+        DefKind::Impl { of_trait: true } => {
+            if allow_dead_code.is_none() {
+                unsolved_items.push(owner_id.def_id);
             }
         }
         DefKind::GlobalAsm => {
             // global_asm! is always live.
-            worklist.push((id.owner_id.def_id, ComesFromAllowExpect::No));
+            worklist.push((owner_id.def_id, ComesFromAllowExpect::No));
         }
         DefKind::Const => {
-            let item = tcx.hir_item(id);
-            if let hir::ItemKind::Const(ident, ..) = item.kind
-                && ident.name == kw::Underscore
-            {
+            if tcx.item_name(owner_id.def_id) == kw::Underscore {
                 // `const _` is always live, as that syntax only exists for the side effects
                 // of type checking and evaluating the constant expression, and marking them
                 // as dead code would defeat that purpose.
-                worklist.push((id.owner_id.def_id, ComesFromAllowExpect::No));
+                worklist.push((owner_id.def_id, ComesFromAllowExpect::No));
             }
         }
         _ => {}
     }
 }
 
-fn check_trait_item(
-    tcx: TyCtxt<'_>,
-    worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
-    id: hir::TraitItemId,
-) {
-    use hir::TraitItemKind::{Const, Fn, Type};
-
-    let trait_item = tcx.hir_trait_item(id);
-    if matches!(trait_item.kind, Const(_, Some(_)) | Type(_, Some(_)) | Fn(..))
-        && let Some(comes_from_allow) =
-            has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
-    {
-        worklist.push((trait_item.owner_id.def_id, comes_from_allow));
-    }
-}
-
-fn check_foreign_item(
-    tcx: TyCtxt<'_>,
-    worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
-    id: hir::ForeignItemId,
-) {
-    if matches!(tcx.def_kind(id.owner_id), DefKind::Static { .. } | DefKind::Fn)
-        && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id)
-    {
-        worklist.push((id.owner_id.def_id, comes_from_allow));
-    }
-}
-
 fn create_and_seed_worklist(
     tcx: TyCtxt<'_>,
-) -> (
-    Vec<(LocalDefId, ComesFromAllowExpect)>,
-    LocalDefIdMap<LocalDefId>,
-    Vec<(hir::ItemId, LocalDefId)>,
-) {
+) -> (Vec<(LocalDefId, ComesFromAllowExpect)>, Vec<LocalDefId>) {
     let effective_visibilities = &tcx.effective_visibilities(());
-    // see `MarkSymbolVisitor::struct_constructors`
     let mut unsolved_impl_item = Vec::new();
-    let mut struct_constructors = Default::default();
     let mut worklist = effective_visibilities
         .iter()
         .filter_map(|(&id, effective_vis)| {
@@ -863,54 +812,49 @@ fn create_and_seed_worklist(
         .collect::<Vec<_>>();
 
     let crate_items = tcx.hir_crate_items(());
-    for id in crate_items.free_items() {
-        check_item(tcx, &mut worklist, &mut struct_constructors, &mut unsolved_impl_item, id);
-    }
-
-    for id in crate_items.trait_items() {
-        check_trait_item(tcx, &mut worklist, id);
-    }
-
-    for id in crate_items.foreign_items() {
-        check_foreign_item(tcx, &mut worklist, id);
+    for id in crate_items.owners() {
+        maybe_record_as_seed(tcx, id, &mut worklist, &mut unsolved_impl_item);
     }
 
-    (worklist, struct_constructors, unsolved_impl_item)
+    (worklist, unsolved_impl_item)
 }
 
 fn live_symbols_and_ignored_derived_traits(
     tcx: TyCtxt<'_>,
     (): (),
-) -> (LocalDefIdSet, LocalDefIdMap<FxIndexSet<(DefId, DefId)>>) {
-    let (worklist, struct_constructors, mut unsolved_items) = create_and_seed_worklist(tcx);
+) -> (LocalDefIdSet, LocalDefIdMap<FxIndexSet<DefId>>) {
+    let (worklist, mut unsolved_items) = create_and_seed_worklist(tcx);
     let mut symbol_visitor = MarkSymbolVisitor {
         worklist,
         tcx,
         maybe_typeck_results: None,
+        scanned: Default::default(),
         live_symbols: Default::default(),
         repr_unconditionally_treats_fields_as_live: false,
         repr_has_repr_simd: false,
         in_pat: false,
         ignore_variant_stack: vec![],
-        struct_constructors,
         ignored_derived_traits: Default::default(),
     };
     symbol_visitor.mark_live_symbols();
-    let mut items_to_check;
-    (items_to_check, unsolved_items) =
-        unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
-            symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id)
-        });
+
+    // We have marked the primary seeds as live. We now need to process unsolved items from traits
+    // and trait impls: add them to the work list if the trait or the implemented type is live.
+    let mut items_to_check: Vec<_> = unsolved_items
+        .extract_if(.., |&mut local_def_id| {
+            symbol_visitor.check_impl_or_impl_item_live(local_def_id)
+        })
+        .collect();
 
     while !items_to_check.is_empty() {
-        symbol_visitor.worklist =
-            items_to_check.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect();
+        symbol_visitor
+            .worklist
+            .extend(items_to_check.drain(..).map(|id| (id, ComesFromAllowExpect::No)));
         symbol_visitor.mark_live_symbols();
 
-        (items_to_check, unsolved_items) =
-            unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
-                symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id)
-            });
+        items_to_check.extend(unsolved_items.extract_if(.., |&mut local_def_id| {
+            symbol_visitor.check_impl_or_impl_item_live(local_def_id)
+        }));
     }
 
     (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
@@ -925,7 +869,7 @@ struct DeadItem {
 struct DeadVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
     live_symbols: &'tcx LocalDefIdSet,
-    ignored_derived_traits: &'tcx LocalDefIdMap<FxIndexSet<(DefId, DefId)>>,
+    ignored_derived_traits: &'tcx LocalDefIdMap<FxIndexSet<DefId>>,
 }
 
 enum ShouldWarnAboutField {
@@ -984,25 +928,7 @@ impl<'tcx> DeadVisitor<'tcx> {
         parent_item: Option<LocalDefId>,
         report_on: ReportOn,
     ) {
-        fn get_parent_if_enum_variant<'tcx>(
-            tcx: TyCtxt<'tcx>,
-            may_variant: LocalDefId,
-        ) -> LocalDefId {
-            if let Node::Variant(_) = tcx.hir_node_by_def_id(may_variant)
-                && let Some(enum_did) = tcx.opt_parent(may_variant.to_def_id())
-                && let Some(enum_local_id) = enum_did.as_local()
-                && let Node::Item(item) = tcx.hir_node_by_def_id(enum_local_id)
-                && let ItemKind::Enum(..) = item.kind
-            {
-                enum_local_id
-            } else {
-                may_variant
-            }
-        }
-
-        let Some(&first_item) = dead_codes.first() else {
-            return;
-        };
+        let Some(&first_item) = dead_codes.first() else { return };
         let tcx = self.tcx;
 
         let first_lint_level = first_item.level;
@@ -1011,81 +937,54 @@ impl<'tcx> DeadVisitor<'tcx> {
         let names: Vec<_> = dead_codes.iter().map(|item| item.name).collect();
         let spans: Vec<_> = dead_codes
             .iter()
-            .map(|item| match tcx.def_ident_span(item.def_id) {
-                Some(s) => s.with_ctxt(tcx.def_span(item.def_id).ctxt()),
-                None => tcx.def_span(item.def_id),
+            .map(|item| {
+                let span = tcx.def_span(item.def_id);
+                let ident_span = tcx.def_ident_span(item.def_id);
+                // FIXME(cjgillot) this SyntaxContext manipulation does not make any sense.
+                ident_span.map(|s| s.with_ctxt(span.ctxt())).unwrap_or(span)
             })
             .collect();
 
-        let descr = tcx.def_descr(first_item.def_id.to_def_id());
+        let mut descr = tcx.def_descr(first_item.def_id.to_def_id());
         // `impl` blocks are "batched" and (unlike other batching) might
         // contain different kinds of associated items.
-        let descr = if dead_codes.iter().any(|item| tcx.def_descr(item.def_id.to_def_id()) != descr)
-        {
-            "associated item"
-        } else {
-            descr
-        };
+        if dead_codes.iter().any(|item| tcx.def_descr(item.def_id.to_def_id()) != descr) {
+            descr = "associated item"
+        }
+
         let num = dead_codes.len();
         let multiple = num > 6;
         let name_list = names.into();
 
-        let parent_info = if let Some(parent_item) = parent_item {
+        let parent_info = parent_item.map(|parent_item| {
             let parent_descr = tcx.def_descr(parent_item.to_def_id());
             let span = if let DefKind::Impl { .. } = tcx.def_kind(parent_item) {
                 tcx.def_span(parent_item)
             } else {
                 tcx.def_ident_span(parent_item).unwrap()
             };
-            Some(ParentInfo { num, descr, parent_descr, span })
-        } else {
-            None
-        };
+            ParentInfo { num, descr, parent_descr, span }
+        });
 
-        let encl_def_id = parent_item.unwrap_or(first_item.def_id);
-        // If parent of encl_def_id is an enum, use the parent ID instead.
-        let encl_def_id = get_parent_if_enum_variant(tcx, encl_def_id);
+        let mut encl_def_id = parent_item.unwrap_or(first_item.def_id);
+        // `ignored_derived_traits` is computed for the enum, not for the variants.
+        if let DefKind::Variant = tcx.def_kind(encl_def_id) {
+            encl_def_id = tcx.local_parent(encl_def_id);
+        }
 
         let ignored_derived_impls =
-            if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) {
+            self.ignored_derived_traits.get(&encl_def_id).map(|ign_traits| {
                 let trait_list = ign_traits
                     .iter()
-                    .map(|(trait_id, _)| self.tcx.item_name(*trait_id))
+                    .map(|trait_id| self.tcx.item_name(*trait_id))
                     .collect::<Vec<_>>();
                 let trait_list_len = trait_list.len();
-                Some(IgnoredDerivedImpls {
+                IgnoredDerivedImpls {
                     name: self.tcx.item_name(encl_def_id.to_def_id()),
                     trait_list: trait_list.into(),
                     trait_list_len,
-                })
-            } else {
-                None
-            };
-
-        let enum_variants_with_same_name = dead_codes
-            .iter()
-            .filter_map(|dead_item| {
-                if let Node::ImplItem(ImplItem {
-                    kind: ImplItemKind::Fn(..) | ImplItemKind::Const(..),
-                    ..
-                }) = tcx.hir_node_by_def_id(dead_item.def_id)
-                    && let Some(impl_did) = tcx.opt_parent(dead_item.def_id.to_def_id())
-                    && let DefKind::Impl { of_trait: false } = tcx.def_kind(impl_did)
-                    && let ty::Adt(maybe_enum, _) = tcx.type_of(impl_did).skip_binder().kind()
-                    && maybe_enum.is_enum()
-                    && let Some(variant) =
-                        maybe_enum.variants().iter().find(|i| i.name == dead_item.name)
-                {
-                    Some(crate::errors::EnumVariantSameName {
-                        dead_descr: tcx.def_descr(dead_item.def_id.to_def_id()),
-                        dead_name: dead_item.name,
-                        variant_span: tcx.def_span(variant.def_id),
-                    })
-                } else {
-                    None
                 }
-            })
-            .collect();
+            });
 
         let diag = match report_on {
             ReportOn::TupleField => {
@@ -1132,16 +1031,42 @@ impl<'tcx> DeadVisitor<'tcx> {
                     ignored_derived_impls,
                 }
             }
-            ReportOn::NamedField => MultipleDeadCodes::DeadCodes {
-                multiple,
-                num,
-                descr,
-                participle,
-                name_list,
-                parent_info,
-                ignored_derived_impls,
-                enum_variants_with_same_name,
-            },
+            ReportOn::NamedField => {
+                let enum_variants_with_same_name = dead_codes
+                    .iter()
+                    .filter_map(|dead_item| {
+                        if let DefKind::AssocFn | DefKind::AssocConst =
+                            tcx.def_kind(dead_item.def_id)
+                            && let impl_did = tcx.local_parent(dead_item.def_id)
+                            && let DefKind::Impl { of_trait: false } = tcx.def_kind(impl_did)
+                            && let ty::Adt(maybe_enum, _) =
+                                tcx.type_of(impl_did).instantiate_identity().kind()
+                            && maybe_enum.is_enum()
+                            && let Some(variant) =
+                                maybe_enum.variants().iter().find(|i| i.name == dead_item.name)
+                        {
+                            Some(crate::errors::EnumVariantSameName {
+                                dead_descr: tcx.def_descr(dead_item.def_id.to_def_id()),
+                                dead_name: dead_item.name,
+                                variant_span: tcx.def_span(variant.def_id),
+                            })
+                        } else {
+                            None
+                        }
+                    })
+                    .collect();
+
+                MultipleDeadCodes::DeadCodes {
+                    multiple,
+                    num,
+                    descr,
+                    participle,
+                    name_list,
+                    parent_info,
+                    ignored_derived_impls,
+                    enum_variants_with_same_name,
+                }
+            }
         };
 
         let hir_id = tcx.local_def_id_to_hir_id(first_item.def_id);
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index af7ecf0830c..2ad0b5ff60e 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -8,6 +8,7 @@
 #![allow(internal_features)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
+#![feature(if_let_guard)]
 #![feature(map_try_insert)]
 #![feature(rustdoc_internals)]
 // tidy-alphabetical-end
diff --git a/compiler/rustc_thread_pool/Cargo.toml b/compiler/rustc_thread_pool/Cargo.toml
index b0194834264..c92984470b7 100644
--- a/compiler/rustc_thread_pool/Cargo.toml
+++ b/compiler/rustc_thread_pool/Cargo.toml
@@ -7,7 +7,6 @@ authors = [
 ]
 description = "Core APIs for Rayon - fork for rustc"
 license = "MIT OR Apache-2.0"
-rust-version = "1.63"
 edition = "2021"
 readme = "README.md"
 keywords = ["parallel", "thread", "concurrency", "join", "performance"]
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index 17c16e4aaff..acbbb6df9a5 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -382,6 +382,7 @@ impl<'a, K: 'a, V: 'a> Default for Iter<'a, K, V> {
 /// documentation for more.
 ///
 /// [`iter_mut`]: BTreeMap::iter_mut
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct IterMut<'a, K: 'a, V: 'a> {
     range: LazyLeafRange<marker::ValMut<'a>, K, V>,
@@ -391,7 +392,6 @@ pub struct IterMut<'a, K: 'a, V: 'a> {
     _marker: PhantomData<&'a mut (K, V)>,
 }
 
-#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "collection_debug", since = "1.17.0")]
 impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for IterMut<'_, K, V> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/library/std/src/sys/args/common.rs b/library/std/src/sys/args/common.rs
index e787105a05a..33f3794ee63 100644
--- a/library/std/src/sys/args/common.rs
+++ b/library/std/src/sys/args/common.rs
@@ -12,7 +12,7 @@ impl !Sync for Args {}
 
 impl Args {
     #[inline]
-    pub(super) fn new(args: Vec<OsString>) -> Self {
+    pub fn new(args: Vec<OsString>) -> Self {
         Args { iter: args.into_iter() }
     }
 }
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 59541bf12de..95707e37018 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -19,7 +19,7 @@ use serde_derive::Deserialize;
 use tracing::{instrument, span};
 
 use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags};
-use crate::core::build_steps::tool::{SourceType, copy_lld_artifacts};
+use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts};
 use crate::core::build_steps::{dist, llvm};
 use crate::core::builder;
 use crate::core::builder::{
@@ -1131,7 +1131,7 @@ impl Step for Rustc {
             cargo.env("RUSTC_BOLT_LINK_FLAGS", "1");
         }
 
-        let _guard = builder.msg_sysroot_tool(
+        let _guard = builder.msg_rustc_tool(
             Kind::Build,
             build_compiler.stage,
             format_args!("compiler artifacts{}", crate_description(&self.crates)),
@@ -1544,9 +1544,8 @@ impl Step for RustcLink {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CodegenBackend {
-    pub target: TargetSelection,
-    pub compiler: Compiler,
-    pub backend: CodegenBackendKind,
+    compilers: RustcPrivateCompilers,
+    backend: CodegenBackendKind,
 }
 
 fn needs_codegen_config(run: &RunConfig<'_>) -> bool {
@@ -1610,8 +1609,11 @@ impl Step for CodegenBackend {
             }
 
             run.builder.ensure(CodegenBackend {
-                target: run.target,
-                compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
+                compilers: RustcPrivateCompilers::new(
+                    run.builder,
+                    run.builder.top_stage,
+                    run.target,
+                ),
                 backend: backend.clone(),
             });
         }
@@ -1624,20 +1626,17 @@ impl Step for CodegenBackend {
             name = "CodegenBackend::run",
             skip_all,
             fields(
-                compiler = ?self.compiler,
-                target = ?self.target,
-                backend = ?self.target,
+                compilers = ?self.compilers,
+                backend = ?self.backend,
             ),
         ),
     )]
     fn run(self, builder: &Builder<'_>) {
-        let compiler = self.compiler;
-        let target = self.target;
         let backend = self.backend;
+        let target = self.compilers.target();
+        let build_compiler = self.compilers.build_compiler();
 
-        builder.ensure(Rustc::new(compiler, target));
-
-        if builder.config.keep_stage.contains(&compiler.stage) {
+        if builder.config.keep_stage.contains(&build_compiler.stage) {
             trace!("`keep-stage` requested");
             builder.info(
                 "WARNING: Using a potentially old codegen backend. \
@@ -1648,17 +1647,11 @@ impl Step for CodegenBackend {
             return;
         }
 
-        let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
-        if compiler_to_use != compiler {
-            builder.ensure(CodegenBackend { compiler: compiler_to_use, target, backend });
-            return;
-        }
-
-        let out_dir = builder.cargo_out(compiler, Mode::Codegen, target);
+        let out_dir = builder.cargo_out(build_compiler, Mode::Codegen, target);
 
         let mut cargo = builder::Cargo::new(
             builder,
-            compiler,
+            build_compiler,
             Mode::Codegen,
             SourceType::InTree,
             target,
@@ -1679,8 +1672,13 @@ impl Step for CodegenBackend {
 
         let tmp_stamp = BuildStamp::new(&out_dir).with_prefix("tmp");
 
-        let _guard =
-            builder.msg_build(compiler, format_args!("codegen backend {}", backend.name()), target);
+        let _guard = builder.msg_rustc_tool(
+            Kind::Build,
+            build_compiler.stage,
+            format_args!("codegen backend {}", backend.name()),
+            build_compiler.host,
+            target,
+        );
         let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false, false);
         if builder.config.dry_run() {
             return;
@@ -1700,10 +1698,17 @@ impl Step for CodegenBackend {
                 f.display()
             );
         }
-        let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, &backend);
+        let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, &backend);
         let codegen_backend = codegen_backend.to_str().unwrap();
         t!(stamp.add_stamp(codegen_backend).write());
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::build(&self.backend.crate_name(), self.compilers.target())
+                .built_by(self.compilers.build_compiler()),
+        )
+    }
 }
 
 /// Creates the `codegen-backends` folder for a compiler that's about to be
@@ -2190,8 +2195,10 @@ impl Step for Assemble {
                 continue;
             }
             builder.ensure(CodegenBackend {
-                compiler: build_compiler,
-                target: target_compiler.host,
+                compilers: RustcPrivateCompilers::from_build_and_target_compiler(
+                    build_compiler,
+                    target_compiler,
+                ),
                 backend: backend.clone(),
             });
         }
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 4699813abf4..0465a071159 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -20,7 +20,7 @@ use object::read::archive::ArchiveFile;
 use tracing::instrument;
 
 use crate::core::build_steps::doc::DocumentationFormat;
-use crate::core::build_steps::tool::{self, Tool};
+use crate::core::build_steps::tool::{self, RustcPrivateCompilers, Tool};
 use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor};
 use crate::core::build_steps::{compile, llvm};
 use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata};
@@ -425,19 +425,20 @@ impl Step for Rustc {
                 .as_ref()
                 .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc"))
             {
-                let rustdoc = builder.rustdoc(compiler);
+                let rustdoc = builder.rustdoc_for_compiler(compiler);
                 builder.install(&rustdoc, &image.join("bin"), FileType::Executable);
             }
 
             let ra_proc_macro_srv_compiler =
                 builder.compiler_for(compiler.stage, builder.config.host_target, compiler.host);
-            builder.ensure(compile::Rustc::new(ra_proc_macro_srv_compiler, compiler.host));
+            let compilers = RustcPrivateCompilers::from_build_compiler(
+                builder,
+                ra_proc_macro_srv_compiler,
+                compiler.host,
+            );
 
             if let Some(ra_proc_macro_srv) = builder.ensure_if_default(
-                tool::RustAnalyzerProcMacroSrv {
-                    compiler: ra_proc_macro_srv_compiler,
-                    target: compiler.host,
-                },
+                tool::RustAnalyzerProcMacroSrv::from_compilers(compilers),
                 builder.kind,
             ) {
                 let dst = image.join("libexec");
@@ -1172,7 +1173,7 @@ impl Step for PlainSourceTarball {
 
 #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
 pub struct Cargo {
-    pub compiler: Compiler,
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
 }
 
@@ -1188,7 +1189,7 @@ impl Step for Cargo {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Cargo {
-            compiler: run.builder.compiler_for(
+            build_compiler: run.builder.compiler_for(
                 run.builder.top_stage,
                 run.builder.config.host_target,
                 run.target,
@@ -1198,12 +1199,10 @@ impl Step for Cargo {
     }
 
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
-        let compiler = self.compiler;
+        let build_compiler = self.build_compiler;
         let target = self.target;
 
-        builder.ensure(compile::Rustc::new(compiler, target));
-
-        let cargo = builder.ensure(tool::Cargo { compiler, target });
+        let cargo = builder.ensure(tool::Cargo::from_build_compiler(build_compiler, target));
         let src = builder.src.join("src/tools/cargo");
         let etc = src.join("src/etc");
 
@@ -1228,7 +1227,7 @@ impl Step for Cargo {
 
 #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
 pub struct RustAnalyzer {
-    pub compiler: Compiler,
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
 }
 
@@ -1244,7 +1243,7 @@ impl Step for RustAnalyzer {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(RustAnalyzer {
-            compiler: run.builder.compiler_for(
+            build_compiler: run.builder.compiler_for(
                 run.builder.top_stage,
                 run.builder.config.host_target,
                 run.target,
@@ -1254,12 +1253,11 @@ impl Step for RustAnalyzer {
     }
 
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
-        let compiler = self.compiler;
         let target = self.target;
+        let compilers =
+            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target);
 
-        builder.ensure(compile::Rustc::new(compiler, target));
-
-        let rust_analyzer = builder.ensure(tool::RustAnalyzer { compiler, target });
+        let rust_analyzer = builder.ensure(tool::RustAnalyzer::from_compilers(compilers));
 
         let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple);
         tarball.set_overlay(OverlayKind::RustAnalyzer);
@@ -1270,9 +1268,9 @@ impl Step for RustAnalyzer {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Clippy {
-    pub compiler: Compiler,
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
 }
 
@@ -1288,7 +1286,7 @@ impl Step for Clippy {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Clippy {
-            compiler: run.builder.compiler_for(
+            build_compiler: run.builder.compiler_for(
                 run.builder.top_stage,
                 run.builder.config.host_target,
                 run.target,
@@ -1298,16 +1296,15 @@ impl Step for Clippy {
     }
 
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
-        let compiler = self.compiler;
         let target = self.target;
-
-        builder.ensure(compile::Rustc::new(compiler, target));
+        let compilers =
+            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target);
 
         // Prepare the image directory
         // We expect clippy to build, because we've exited this step above if tool
         // state for clippy isn't testing.
-        let clippy = builder.ensure(tool::Clippy { compiler, target });
-        let cargoclippy = builder.ensure(tool::CargoClippy { compiler, target });
+        let clippy = builder.ensure(tool::Clippy::from_compilers(compilers));
+        let cargoclippy = builder.ensure(tool::CargoClippy::from_compilers(compilers));
 
         let mut tarball = Tarball::new(builder, "clippy", &target.triple);
         tarball.set_overlay(OverlayKind::Clippy);
@@ -1319,9 +1316,9 @@ impl Step for Clippy {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Miri {
-    pub compiler: Compiler,
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
 }
 
@@ -1337,7 +1334,7 @@ impl Step for Miri {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Miri {
-            compiler: run.builder.compiler_for(
+            build_compiler: run.builder.compiler_for(
                 run.builder.top_stage,
                 run.builder.config.host_target,
                 run.target,
@@ -1354,15 +1351,12 @@ impl Step for Miri {
             return None;
         }
 
-        let compiler = self.compiler;
-        let target = self.target;
-
-        builder.ensure(compile::Rustc::new(compiler, target));
+        let compilers =
+            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target);
+        let miri = builder.ensure(tool::Miri::from_compilers(compilers));
+        let cargomiri = builder.ensure(tool::CargoMiri::from_compilers(compilers));
 
-        let miri = builder.ensure(tool::Miri { compiler, target });
-        let cargomiri = builder.ensure(tool::CargoMiri { compiler, target });
-
-        let mut tarball = Tarball::new(builder, "miri", &target.triple);
+        let mut tarball = Tarball::new(builder, "miri", &self.target.triple);
         tarball.set_overlay(OverlayKind::Miri);
         tarball.is_preview(true);
         tarball.add_file(&miri.tool_path, "bin", FileType::Executable);
@@ -1466,9 +1460,9 @@ impl Step for CodegenBackend {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Rustfmt {
-    pub compiler: Compiler,
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
 }
 
@@ -1484,7 +1478,7 @@ impl Step for Rustfmt {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Rustfmt {
-            compiler: run.builder.compiler_for(
+            build_compiler: run.builder.compiler_for(
                 run.builder.top_stage,
                 run.builder.config.host_target,
                 run.target,
@@ -1494,14 +1488,13 @@ impl Step for Rustfmt {
     }
 
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
-        let compiler = self.compiler;
-        let target = self.target;
+        let compilers =
+            RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target);
 
-        builder.ensure(compile::Rustc::new(compiler, target));
+        let rustfmt = builder.ensure(tool::Rustfmt::from_compilers(compilers));
+        let cargofmt = builder.ensure(tool::Cargofmt::from_compilers(compilers));
 
-        let rustfmt = builder.ensure(tool::Rustfmt { compiler, target });
-        let cargofmt = builder.ensure(tool::Cargofmt { compiler, target });
-        let mut tarball = Tarball::new(builder, "rustfmt", &target.triple);
+        let mut tarball = Tarball::new(builder, "rustfmt", &self.target.triple);
         tarball.set_overlay(OverlayKind::Rustfmt);
         tarball.is_preview(true);
         tarball.add_file(&rustfmt.tool_path, "bin", FileType::Executable);
@@ -1548,7 +1541,7 @@ impl Step for Extended {
         let mut built_tools = HashSet::new();
         macro_rules! add_component {
             ($name:expr => $step:expr) => {
-                if let Some(tarball) = builder.ensure_if_default($step, Kind::Dist) {
+                if let Some(Some(tarball)) = builder.ensure_if_default($step, Kind::Dist) {
                     tarballs.push(tarball);
                     built_tools.insert($name);
                 }
@@ -1568,12 +1561,12 @@ impl Step for Extended {
 
         add_component!("rust-docs" => Docs { host: target });
         add_component!("rust-json-docs" => JsonDocs { host: target });
-        add_component!("cargo" => Cargo { compiler, target });
-        add_component!("rustfmt" => Rustfmt { compiler, target });
-        add_component!("rust-analyzer" => RustAnalyzer { compiler, target });
+        add_component!("cargo" => Cargo { build_compiler: compiler, target });
+        add_component!("rustfmt" => Rustfmt { build_compiler: compiler, target });
+        add_component!("rust-analyzer" => RustAnalyzer { build_compiler: compiler, target });
         add_component!("llvm-components" => LlvmTools { target });
-        add_component!("clippy" => Clippy { compiler, target });
-        add_component!("miri" => Miri { compiler, target });
+        add_component!("clippy" => Clippy { build_compiler: compiler, target });
+        add_component!("miri" => Miri { build_compiler: compiler, target });
         add_component!("analysis" => Analysis { compiler, target });
         add_component!("rustc-codegen-cranelift" => CodegenBackend {
             compiler: builder.compiler(stage, target),
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 37418f640ac..d4539a0eb34 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -149,7 +149,7 @@ impl<P: Step> Step for RustbookSrc<P> {
             let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
 
             if let Some(compiler) = self.rustdoc_compiler {
-                let mut rustdoc = builder.rustdoc(compiler);
+                let mut rustdoc = builder.rustdoc_for_compiler(compiler);
                 rustdoc.pop();
                 let old_path = env::var_os("PATH").unwrap_or_default();
                 let new_path =
@@ -365,7 +365,7 @@ impl Step for Standalone {
             }
 
             let html = out.join(filename).with_extension("html");
-            let rustdoc = builder.rustdoc(compiler);
+            let rustdoc = builder.rustdoc_for_compiler(compiler);
             if up_to_date(&path, &html)
                 && up_to_date(&footer, &html)
                 && up_to_date(&favicon, &html)
@@ -463,7 +463,7 @@ impl Step for Releases {
         let html = out.join("releases.html");
         let tmppath = out.join("releases.md");
         let inpath = builder.src.join("RELEASES.md");
-        let rustdoc = builder.rustdoc(compiler);
+        let rustdoc = builder.rustdoc_for_compiler(compiler);
         if !up_to_date(&inpath, &html)
             || !up_to_date(&footer, &html)
             || !up_to_date(&favicon, &html)
@@ -811,7 +811,7 @@ impl Step for Rustc {
         let compiler = builder.compiler(stage, builder.config.host_target);
         builder.std(compiler, builder.config.host_target);
 
-        let _guard = builder.msg_sysroot_tool(
+        let _guard = builder.msg_rustc_tool(
             Kind::Doc,
             stage,
             format!("compiler{}", crate_description(&self.crates)),
@@ -901,6 +901,10 @@ impl Step for Rustc {
             builder.open_in_browser(index);
         }
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::doc("rustc", self.target).stage(self.stage))
+    }
 }
 
 macro_rules! tool_doc {
@@ -1018,6 +1022,10 @@ macro_rules! tool_doc {
                     })?
                 }
             }
+
+            fn metadata(&self) -> Option<StepMetadata> {
+                Some(StepMetadata::doc(stringify!($tool), self.target))
+            }
         }
     }
 }
diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs
index 4513a138e19..f628330e9ed 100644
--- a/src/bootstrap/src/core/build_steps/install.rs
+++ b/src/bootstrap/src/core/build_steps/install.rs
@@ -215,13 +215,13 @@ install!((self, builder, _config),
     };
     Cargo, alias = "cargo", Self::should_build(_config), only_hosts: true, {
         let tarball = builder
-            .ensure(dist::Cargo { compiler: self.compiler, target: self.target })
+            .ensure(dist::Cargo { build_compiler: self.compiler, target: self.target })
             .expect("missing cargo");
         install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball);
     };
     RustAnalyzer, alias = "rust-analyzer", Self::should_build(_config), only_hosts: true, {
         if let Some(tarball) =
-            builder.ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target })
+            builder.ensure(dist::RustAnalyzer { build_compiler: self.compiler, target: self.target })
         {
             install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball);
         } else {
@@ -232,12 +232,12 @@ install!((self, builder, _config),
     };
     Clippy, alias = "clippy", Self::should_build(_config), only_hosts: true, {
         let tarball = builder
-            .ensure(dist::Clippy { compiler: self.compiler, target: self.target })
+            .ensure(dist::Clippy { build_compiler: self.compiler, target: self.target })
             .expect("missing clippy");
         install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball);
     };
     Miri, alias = "miri", Self::should_build(_config), only_hosts: true, {
-        if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) {
+        if let Some(tarball) = builder.ensure(dist::Miri { build_compiler: self.compiler, target: self.target }) {
             install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
         } else {
             // Miri is only available on nightly
@@ -257,7 +257,7 @@ install!((self, builder, _config),
     };
     Rustfmt, alias = "rustfmt", Self::should_build(_config), only_hosts: true, {
         if let Some(tarball) = builder.ensure(dist::Rustfmt {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.target
         }) {
             install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball);
diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs
index 4d61b38c876..108b7f90c14 100644
--- a/src/bootstrap/src/core/build_steps/perf.rs
+++ b/src/bootstrap/src/core/build_steps/perf.rs
@@ -157,7 +157,7 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#);
     if let Some(opts) = args.cmd.shared_opts()
         && opts.profiles.contains(&Profile::Doc)
     {
-        builder.ensure(Rustdoc { compiler });
+        builder.ensure(Rustdoc { target_compiler: compiler });
     }
 
     let sysroot = builder.ensure(Sysroot::new(compiler));
diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index b2293fdd9b5..962dd372849 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -9,7 +9,7 @@ use clap_complete::{Generator, shells};
 
 use crate::core::build_steps::dist::distdir;
 use crate::core::build_steps::test;
-use crate::core::build_steps::tool::{self, SourceType, Tool};
+use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, Tool};
 use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor};
 use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
@@ -135,13 +135,13 @@ impl Step for Miri {
         }
 
         // This compiler runs on the host, we'll just use it for the target.
-        let target_compiler = builder.compiler(stage, target);
-        let miri_build = builder.ensure(tool::Miri { compiler: target_compiler, target });
-        // Rustc tools are off by one stage, so use the build compiler to run miri.
+        let compilers = RustcPrivateCompilers::new(builder, stage, target);
+        let miri_build = builder.ensure(tool::Miri::from_compilers(compilers));
         let host_compiler = miri_build.build_compiler;
 
         // Get a target sysroot for Miri.
-        let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target);
+        let miri_sysroot =
+            test::Miri::build_miri_sysroot(builder, compilers.target_compiler(), target);
 
         // # Run miri.
         // Running it via `cargo run` as that figures out the right dylib path.
@@ -465,8 +465,8 @@ impl Step for Rustfmt {
             std::process::exit(1);
         }
 
-        let compiler = builder.compiler(stage, host);
-        let rustfmt_build = builder.ensure(tool::Rustfmt { compiler, target: host });
+        let compilers = RustcPrivateCompilers::new(builder, stage, host);
+        let rustfmt_build = builder.ensure(tool::Rustfmt::from_compilers(compilers));
 
         let mut rustfmt = tool::prepare_tool_cargo(
             builder,
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 8bee00a9c13..09d2657b666 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -4,10 +4,12 @@
 //! However, this contains ~all test parts we expect people to be able to build and run locally.
 
 use std::collections::HashSet;
+use std::env::split_paths;
 use std::ffi::{OsStr, OsString};
 use std::path::{Path, PathBuf};
 use std::{env, fs, iter};
 
+use build_helper::exit;
 #[cfg(feature = "tracing")]
 use tracing::instrument;
 
@@ -17,7 +19,10 @@ 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, SourceType, Tool};
+use crate::core::build_steps::tool::{
+    self, COMPILETEST_ALLOW_FEATURES, RustcPrivateCompilers, SourceType, Tool, ToolTargetBuildMode,
+    get_tool_target_compiler,
+};
 use crate::core::build_steps::toolstate::ToolState;
 use crate::core::build_steps::{compile, dist, llvm};
 use crate::core::builder::{
@@ -29,8 +34,8 @@ use crate::core::config::flags::{Subcommand, get_completion};
 use crate::utils::build_stamp::{self, BuildStamp};
 use crate::utils::exec::{BootstrapCommand, command};
 use crate::utils::helpers::{
-    self, LldThreads, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, linker_args,
-    linker_flags, t, target_supports_cranelift_backend, up_to_date,
+    self, LldThreads, add_dylib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var,
+    linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date,
 };
 use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
 use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, debug, envify};
@@ -226,7 +231,7 @@ impl Step for HtmlCheck {
 /// order to test cargo.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Cargotest {
-    stage: u32,
+    build_compiler: Compiler,
     host: TargetSelection,
 }
 
@@ -239,7 +244,19 @@ impl Step for Cargotest {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Cargotest { stage: run.builder.top_stage, host: run.target });
+        if run.builder.top_stage == 0 {
+            eprintln!(
+                "ERROR: running cargotest with stage 0 is currently unsupported. Use at least stage 1."
+            );
+            exit!(1);
+        }
+        // We want to build cargo stage N (where N == top_stage), and rustc stage N,
+        // and test both of these together.
+        // So we need to get a build compiler stage N-1 to build the stage N components.
+        run.builder.ensure(Cargotest {
+            build_compiler: run.builder.compiler(run.builder.top_stage - 1, run.target),
+            host: run.target,
+        });
     }
 
     /// Runs the `cargotest` tool as compiled in `stage` by the `host` compiler.
@@ -247,9 +264,19 @@ impl Step for Cargotest {
     /// This tool in `src/tools` will check out a few Rust projects and run `cargo
     /// test` to ensure that we don't regress the test suites there.
     fn run(self, builder: &Builder<'_>) {
-        let compiler = builder.compiler(self.stage, self.host);
-        builder.ensure(compile::Rustc::new(compiler, compiler.host));
-        let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host });
+        // cargotest's staging has several pieces:
+        // consider ./x test cargotest --stage=2.
+        //
+        // The test goal is to exercise a (stage 2 cargo, stage 2 rustc) pair through a stage 2
+        // cargotest tool.
+        // To produce the stage 2 cargo and cargotest, we need to do so with the stage 1 rustc and std.
+        // Importantly, the stage 2 rustc being tested (`tested_compiler`) via stage 2 cargotest is
+        // the rustc built by an earlier stage 1 rustc (the build_compiler). These are two different
+        // compilers!
+        let cargo =
+            builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host));
+        let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host);
+        builder.std(tested_compiler, self.host);
 
         // Note that this is a short, cryptic, and not scoped directory name. This
         // is currently to minimize the length of path on Windows where we otherwise
@@ -262,23 +289,28 @@ impl Step for Cargotest {
         cmd.arg(&cargo.tool_path)
             .arg(&out_dir)
             .args(builder.config.test_args())
-            .env("RUSTC", builder.rustc(compiler))
-            .env("RUSTDOC", builder.rustdoc(compiler));
+            .env("RUSTC", builder.rustc(tested_compiler))
+            .env("RUSTDOC", builder.rustdoc_for_compiler(tested_compiler));
         add_rustdoc_cargo_linker_args(
             &mut cmd,
             builder,
-            compiler.host,
+            tested_compiler.host,
             LldThreads::No,
-            compiler.stage,
+            tested_compiler.stage,
         );
         cmd.delay_failure().run(builder);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::test("cargotest", self.host).stage(self.build_compiler.stage + 1))
+    }
 }
 
 /// Runs `cargo test` for cargo itself.
+/// We label these tests as "cargo self-tests".
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Cargo {
-    stage: u32,
+    build_compiler: Compiler,
     host: TargetSelection,
 }
 
@@ -295,35 +327,33 @@ impl Step for Cargo {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        // If stage is explicitly set or not lower than 2, keep it. Otherwise, make sure it's at least 2
-        // as tests for this step don't work with a lower stage.
-        let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
-            run.builder.top_stage
-        } else {
-            2
-        };
-
-        run.builder.ensure(Cargo { stage, host: run.target });
+        run.builder.ensure(Cargo {
+            build_compiler: get_tool_target_compiler(
+                run.builder,
+                ToolTargetBuildMode::Build(run.target),
+            ),
+            host: run.target,
+        });
     }
 
     /// Runs `cargo test` for `cargo` packaged with Rust.
     fn run(self, builder: &Builder<'_>) {
-        let stage = self.stage;
-
-        if stage < 2 {
-            eprintln!("WARNING: cargo tests on stage {stage} may not behave well.");
-            eprintln!("HELP: consider using stage 2");
-        }
-
-        let compiler = builder.compiler(stage, self.host);
-
-        let cargo = builder.ensure(tool::Cargo { compiler, target: self.host });
-        let compiler = cargo.build_compiler;
+        // When we do a "stage 1 cargo self-test", it means that we test the stage 1 rustc
+        // using stage 1 cargo. So we actually build cargo using the stage 0 compiler, and then
+        // run its tests against the stage 1 compiler (called `tested_compiler` below).
+        builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host));
+
+        let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host);
+        builder.std(tested_compiler, self.host);
+        // We also need to build rustdoc for cargo tests
+        // It will be located in the bindir of `tested_compiler`, so we don't need to explicitly
+        // pass its path to Cargo.
+        builder.rustdoc_for_compiler(tested_compiler);
 
         let cargo = tool::prepare_tool_cargo(
             builder,
-            compiler,
-            Mode::ToolRustc,
+            self.build_compiler,
+            Mode::ToolTarget,
             self.host,
             Kind::Test,
             Self::CRATE_PATH,
@@ -340,7 +370,25 @@ impl Step for Cargo {
         // Forcibly disable tests using nightly features since any changes to
         // those features won't be able to land.
         cargo.env("CARGO_TEST_DISABLE_NIGHTLY", "1");
-        cargo.env("PATH", path_for_cargo(builder, compiler));
+
+        // Configure PATH to find the right rustc. NB. we have to use PATH
+        // and not RUSTC because the Cargo test suite has tests that will
+        // fail if rustc is not spelled `rustc`.
+        cargo.env("PATH", bin_path_for_cargo(builder, tested_compiler));
+
+        // The `cargo` command configured above has dylib dir path set to the `build_compiler`'s
+        // libdir. That causes issues in cargo test, because the programs that cargo compiles are
+        // incorrectly picking that libdir, even though they should be picking the
+        // `tested_compiler`'s libdir. We thus have to override the precedence here.
+        let mut existing_dylib_paths = cargo
+            .get_envs()
+            .find(|(k, _)| *k == OsStr::new(dylib_path_var()))
+            .and_then(|(_, v)| v)
+            .map(|value| split_paths(value).collect::<Vec<PathBuf>>())
+            .unwrap_or_default();
+        existing_dylib_paths.insert(0, builder.rustc_libdir(tested_compiler));
+        add_dylib_path(existing_dylib_paths, &mut cargo);
+
         // Cargo's test suite uses `CARGO_RUSTC_CURRENT_DIR` to determine the path that `file!` is
         // relative to. Cargo no longer sets this env var, so we have to do that. This has to be the
         // same value as `-Zroot-dir`.
@@ -352,7 +400,7 @@ impl Step for Cargo {
                 crates: vec!["cargo".into()],
                 target: self.host.triple.to_string(),
                 host: self.host.triple.to_string(),
-                stage,
+                stage: self.build_compiler.stage + 1,
             },
             builder,
         );
@@ -364,8 +412,7 @@ impl Step for Cargo {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct RustAnalyzer {
-    stage: u32,
-    host: TargetSelection,
+    compilers: RustcPrivateCompilers,
 }
 
 impl Step for RustAnalyzer {
@@ -378,19 +425,18 @@ impl Step for RustAnalyzer {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Self { stage: run.builder.top_stage, host: run.target });
+        run.builder.ensure(Self {
+            compilers: RustcPrivateCompilers::new(
+                run.builder,
+                run.builder.top_stage,
+                run.builder.host_target,
+            ),
+        });
     }
 
     /// Runs `cargo test` for rust-analyzer
     fn run(self, builder: &Builder<'_>) {
-        let stage = self.stage;
-        let host = self.host;
-        let compiler = builder.compiler(stage, host);
-        let compiler = tool::get_tool_rustc_compiler(builder, compiler);
-
-        // We don't need to build the whole Rust Analyzer for the proc-macro-srv test suite,
-        // but we do need the standard library to be present.
-        builder.ensure(compile::Rustc::new(compiler, host));
+        let host = self.compilers.target();
 
         let workspace_path = "src/tools/rust-analyzer";
         // until the whole RA test suite runs on `i686`, we only run
@@ -398,7 +444,7 @@ impl Step for RustAnalyzer {
         let crate_path = "src/tools/rust-analyzer/crates/proc-macro-srv";
         let mut cargo = tool::prepare_tool_cargo(
             builder,
-            compiler,
+            self.compilers.build_compiler(),
             Mode::ToolRustc,
             host,
             Kind::Test,
@@ -425,8 +471,7 @@ impl Step for RustAnalyzer {
 /// Runs `cargo test` for rustfmt.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Rustfmt {
-    stage: u32,
-    host: TargetSelection,
+    compilers: RustcPrivateCompilers,
 }
 
 impl Step for Rustfmt {
@@ -438,36 +483,39 @@ impl Step for Rustfmt {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Rustfmt { stage: run.builder.top_stage, host: run.target });
+        run.builder.ensure(Rustfmt {
+            compilers: RustcPrivateCompilers::new(
+                run.builder,
+                run.builder.top_stage,
+                run.builder.host_target,
+            ),
+        });
     }
 
     /// Runs `cargo test` for rustfmt.
     fn run(self, builder: &Builder<'_>) {
-        let stage = self.stage;
-        let host = self.host;
-        let compiler = builder.compiler(stage, host);
-
-        let tool_result = builder.ensure(tool::Rustfmt { compiler, target: self.host });
-        let compiler = tool_result.build_compiler;
+        let tool_result = builder.ensure(tool::Rustfmt::from_compilers(self.compilers));
+        let build_compiler = tool_result.build_compiler;
+        let target = self.compilers.target();
 
         let mut cargo = tool::prepare_tool_cargo(
             builder,
-            compiler,
+            build_compiler,
             Mode::ToolRustc,
-            host,
+            target,
             Kind::Test,
             "src/tools/rustfmt",
             SourceType::InTree,
             &[],
         );
 
-        let dir = testdir(builder, compiler.host);
+        let dir = testdir(builder, target);
         t!(fs::create_dir_all(&dir));
         cargo.env("RUSTFMT_TEST_DIR", dir);
 
         cargo.add_rustc_lib_path(builder);
 
-        run_cargo_test(cargo, &[], &[], "rustfmt", host, builder);
+        run_cargo_test(cargo, &[], &[], "rustfmt", target, builder);
     }
 }
 
@@ -542,12 +590,14 @@ impl Step for Miri {
         }
 
         // This compiler runs on the host, we'll just use it for the target.
-        let target_compiler = builder.compiler(stage, host);
+        let compilers = RustcPrivateCompilers::new(builder, stage, host);
 
         // Build our tools.
-        let miri = builder.ensure(tool::Miri { compiler: target_compiler, target: host });
+        let miri = builder.ensure(tool::Miri::from_compilers(compilers));
         // the ui tests also assume cargo-miri has been built
-        builder.ensure(tool::CargoMiri { compiler: target_compiler, target: host });
+        builder.ensure(tool::CargoMiri::from_compilers(compilers));
+
+        let target_compiler = compilers.target_compiler();
 
         // We also need sysroots, for Miri and for the host (the latter for build scripts).
         // This is for the tests so everything is done with the target compiler.
@@ -599,7 +649,7 @@ impl Step for Miri {
         cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg());
 
         {
-            let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target);
+            let _guard = builder.msg_rustc_tool(Kind::Test, stage, "miri", host, target);
             let _time = helpers::timeit(builder);
             cargo.run(builder);
         }
@@ -615,7 +665,7 @@ impl Step for Miri {
             cargo.args(["tests/pass", "tests/panic"]);
 
             {
-                let _guard = builder.msg_sysroot_tool(
+                let _guard = builder.msg_rustc_tool(
                     Kind::Test,
                     stage,
                     "miri (mir-opt-level 4)",
@@ -692,7 +742,7 @@ impl Step for CargoMiri {
         // Finally, run everything.
         let mut cargo = BootstrapCommand::from(cargo);
         {
-            let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target);
+            let _guard = builder.msg_rustc_tool(Kind::Test, stage, "cargo-miri", host, target);
             let _time = helpers::timeit(builder);
             cargo.run(builder);
         }
@@ -762,7 +812,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Clippy {
-    host: TargetSelection,
+    compilers: RustcPrivateCompilers,
 }
 
 impl Step for Clippy {
@@ -775,23 +825,30 @@ impl Step for Clippy {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Clippy { host: run.target });
+        run.builder.ensure(Clippy {
+            compilers: RustcPrivateCompilers::new(
+                run.builder,
+                run.builder.top_stage,
+                run.builder.host_target,
+            ),
+        });
     }
 
     /// Runs `cargo test` for clippy.
     fn run(self, builder: &Builder<'_>) {
-        let stage = builder.top_stage;
-        let host = self.host;
+        let host = self.compilers.target();
+
         // We need to carefully distinguish the compiler that builds clippy, and the compiler
         // that is linked into the clippy being tested. `target_compiler` is the latter,
         // and it must also be used by clippy's test runner to build tests and their dependencies.
-        let target_compiler = builder.compiler(stage, host);
+        let compilers = self.compilers;
+        let target_compiler = compilers.target_compiler();
 
-        let tool_result = builder.ensure(tool::Clippy { compiler: target_compiler, target: host });
-        let tool_compiler = tool_result.build_compiler;
+        let tool_result = builder.ensure(tool::Clippy::from_compilers(compilers));
+        let build_compiler = tool_result.build_compiler;
         let mut cargo = tool::prepare_tool_cargo(
             builder,
-            tool_compiler,
+            build_compiler,
             Mode::ToolRustc,
             host,
             Kind::Test,
@@ -800,9 +857,10 @@ impl Step for Clippy {
             &[],
         );
 
-        cargo.env("RUSTC_TEST_SUITE", builder.rustc(tool_compiler));
-        cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(tool_compiler));
-        let host_libs = builder.stage_out(tool_compiler, Mode::ToolRustc).join(builder.cargo_dir());
+        cargo.env("RUSTC_TEST_SUITE", builder.rustc(build_compiler));
+        cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(build_compiler));
+        let host_libs =
+            builder.stage_out(build_compiler, Mode::ToolRustc).join(builder.cargo_dir());
         cargo.env("HOST_LIBS", host_libs);
 
         // Build the standard library that the tests can use.
@@ -831,8 +889,7 @@ impl Step for Clippy {
         cargo.add_rustc_lib_path(builder);
         let cargo = prepare_cargo_test(cargo, &[], &[], host, builder);
 
-        let _guard =
-            builder.msg_sysroot_tool(Kind::Test, tool_compiler.stage, "clippy", host, host);
+        let _guard = builder.msg_rustc_tool(Kind::Test, build_compiler.stage, "clippy", host, host);
 
         // Clippy reports errors if it blessed the outputs
         if cargo.allow_failure().run(builder) {
@@ -846,10 +903,7 @@ impl Step for Clippy {
     }
 }
 
-fn path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString {
-    // Configure PATH to find the right rustc. NB. we have to use PATH
-    // and not RUSTC because the Cargo test suite has tests that will
-    // fail if rustc is not spelled `rustc`.
+fn bin_path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString {
     let path = builder.sysroot(compiler).join("bin");
     let old_path = env::var_os("PATH").unwrap_or_default();
     env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect("")
@@ -884,7 +938,7 @@ impl Step for RustdocTheme {
             .env("RUSTC_SYSROOT", builder.sysroot(self.compiler))
             .env("RUSTDOC_LIBDIR", builder.sysroot_target_libdir(self.compiler, self.compiler.host))
             .env("CFG_RELEASE_CHANNEL", &builder.config.channel)
-            .env("RUSTDOC_REAL", builder.rustdoc(self.compiler))
+            .env("RUSTDOC_REAL", builder.rustdoc_for_compiler(self.compiler))
             .env("RUSTC_BOOTSTRAP", "1");
         cmd.args(linker_args(builder, self.compiler.host, LldThreads::No, self.compiler.stage));
 
@@ -1043,7 +1097,11 @@ impl Step for RustdocGUI {
         let mut cmd = builder.tool_cmd(Tool::RustdocGUITest);
 
         let out_dir = builder.test_out(self.target).join("rustdoc-gui");
-        build_stamp::clear_if_dirty(builder, &out_dir, &builder.rustdoc(self.compiler));
+        build_stamp::clear_if_dirty(
+            builder,
+            &out_dir,
+            &builder.rustdoc_for_compiler(self.compiler),
+        );
 
         if let Some(src) = builder.config.src.to_str() {
             cmd.arg("--rust-src").arg(src);
@@ -1059,7 +1117,7 @@ impl Step for RustdocGUI {
 
         cmd.arg("--jobs").arg(builder.jobs().to_string());
 
-        cmd.env("RUSTDOC", builder.rustdoc(self.compiler))
+        cmd.env("RUSTDOC", builder.rustdoc_for_compiler(self.compiler))
             .env("RUSTC", builder.rustc(self.compiler));
 
         add_rustdoc_cargo_linker_args(
@@ -1095,7 +1153,7 @@ impl Step for RustdocGUI {
         }
 
         let _time = helpers::timeit(builder);
-        let _guard = builder.msg_sysroot_tool(
+        let _guard = builder.msg_rustc_tool(
             Kind::Test,
             self.compiler.stage,
             "rustdoc-gui",
@@ -1737,7 +1795,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
                 // If we're using `--stage 0`, we should provide the bootstrap cargo.
                 builder.initial_cargo.clone()
             } else {
-                builder.ensure(tool::Cargo { compiler, target: compiler.host }).tool_path
+                builder.ensure(tool::Cargo::from_build_compiler(compiler, compiler.host)).tool_path
             };
 
             cmd.arg("--cargo-path").arg(cargo_path);
@@ -1756,7 +1814,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             || mode == "rustdoc-json"
             || suite == "coverage-run-rustdoc"
         {
-            cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler));
+            cmd.arg("--rustdoc-path").arg(builder.rustdoc_for_compiler(compiler));
         }
 
         if mode == "rustdoc-json" {
@@ -2272,7 +2330,7 @@ impl BookTest {
 
         // 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(compiler);
+        let mut rustdoc_path = builder.rustdoc_for_compiler(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)))
@@ -2595,7 +2653,7 @@ fn run_cargo_test<'a>(
     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_sysroot_tool(Kind::Test, compiler.stage, what, compiler.host, target)
+        builder.msg_rustc_tool(Kind::Test, compiler.stage, what, compiler.host, target)
     });
 
     #[cfg(feature = "build-metrics")]
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index f5fa33b98f3..e3f49fa126e 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -71,13 +71,9 @@ impl Builder<'_> {
     ) -> Option<gha::Group> {
         match mode {
             // depends on compiler stage, different to host compiler
-            Mode::ToolRustc => self.msg_sysroot_tool(
-                kind,
-                build_stage,
-                format_args!("tool {tool}"),
-                *host,
-                *target,
-            ),
+            Mode::ToolRustc => {
+                self.msg_rustc_tool(kind, build_stage, format_args!("tool {tool}"), *host, *target)
+            }
             // doesn't depend on compiler, same as host compiler
             _ => self.msg(kind, build_stage, format_args!("tool {tool}"), *host, *target),
         }
@@ -90,11 +86,8 @@ impl Builder<'_> {
 pub struct ToolBuildResult {
     /// Artifact path of the corresponding tool that was built.
     pub tool_path: PathBuf,
-    /// Compiler used to build the tool. For non-`ToolRustc` tools this is equal to `target_compiler`.
-    /// For `ToolRustc` this is one stage before of the `target_compiler`.
+    /// Compiler used to build the tool.
     pub build_compiler: Compiler,
-    /// Target compiler passed to `Step`.
-    pub target_compiler: Compiler,
 }
 
 impl Step for ToolBuild {
@@ -108,22 +101,15 @@ impl Step for ToolBuild {
     ///
     /// This will build the specified tool with the specified `host` compiler in
     /// `stage` into the normal cargo output directory.
-    fn run(mut self, builder: &Builder<'_>) -> ToolBuildResult {
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         let target = self.target;
         let mut tool = self.tool;
         let path = self.path;
 
-        let target_compiler = self.build_compiler;
-        self.build_compiler = if self.mode == Mode::ToolRustc {
-            get_tool_rustc_compiler(builder, self.build_compiler)
-        } else {
-            self.build_compiler
-        };
-
         match self.mode {
             Mode::ToolRustc => {
-                // If compiler was forced, its artifacts should have been prepared earlier.
-                if !self.build_compiler.is_forced_compiler() {
+                // FIXME: remove this, it's only needed for download-rustc...
+                if !self.build_compiler.is_forced_compiler() && builder.download_rustc() {
                     builder.std(self.build_compiler, self.build_compiler.host);
                     builder.ensure(compile::Rustc::new(self.build_compiler, target));
                 }
@@ -184,8 +170,7 @@ impl Step for ToolBuild {
             Kind::Build,
             self.mode,
             self.tool,
-            // A stage N tool is built with the stage N-1 compiler.
-            self.build_compiler.stage + 1,
+            self.build_compiler.stage,
             &self.build_compiler.host,
             &self.target,
         );
@@ -216,7 +201,7 @@ impl Step for ToolBuild {
                     .join(format!("lib{tool}.rlib")),
             };
 
-            ToolBuildResult { tool_path, build_compiler: self.build_compiler, target_compiler }
+            ToolBuildResult { tool_path, build_compiler: self.build_compiler }
         }
     }
 }
@@ -346,27 +331,6 @@ pub fn prepare_tool_cargo(
     cargo
 }
 
-/// Handle stage-off logic for `ToolRustc` tools when necessary.
-pub(crate) fn get_tool_rustc_compiler(
-    builder: &Builder<'_>,
-    target_compiler: Compiler,
-) -> Compiler {
-    if target_compiler.is_forced_compiler() {
-        return target_compiler;
-    }
-
-    if builder.download_rustc() && target_compiler.stage == 1 {
-        // We shouldn't drop to stage0 compiler when using CI rustc.
-        return builder.compiler(1, builder.config.host_target);
-    }
-
-    // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
-    // we'd have stageN/bin/rustc and stageN/bin/$rustc_tool be effectively different stage
-    // compilers, which isn't what we want. Rustc tools should be linked in the same way as the
-    // compiler it's paired with, so it must be built with the previous stage compiler.
-    builder.compiler(target_compiler.stage.saturating_sub(1), builder.config.host_target)
-}
-
 /// Determines how to build a `ToolTarget`, i.e. which compiler should be used to compile it.
 /// The compiler stage is automatically bumped if we need to cross-compile a stage 1 tool.
 pub enum ToolTargetBuildMode {
@@ -719,15 +683,21 @@ impl Step for RemoteTestServer {
     }
 }
 
-#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
+/// Represents `Rustdoc` that either comes from the external stage0 sysroot or that is built
+/// locally.
+/// Rustdoc is special, because it both essentially corresponds to a `Compiler` (that can be
+/// externally provided), but also to a `ToolRustc` tool.
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Rustdoc {
-    /// This should only ever be 0 or 2.
-    /// We sometimes want to reference the "bootstrap" rustdoc, which is why this option is here.
-    pub compiler: Compiler,
+    /// If the stage of `target_compiler` is `0`, then rustdoc is externally provided.
+    /// Otherwise it is built locally.
+    pub target_compiler: Compiler,
 }
 
 impl Step for Rustdoc {
-    type Output = ToolBuildResult;
+    /// Path to the built rustdoc binary.
+    type Output = PathBuf;
+
     const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
@@ -736,26 +706,25 @@ impl Step for Rustdoc {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder
-            .ensure(Rustdoc { compiler: run.builder.compiler(run.builder.top_stage, run.target) });
+        run.builder.ensure(Rustdoc {
+            target_compiler: run.builder.compiler(run.builder.top_stage, run.target),
+        });
     }
 
-    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
-        let target_compiler = self.compiler;
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        let target_compiler = self.target_compiler;
         let target = target_compiler.host;
 
+        // If stage is 0, we use a prebuilt rustdoc from stage0
         if target_compiler.stage == 0 {
             if !target_compiler.is_snapshot(builder) {
                 panic!("rustdoc in stage 0 must be snapshot rustdoc");
             }
 
-            return ToolBuildResult {
-                tool_path: builder.initial_rustdoc.clone(),
-                build_compiler: target_compiler,
-                target_compiler,
-            };
+            return builder.initial_rustdoc.clone();
         }
 
+        // If stage is higher, we build rustdoc instead
         let bin_rustdoc = || {
             let sysroot = builder.sysroot(target_compiler);
             let bindir = sysroot.join("bin");
@@ -767,10 +736,7 @@ impl Step for Rustdoc {
 
         // If CI rustc is enabled and we haven't modified the rustdoc sources,
         // use the precompiled rustdoc from CI rustc's sysroot to speed up bootstrapping.
-        if builder.download_rustc()
-            && target_compiler.stage > 0
-            && builder.rust_info().is_managed_git_subrepository()
-        {
+        if builder.download_rustc() && builder.rust_info().is_managed_git_subrepository() {
             let files_to_track = &["src/librustdoc", "src/tools/rustdoc", "src/rustdoc-json-types"];
 
             // Check if unchanged
@@ -783,12 +749,7 @@ impl Step for Rustdoc {
 
                 let bin_rustdoc = bin_rustdoc();
                 builder.copy_link(&precompiled_rustdoc, &bin_rustdoc, FileType::Executable);
-
-                return ToolBuildResult {
-                    tool_path: bin_rustdoc,
-                    build_compiler: target_compiler,
-                    target_compiler,
-                };
+                return bin_rustdoc;
             }
         }
 
@@ -804,9 +765,10 @@ impl Step for Rustdoc {
             extra_features.push("jemalloc".to_string());
         }
 
-        let ToolBuildResult { tool_path, build_compiler, target_compiler } =
-            builder.ensure(ToolBuild {
-                build_compiler: target_compiler,
+        let compilers = RustcPrivateCompilers::from_target_compiler(builder, target_compiler);
+        let tool_path = builder
+            .ensure(ToolBuild {
+                build_compiler: compilers.build_compiler,
                 target,
                 // Cargo adds a number of paths to the dylib search path on windows, which results in
                 // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
@@ -819,38 +781,41 @@ impl Step for Rustdoc {
                 allow_features: "",
                 cargo_args: Vec::new(),
                 artifact_kind: ToolArtifactKind::Binary,
-            });
-
-        // don't create a stage0-sysroot/bin directory.
-        if target_compiler.stage > 0 {
-            if builder.config.rust_debuginfo_level_tools == DebuginfoLevel::None {
-                // Due to LTO a lot of debug info from C++ dependencies such as jemalloc can make it into
-                // our final binaries
-                compile::strip_debug(builder, target, &tool_path);
-            }
-            let bin_rustdoc = bin_rustdoc();
-            builder.copy_link(&tool_path, &bin_rustdoc, FileType::Executable);
-            ToolBuildResult { tool_path: bin_rustdoc, build_compiler, target_compiler }
-        } else {
-            ToolBuildResult { tool_path, build_compiler, target_compiler }
+            })
+            .tool_path;
+
+        if builder.config.rust_debuginfo_level_tools == DebuginfoLevel::None {
+            // Due to LTO a lot of debug info from C++ dependencies such as jemalloc can make it into
+            // our final binaries
+            compile::strip_debug(builder, target, &tool_path);
         }
+        let bin_rustdoc = bin_rustdoc();
+        builder.copy_link(&tool_path, &bin_rustdoc, FileType::Executable);
+        bin_rustdoc
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
         Some(
-            StepMetadata::build("rustdoc", self.compiler.host)
-                // rustdoc is ToolRustc, so stage N rustdoc is built by stage N-1 rustc
-                // FIXME: make this stage deduction automatic somehow
-                // FIXME: log the compiler that actually built ToolRustc steps
-                .stage(self.compiler.stage.saturating_sub(1)),
+            StepMetadata::build("rustdoc", self.target_compiler.host)
+                .stage(self.target_compiler.stage),
         )
     }
 }
 
+/// Builds the cargo tool.
+/// Note that it can be built using a stable compiler.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Cargo {
-    pub compiler: Compiler,
-    pub target: TargetSelection,
+    build_compiler: Compiler,
+    target: TargetSelection,
+}
+
+impl Cargo {
+    /// Returns `Cargo` that will be **compiled** by the passed compiler, for the given
+    /// `target`.
+    pub fn from_build_compiler(build_compiler: Compiler, target: TargetSelection) -> Self {
+        Self { build_compiler, target }
+    }
 }
 
 impl Step for Cargo {
@@ -865,7 +830,10 @@ impl Step for Cargo {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Cargo {
-            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
+            build_compiler: get_tool_target_compiler(
+                run.builder,
+                ToolTargetBuildMode::Build(run.target),
+            ),
             target: run.target,
         });
     }
@@ -873,19 +841,28 @@ impl Step for Cargo {
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.build.require_submodule("src/tools/cargo", None);
 
+        builder.std(self.build_compiler, self.target);
         builder.ensure(ToolBuild {
-            build_compiler: self.compiler,
+            build_compiler: self.build_compiler,
             target: self.target,
             tool: "cargo",
-            mode: Mode::ToolRustc,
+            mode: Mode::ToolTarget,
             path: "src/tools/cargo",
             source_type: SourceType::Submodule,
             extra_features: Vec::new(),
-            allow_features: "",
+            // Cargo is compilable with a stable compiler, but since we run in bootstrap,
+            // with RUSTC_BOOTSTRAP being set, some "clever" build scripts enable specialization
+            // based on this, which breaks stuff. We thus have to explicitly allow these features
+            // here.
+            allow_features: "min_specialization,specialization",
             cargo_args: Vec::new(),
             artifact_kind: ToolArtifactKind::Binary,
         })
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::build("cargo", self.target).built_by(self.build_compiler))
+    }
 }
 
 /// Represents a built LldWrapper, the `lld-wrapper` tool itself, and a directory
@@ -1066,8 +1043,13 @@ impl Step for WasmComponentLd {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RustAnalyzer {
-    pub compiler: Compiler,
-    pub target: TargetSelection,
+    compilers: RustcPrivateCompilers,
+}
+
+impl RustAnalyzer {
+    pub fn from_compilers(compilers: RustcPrivateCompilers) -> Self {
+        Self { compilers }
+    }
 }
 
 impl RustAnalyzer {
@@ -1086,15 +1068,16 @@ impl Step for RustAnalyzer {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(RustAnalyzer {
-            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
-            target: run.target,
+            compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
         });
     }
 
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
+        let build_compiler = self.compilers.build_compiler;
+        let target = self.compilers.target();
         builder.ensure(ToolBuild {
-            build_compiler: self.compiler,
-            target: self.target,
+            build_compiler,
+            target,
             tool: "rust-analyzer",
             mode: Mode::ToolRustc,
             path: "src/tools/rust-analyzer",
@@ -1105,16 +1088,29 @@ impl Step for RustAnalyzer {
             artifact_kind: ToolArtifactKind::Binary,
         })
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::build("rust-analyzer", self.compilers.target())
+                .built_by(self.compilers.build_compiler),
+        )
+    }
 }
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RustAnalyzerProcMacroSrv {
-    pub compiler: Compiler,
-    pub target: TargetSelection,
+    compilers: RustcPrivateCompilers,
+}
+
+impl RustAnalyzerProcMacroSrv {
+    pub fn from_compilers(compilers: RustcPrivateCompilers) -> Self {
+        Self { compilers }
+    }
 }
 
 impl Step for RustAnalyzerProcMacroSrv {
-    type Output = Option<ToolBuildResult>;
+    type Output = ToolBuildResult;
+
     const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
@@ -1131,15 +1127,14 @@ impl Step for RustAnalyzerProcMacroSrv {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(RustAnalyzerProcMacroSrv {
-            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
-            target: run.target,
+            compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> Option<ToolBuildResult> {
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
         let tool_result = builder.ensure(ToolBuild {
-            build_compiler: self.compiler,
-            target: self.target,
+            build_compiler: self.compilers.build_compiler,
+            target: self.compilers.target(),
             tool: "rust-analyzer-proc-macro-srv",
             mode: Mode::ToolRustc,
             path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli",
@@ -1152,7 +1147,7 @@ impl Step for RustAnalyzerProcMacroSrv {
 
         // Copy `rust-analyzer-proc-macro-srv` to `<sysroot>/libexec/`
         // so that r-a can use it.
-        let libexec_path = builder.sysroot(self.compiler).join("libexec");
+        let libexec_path = builder.sysroot(self.compilers.target_compiler).join("libexec");
         t!(fs::create_dir_all(&libexec_path));
         builder.copy_link(
             &tool_result.tool_path,
@@ -1160,7 +1155,14 @@ impl Step for RustAnalyzerProcMacroSrv {
             FileType::Executable,
         );
 
-        Some(tool_result)
+        tool_result
+    }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::build("rust-analyzer-proc-macro-srv", self.compilers.target())
+                .built_by(self.compilers.build_compiler),
+        )
     }
 }
 
@@ -1302,7 +1304,92 @@ impl Step for LibcxxVersionTool {
     }
 }
 
-macro_rules! tool_extended {
+/// Represents which compilers are involved in the compilation of a tool
+/// that depends on compiler internals (`rustc_private`).
+/// Their compilation looks like this:
+///
+/// - `build_compiler` (stage N-1) builds `target_compiler` (stage N) to produce .rlibs
+///     - These .rlibs are copied into the sysroot of `build_compiler`
+/// - `build_compiler` (stage N-1) builds `<tool>` (stage N)
+///     - `<tool>` links to .rlibs from `target_compiler`
+///
+/// Eventually, this could also be used for .rmetas and check builds, but so far we only deal with
+/// normal builds here.
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct RustcPrivateCompilers {
+    /// Compiler that builds the tool and that builds `target_compiler`.
+    build_compiler: Compiler,
+    /// Compiler to which .rlib artifacts the tool links to.
+    /// The host target of this compiler corresponds to the target of the tool.
+    target_compiler: Compiler,
+}
+
+impl RustcPrivateCompilers {
+    /// Create compilers for a `rustc_private` tool with the given `stage` and for the given
+    /// `target`.
+    pub fn new(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self {
+        let build_compiler = Self::build_compiler_from_stage(builder, stage);
+
+        // This is the compiler we'll link to
+        // FIXME: make 100% sure that `target_compiler` was indeed built with `build_compiler`...
+        let target_compiler = builder.compiler(build_compiler.stage + 1, target);
+
+        Self { build_compiler, target_compiler }
+    }
+
+    pub fn from_build_and_target_compiler(
+        build_compiler: Compiler,
+        target_compiler: Compiler,
+    ) -> Self {
+        Self { build_compiler, target_compiler }
+    }
+
+    /// Create rustc tool compilers from the build compiler.
+    pub fn from_build_compiler(
+        builder: &Builder<'_>,
+        build_compiler: Compiler,
+        target: TargetSelection,
+    ) -> Self {
+        let target_compiler = builder.compiler(build_compiler.stage + 1, target);
+        Self { build_compiler, target_compiler }
+    }
+
+    /// Create rustc tool compilers from the target compiler.
+    pub fn from_target_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
+        Self {
+            build_compiler: Self::build_compiler_from_stage(builder, target_compiler.stage),
+            target_compiler,
+        }
+    }
+
+    fn build_compiler_from_stage(builder: &Builder<'_>, stage: u32) -> Compiler {
+        assert!(stage > 0);
+
+        if builder.download_rustc() && stage == 1 {
+            // We shouldn't drop to stage0 compiler when using CI rustc.
+            builder.compiler(1, builder.config.host_target)
+        } else {
+            builder.compiler(stage - 1, builder.config.host_target)
+        }
+    }
+
+    pub fn build_compiler(&self) -> Compiler {
+        self.build_compiler
+    }
+
+    pub fn target_compiler(&self) -> Compiler {
+        self.target_compiler
+    }
+
+    /// Target of the tool being compiled
+    pub fn target(&self) -> TargetSelection {
+        self.target_compiler.host
+    }
+}
+
+/// Creates a step that builds an extended `Mode::ToolRustc` tool
+/// and installs it into the sysroot of a corresponding compiler.
+macro_rules! tool_rustc_extended {
     (
         $name:ident {
             path: $path:expr,
@@ -1316,8 +1403,15 @@ macro_rules! tool_extended {
     ) => {
         #[derive(Debug, Clone, Hash, PartialEq, Eq)]
         pub struct $name {
-            pub compiler: Compiler,
-            pub target: TargetSelection,
+            compilers: RustcPrivateCompilers,
+        }
+
+        impl $name {
+            pub fn from_compilers(compilers: RustcPrivateCompilers) -> Self {
+                Self {
+                    compilers,
+                }
+            }
         }
 
         impl Step for $name {
@@ -1326,7 +1420,7 @@ macro_rules! tool_extended {
             const ONLY_HOSTS: bool = true;
 
             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-                should_run_tool_build_step(
+                should_run_extended_rustc_tool(
                     run,
                     $tool_name,
                     $path,
@@ -1336,17 +1430,15 @@ macro_rules! tool_extended {
 
             fn make_run(run: RunConfig<'_>) {
                 run.builder.ensure($name {
-                    compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
-                    target: run.target,
+                    compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
                 });
             }
 
             fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
-                let Self { compiler, target } = self;
-                run_tool_build_step(
+                let Self { compilers } = self;
+                build_extended_rustc_tool(
                     builder,
-                    compiler,
-                    target,
+                    compilers,
                     $tool_name,
                     $path,
                     None $( .or(Some(&$add_bins_to_sysroot)) )?,
@@ -1356,18 +1448,16 @@ macro_rules! tool_extended {
             }
 
             fn metadata(&self) -> Option<StepMetadata> {
-                // FIXME: refactor extended tool steps to make the build_compiler explicit,
-                // it is offset by one now for rustc tools
                 Some(
-                    StepMetadata::build($tool_name, self.target)
-                        .built_by(self.compiler.with_stage(self.compiler.stage.saturating_sub(1)))
+                    StepMetadata::build($tool_name, self.compilers.target())
+                        .built_by(self.compilers.build_compiler)
                 )
             }
         }
     }
 }
 
-fn should_run_tool_build_step<'a>(
+fn should_run_extended_rustc_tool<'a>(
     run: ShouldRun<'a>,
     tool_name: &'static str,
     path: &'static str,
@@ -1391,39 +1481,38 @@ fn should_run_tool_build_step<'a>(
     )
 }
 
-#[expect(clippy::too_many_arguments)] // silence overeager clippy lint
-fn run_tool_build_step(
+fn build_extended_rustc_tool(
     builder: &Builder<'_>,
-    compiler: Compiler,
-    target: TargetSelection,
+    compilers: RustcPrivateCompilers,
     tool_name: &'static str,
     path: &'static str,
     add_bins_to_sysroot: Option<&[&str]>,
     add_features: Option<fn(&Builder<'_>, TargetSelection, &mut Vec<String>)>,
     cargo_args: Option<&[&'static str]>,
 ) -> ToolBuildResult {
+    let target = compilers.target();
     let mut extra_features = Vec::new();
     if let Some(func) = add_features {
         func(builder, target, &mut extra_features);
     }
 
-    let ToolBuildResult { tool_path, build_compiler, target_compiler } =
-        builder.ensure(ToolBuild {
-            build_compiler: compiler,
-            target,
-            tool: tool_name,
-            mode: Mode::ToolRustc,
-            path,
-            extra_features,
-            source_type: SourceType::InTree,
-            allow_features: "",
-            cargo_args: cargo_args.unwrap_or_default().iter().map(|s| String::from(*s)).collect(),
-            artifact_kind: ToolArtifactKind::Binary,
-        });
-
+    let build_compiler = compilers.build_compiler;
+    let ToolBuildResult { tool_path, .. } = builder.ensure(ToolBuild {
+        build_compiler,
+        target,
+        tool: tool_name,
+        mode: Mode::ToolRustc,
+        path,
+        extra_features,
+        source_type: SourceType::InTree,
+        allow_features: "",
+        cargo_args: cargo_args.unwrap_or_default().iter().map(|s| String::from(*s)).collect(),
+        artifact_kind: ToolArtifactKind::Binary,
+    });
+
+    let target_compiler = compilers.target_compiler;
     if let Some(add_bins_to_sysroot) = add_bins_to_sysroot
         && !add_bins_to_sysroot.is_empty()
-        && target_compiler.stage > 0
     {
         let bindir = builder.sysroot(target_compiler).join("bin");
         t!(fs::create_dir_all(&bindir));
@@ -1435,25 +1524,25 @@ fn run_tool_build_step(
 
         // Return a path into the bin dir.
         let path = bindir.join(exe(tool_name, target_compiler.host));
-        ToolBuildResult { tool_path: path, build_compiler, target_compiler }
+        ToolBuildResult { tool_path: path, build_compiler }
     } else {
-        ToolBuildResult { tool_path, build_compiler, target_compiler }
+        ToolBuildResult { tool_path, build_compiler }
     }
 }
 
-tool_extended!(Cargofmt {
+tool_rustc_extended!(Cargofmt {
     path: "src/tools/rustfmt",
     tool_name: "cargo-fmt",
     stable: true,
     add_bins_to_sysroot: ["cargo-fmt"]
 });
-tool_extended!(CargoClippy {
+tool_rustc_extended!(CargoClippy {
     path: "src/tools/clippy",
     tool_name: "cargo-clippy",
     stable: true,
     add_bins_to_sysroot: ["cargo-clippy"]
 });
-tool_extended!(Clippy {
+tool_rustc_extended!(Clippy {
     path: "src/tools/clippy",
     tool_name: "clippy-driver",
     stable: true,
@@ -1464,7 +1553,7 @@ tool_extended!(Clippy {
         }
     }
 });
-tool_extended!(Miri {
+tool_rustc_extended!(Miri {
     path: "src/tools/miri",
     tool_name: "miri",
     stable: false,
@@ -1472,13 +1561,13 @@ tool_extended!(Miri {
     // Always compile also tests when building miri. Otherwise feature unification can cause rebuilds between building and testing miri.
     cargo_args: &["--all-targets"],
 });
-tool_extended!(CargoMiri {
+tool_rustc_extended!(CargoMiri {
     path: "src/tools/miri/cargo-miri",
     tool_name: "cargo-miri",
     stable: false,
     add_bins_to_sysroot: ["cargo-miri"]
 });
-tool_extended!(Rustfmt {
+tool_rustc_extended!(Rustfmt {
     path: "src/tools/rustfmt",
     tool_name: "rustfmt",
     stable: true,
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index 6b3236ef47e..1d0c9da694f 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -508,7 +508,7 @@ impl Builder<'_> {
                 }
                 _ => panic!("doc mode {mode:?} not expected"),
             };
-            let rustdoc = self.rustdoc(compiler);
+            let rustdoc = self.rustdoc_for_compiler(compiler);
             build_stamp::clear_if_dirty(self, &my_out, &rustdoc);
         }
 
@@ -822,7 +822,7 @@ impl Builder<'_> {
         }
 
         let rustdoc_path = match cmd_kind {
-            Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler),
+            Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc_for_compiler(compiler),
             _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"),
         };
 
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index 20f3fee1c6c..f84565fccfd 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -16,6 +16,7 @@ use tracing::instrument;
 pub use self::cargo::{Cargo, cargo_profile_var};
 pub use crate::Compiler;
 use crate::core::build_steps::compile::{Std, StdLink};
+use crate::core::build_steps::tool::RustcPrivateCompilers;
 use crate::core::build_steps::{
     check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor,
 };
@@ -1543,8 +1544,11 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
             .map(|entry| entry.path())
     }
 
-    pub fn rustdoc(&self, compiler: Compiler) -> PathBuf {
-        self.ensure(tool::Rustdoc { compiler }).tool_path
+    /// Returns a path to `Rustdoc` that "belongs" to the `target_compiler`.
+    /// It can be either a stage0 rustdoc or a locally built rustdoc that *links* to
+    /// `target_compiler`.
+    pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf {
+        self.ensure(tool::Rustdoc { target_compiler })
     }
 
     pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
@@ -1560,10 +1564,13 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
             return cmd;
         }
 
-        let _ =
-            self.ensure(tool::Clippy { compiler: run_compiler, target: self.build.host_target });
-        let cargo_clippy = self
-            .ensure(tool::CargoClippy { compiler: run_compiler, target: self.build.host_target });
+        // FIXME: double check that `run_compiler`'s stage is what we want to use
+        let compilers =
+            RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target);
+        assert_eq!(run_compiler, compilers.target_compiler());
+
+        let _ = self.ensure(tool::Clippy::from_compilers(compilers));
+        let cargo_clippy = self.ensure(tool::CargoClippy::from_compilers(compilers));
         let mut dylib_path = helpers::dylib_path();
         dylib_path.insert(0, self.sysroot(run_compiler).join("lib"));
 
@@ -1575,11 +1582,14 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
 
     pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
         assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0");
+
+        let compilers =
+            RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target);
+        assert_eq!(run_compiler, compilers.target_compiler());
+
         // Prepare the tools
-        let miri =
-            self.ensure(tool::Miri { compiler: run_compiler, target: self.build.host_target });
-        let cargo_miri =
-            self.ensure(tool::CargoMiri { compiler: run_compiler, target: self.build.host_target });
+        let miri = self.ensure(tool::Miri::from_compilers(compilers));
+        let cargo_miri = self.ensure(tool::CargoMiri::from_compilers(compilers));
         // Invoke cargo-miri, make sure it can find miri and cargo.
         let mut cmd = command(cargo_miri.tool_path);
         cmd.env("MIRI", &miri.tool_path);
@@ -1604,7 +1614,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
             // equivalently to rustc.
             .env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler))
             .env("CFG_RELEASE_CHANNEL", &self.config.channel)
-            .env("RUSTDOC_REAL", self.rustdoc(compiler))
+            .env("RUSTDOC_REAL", self.rustdoc_for_compiler(compiler))
             .env("RUSTC_BOOTSTRAP", "1");
 
         cmd.arg("-Wrustdoc::invalid_codeblock_attributes");
@@ -1728,11 +1738,11 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
     /// Ensure that a given step is built *only if it's supposed to be built by default*, returning
     /// its output. This will cache the step, so it's safe (and good!) to call this as often as
     /// needed to ensure that all dependencies are build.
-    pub(crate) fn ensure_if_default<T, S: Step<Output = Option<T>>>(
+    pub(crate) fn ensure_if_default<T, S: Step<Output = T>>(
         &'a self,
         step: S,
         kind: Kind,
-    ) -> S::Output {
+    ) -> Option<S::Output> {
         let desc = StepDescription::from::<S>(kind);
         let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind));
 
@@ -1744,7 +1754,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
         }
 
         // Only execute if it's supposed to run as default
-        if desc.default && should_run.is_really_default() { self.ensure(step) } else { None }
+        if desc.default && should_run.is_really_default() { Some(self.ensure(step)) } else { None }
     }
 
     /// Checks if any of the "should_run" paths is in the `Builder` paths.
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index f012645b7ef..139ddc9ed24 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -279,13 +279,6 @@ mod defaults {
             first(cache.all::<tool::ErrorIndex>()),
             &[tool::ErrorIndex { compiler: Compiler::new(1, a) }]
         );
-        // docs should be built with the stage0 compiler, not with the stage0 artifacts.
-        // recall that rustdoc is off-by-one: `stage` is the compiler rustdoc is _linked_ to,
-        // not the one it was built by.
-        assert_eq!(
-            first(cache.all::<tool::Rustdoc>()),
-            &[tool::Rustdoc { compiler: Compiler::new(1, a) },]
-        );
     }
 }
 
@@ -337,12 +330,6 @@ mod dist {
             first(builder.cache.all::<tool::ErrorIndex>()),
             &[tool::ErrorIndex { compiler: Compiler::new(1, a) }]
         );
-        // This is actually stage 1, but Rustdoc::run swaps out the compiler with
-        // stage minus 1 if --stage is not 0. Very confusing!
-        assert_eq!(
-            first(builder.cache.all::<tool::Rustdoc>()),
-            &[tool::Rustdoc { compiler: Compiler::new(2, a) },]
-        );
     }
 }
 
@@ -569,36 +556,6 @@ fn test_is_builder_target() {
     }
 }
 
-#[test]
-fn test_get_tool_rustc_compiler() {
-    let mut config = configure("build", &[], &[]);
-    config.download_rustc_commit = None;
-    let build = Build::new(config);
-    let builder = Builder::new(&build);
-
-    let target_triple_1 = TargetSelection::from_user(TEST_TRIPLE_1);
-
-    let compiler = Compiler::new(2, target_triple_1);
-    let expected = Compiler::new(1, target_triple_1);
-    let actual = tool::get_tool_rustc_compiler(&builder, compiler);
-    assert_eq!(expected, actual);
-
-    let compiler = Compiler::new(1, target_triple_1);
-    let expected = Compiler::new(0, target_triple_1);
-    let actual = tool::get_tool_rustc_compiler(&builder, compiler);
-    assert_eq!(expected, actual);
-
-    let mut config = configure("build", &[], &[]);
-    config.download_rustc_commit = Some("".to_owned());
-    let build = Build::new(config);
-    let builder = Builder::new(&build);
-
-    let compiler = Compiler::new(1, target_triple_1);
-    let expected = Compiler::new(1, target_triple_1);
-    let actual = tool::get_tool_rustc_compiler(&builder, compiler);
-    assert_eq!(expected, actual);
-}
-
 /// When bootstrap detects a step dependency cycle (which is a bug), its panic
 /// message should show the actual steps on the stack, not just several copies
 /// of `Any { .. }`.
@@ -657,7 +614,7 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
-        [build] rustdoc 0 <host>
+        [build] rustdoc 1 <host>
         ");
     }
 
@@ -680,10 +637,10 @@ mod snapshot {
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 1 <host> -> std 1 <target1>
         [build] rustc 2 <host> -> std 2 <target1>
-        [build] rustdoc 1 <host>
+        [build] rustdoc 2 <host>
         [build] llvm <target1>
         [build] rustc 1 <host> -> rustc 2 <target1>
-        [build] rustdoc 1 <target1>
+        [build] rustdoc 2 <target1>
         ");
     }
 
@@ -763,6 +720,23 @@ mod snapshot {
     }
 
     #[test]
+    fn build_compiler_codegen_backend() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx
+                .config("build")
+                .args(&["--set", "rust.codegen-backends=['llvm', 'cranelift']"])
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 0 <host> -> rustc_codegen_cranelift 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustdoc 1 <host>
+        "
+        );
+    }
+
+    #[test]
     fn build_compiler_tools() {
         let ctx = TestCtx::new();
         insta::assert_snapshot!(
@@ -780,7 +754,7 @@ mod snapshot {
         [build] rustc 1 <host> -> LldWrapper 2 <host>
         [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
         [build] rustc 2 <host> -> std 2 <host>
-        [build] rustdoc 1 <host>
+        [build] rustdoc 2 <host>
         "
         );
     }
@@ -809,7 +783,7 @@ mod snapshot {
         [build] rustc 1 <host> -> rustc 2 <target1>
         [build] rustc 1 <host> -> LldWrapper 2 <target1>
         [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <target1>
-        [build] rustdoc 1 <target1>
+        [build] rustdoc 2 <target1>
         "
         );
     }
@@ -998,7 +972,7 @@ mod snapshot {
             .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustdoc 0 <host>
+        [build] rustdoc 1 <host>
         [doc] std 1 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         ");
     }
@@ -1027,6 +1001,15 @@ mod snapshot {
     }
 
     #[test]
+    fn build_cargo() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("build")
+                .paths(&["cargo"])
+            .render_steps(), @"[build] rustc 0 <host> -> cargo 1 <host>");
+    }
+
+    #[test]
     fn dist_default_stage() {
         let ctx = TestCtx::new();
         assert_eq!(ctx.config("dist").path("compiler").create_config().stage, 2);
@@ -1047,7 +1030,7 @@ mod snapshot {
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustdoc 1 <host>
+        [build] rustdoc 2 <host>
         [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> LintDocs 1 <host>
@@ -1089,7 +1072,7 @@ mod snapshot {
         [build] rustc 1 <host> -> LldWrapper 2 <host>
         [build] rustc 1 <host> -> WasmComponentLd 2 <host>
         [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
-        [build] rustdoc 1 <host>
+        [build] rustdoc 2 <host>
         [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> LintDocs 1 <host>
@@ -1097,16 +1080,19 @@ mod snapshot {
         [dist] docs <host>
         [doc] std 2 <host> crates=[]
         [dist] mingw <host>
+        [build] rustc 1 <host> -> rust-analyzer-proc-macro-srv 2 <host>
         [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <host>
         [dist] rustc 1 <host> -> std 1 <host>
         [dist] src <>
-        [build] rustc 0 <host> -> rustfmt 1 <host>
-        [build] rustc 0 <host> -> cargo-fmt 1 <host>
-        [build] rustc 0 <host> -> clippy-driver 1 <host>
-        [build] rustc 0 <host> -> cargo-clippy 1 <host>
-        [build] rustc 0 <host> -> miri 1 <host>
-        [build] rustc 0 <host> -> cargo-miri 1 <host>
+        [build] rustc 1 <host> -> cargo 2 <host>
+        [build] rustc 1 <host> -> rust-analyzer 2 <host>
+        [build] rustc 1 <host> -> rustfmt 2 <host>
+        [build] rustc 1 <host> -> cargo-fmt 2 <host>
+        [build] rustc 1 <host> -> clippy-driver 2 <host>
+        [build] rustc 1 <host> -> cargo-clippy 2 <host>
+        [build] rustc 1 <host> -> miri 2 <host>
+        [build] rustc 1 <host> -> cargo-miri 2 <host>
         ");
     }
 
@@ -1125,7 +1111,7 @@ mod snapshot {
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustdoc 1 <host>
+        [build] rustdoc 2 <host>
         [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         [doc] std 2 <target1> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
@@ -1162,7 +1148,7 @@ mod snapshot {
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustdoc 1 <host>
+        [build] rustdoc 2 <host>
         [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> LintDocs 1 <host>
@@ -1176,7 +1162,7 @@ mod snapshot {
         [dist] rustc <host>
         [build] llvm <target1>
         [build] rustc 1 <host> -> rustc 2 <target1>
-        [build] rustdoc 1 <target1>
+        [build] rustdoc 2 <target1>
         [dist] rustc <target1>
         [dist] rustc 1 <host> -> std 1 <host>
         [dist] src <>
@@ -1199,7 +1185,7 @@ mod snapshot {
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustdoc 1 <host>
+        [build] rustdoc 2 <host>
         [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         [doc] std 2 <target1> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
@@ -1217,7 +1203,7 @@ mod snapshot {
         [dist] rustc <host>
         [build] llvm <target1>
         [build] rustc 1 <host> -> rustc 2 <target1>
-        [build] rustdoc 1 <target1>
+        [build] rustdoc 2 <target1>
         [dist] rustc <target1>
         [dist] rustc 1 <host> -> std 1 <host>
         [dist] rustc 1 <host> -> std 1 <target1>
@@ -1241,7 +1227,7 @@ mod snapshot {
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustdoc 1 <host>
+        [build] rustdoc 2 <host>
         [doc] std 2 <target1> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> RustInstaller 1 <host>
@@ -1273,7 +1259,7 @@ mod snapshot {
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustc 1 <host> -> WasmComponentLd 2 <host>
-        [build] rustdoc 1 <host>
+        [build] rustdoc 2 <host>
         [doc] std 2 <target1> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 1 <host> -> std 1 <target1>
@@ -1286,17 +1272,20 @@ mod snapshot {
         [build] llvm <target1>
         [build] rustc 1 <host> -> rustc 2 <target1>
         [build] rustc 1 <host> -> WasmComponentLd 2 <target1>
-        [build] rustdoc 1 <target1>
+        [build] rustdoc 2 <target1>
+        [build] rustc 1 <host> -> rust-analyzer-proc-macro-srv 2 <target1>
         [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <target1>
         [dist] rustc 1 <host> -> std 1 <target1>
         [dist] src <>
-        [build] rustc 0 <host> -> rustfmt 1 <target1>
-        [build] rustc 0 <host> -> cargo-fmt 1 <target1>
-        [build] rustc 0 <host> -> clippy-driver 1 <target1>
-        [build] rustc 0 <host> -> cargo-clippy 1 <target1>
-        [build] rustc 0 <host> -> miri 1 <target1>
-        [build] rustc 0 <host> -> cargo-miri 1 <target1>
+        [build] rustc 1 <host> -> cargo 2 <target1>
+        [build] rustc 1 <host> -> rust-analyzer 2 <target1>
+        [build] rustc 1 <host> -> rustfmt 2 <target1>
+        [build] rustc 1 <host> -> cargo-fmt 2 <target1>
+        [build] rustc 1 <host> -> clippy-driver 2 <target1>
+        [build] rustc 1 <host> -> cargo-clippy 2 <target1>
+        [build] rustc 1 <host> -> miri 2 <target1>
+        [build] rustc 1 <host> -> cargo-miri 2 <target1>
         [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <target1>
         ");
     }
@@ -1611,6 +1600,58 @@ mod snapshot {
     }
 
     #[test]
+    fn test_cargo_stage_1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("test")
+                .path("cargo")
+                .render_steps(), @r"
+        [build] rustc 0 <host> -> cargo 1 <host>
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustdoc 1 <host>
+        [build] rustdoc 0 <host>
+        ");
+    }
+
+    #[test]
+    fn test_cargo_stage_2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("test")
+                .path("cargo")
+                .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> -> cargo 2 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 2 <host> -> std 2 <host>
+        [build] rustdoc 2 <host>
+        [build] rustdoc 1 <host>
+        ");
+    }
+
+    #[test]
+    fn test_cargotest() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("test")
+                .path("cargotest")
+                .render_steps(), @r"
+        [build] rustc 0 <host> -> cargo 1 <host>
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 0 <host> -> CargoTest 1 <host>
+        [build] rustdoc 1 <host>
+        [test] cargotest 1 <host>
+        ");
+    }
+
+    #[test]
     fn doc_library() {
         let ctx = TestCtx::new();
         insta::assert_snapshot!(
@@ -1619,7 +1660,7 @@ mod snapshot {
                 .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustdoc 0 <host>
+        [build] rustdoc 1 <host>
         [doc] std 1 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind]
         ");
     }
@@ -1633,7 +1674,7 @@ mod snapshot {
                 .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustdoc 0 <host>
+        [build] rustdoc 1 <host>
         [doc] std 1 <host> crates=[core]
         ");
     }
@@ -1648,7 +1689,7 @@ mod snapshot {
                 .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustdoc 0 <host>
+        [build] rustdoc 1 <host>
         [doc] std 1 <host> crates=[core]
         ");
     }
@@ -1678,7 +1719,7 @@ mod snapshot {
                 .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustdoc 0 <host>
+        [build] rustdoc 1 <host>
         [doc] std 1 <host> crates=[alloc,core]
         ");
     }
@@ -1694,10 +1735,105 @@ mod snapshot {
                 .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustdoc 0 <host>
+        [build] rustdoc 1 <host>
         [doc] std 1 <target1> crates=[alloc,core]
         ");
     }
+
+    #[test]
+    fn doc_compiler_stage_0() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("compiler")
+                .stage(0)
+                .render_steps(), @r"
+        [build] rustdoc 0 <host>
+        [build] llvm <host>
+        [doc] rustc 0 <host>
+        ");
+    }
+
+    #[test]
+    fn doc_compiler_stage_1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("compiler")
+                .stage(1)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustdoc 1 <host>
+        [doc] rustc 1 <host>
+        ");
+    }
+
+    #[test]
+    fn doc_compiler_stage_2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .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 2 <host> -> std 2 <host>
+        [build] rustdoc 2 <host>
+        [doc] rustc 2 <host>
+        ");
+    }
+
+    #[test]
+    fn doc_compiletest_stage_0() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("src/tools/compiletest")
+                .stage(0)
+                .render_steps(), @r"
+        [build] rustdoc 0 <host>
+        [doc] Compiletest <host>
+        ");
+    }
+
+    #[test]
+    fn doc_compiletest_stage_1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("src/tools/compiletest")
+                .stage(1)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustdoc 1 <host>
+        [doc] Compiletest <host>
+        ");
+    }
+
+    #[test]
+    fn doc_compiletest_stage_2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("src/tools/compiletest")
+                .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 2 <host> -> std 2 <host>
+        [build] rustdoc 2 <host>
+        [doc] Compiletest <host>
+        ");
+    }
 }
 
 struct ExecutedSteps {
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index e49513a2116..42192f1d721 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -1105,17 +1105,6 @@ impl Build {
         self.msg(Kind::Doc, compiler.stage, what, compiler.host, target.into())
     }
 
-    #[must_use = "Groups should not be dropped until the Step finishes running"]
-    #[track_caller]
-    fn msg_build(
-        &self,
-        compiler: Compiler,
-        what: impl Display,
-        target: impl Into<Option<TargetSelection>>,
-    ) -> Option<gha::Group> {
-        self.msg(Kind::Build, compiler.stage, what, compiler.host, target)
-    }
-
     /// Return a `Group` guard for a [`Step`] that is built for each `--stage`.
     ///
     /// [`Step`]: crate::core::builder::Step
@@ -1162,20 +1151,21 @@ impl Build {
 
     #[must_use = "Groups should not be dropped until the Step finishes running"]
     #[track_caller]
-    fn msg_sysroot_tool(
+    fn msg_rustc_tool(
         &self,
         action: impl Into<Kind>,
-        stage: u32,
+        build_stage: u32,
         what: impl Display,
         host: TargetSelection,
         target: TargetSelection,
     ) -> Option<gha::Group> {
         let action = action.into().description();
         let msg = |fmt| format!("{action} {what} {fmt}");
+
         let msg = if host == target {
-            msg(format_args!("(stage{stage} -> stage{}, {target})", stage + 1))
+            msg(format_args!("(stage{build_stage} -> stage{}, {target})", build_stage + 1))
         } else {
-            msg(format_args!("(stage{stage}:{host} -> stage{}:{target})", stage + 1))
+            msg(format_args!("(stage{build_stage}:{host} -> stage{}:{target})", build_stage + 1))
         };
         self.group(&msg)
     }
diff --git a/src/ci/docker/scripts/build-clang.sh b/src/ci/docker/scripts/build-clang.sh
index 536991cc06b..905c4077304 100755
--- a/src/ci/docker/scripts/build-clang.sh
+++ b/src/ci/docker/scripts/build-clang.sh
@@ -5,7 +5,7 @@ set -ex
 source shared.sh
 
 # Try to keep the LLVM version here in sync with src/ci/scripts/install-clang.sh
-LLVM=llvmorg-20.1.0-rc2
+LLVM=llvmorg-21.1.0-rc2
 
 mkdir llvm-project
 cd llvm-project
@@ -44,8 +44,10 @@ hide_output \
       -DLLVM_INCLUDE_BENCHMARKS=OFF \
       -DLLVM_INCLUDE_TESTS=OFF \
       -DLLVM_INCLUDE_EXAMPLES=OFF \
-      -DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt;bolt" \
+      -DLLVM_ENABLE_PROJECTS="clang;lld;bolt" \
+      -DLLVM_ENABLE_RUNTIMES="compiler-rt" \
       -DLLVM_BINUTILS_INCDIR="/rustroot/lib/gcc/$GCC_PLUGIN_TARGET/$GCC_VERSION/plugin/include/" \
+      -DRUNTIMES_CMAKE_ARGS="-DCMAKE_CXX_FLAGS=\"--gcc-toolchain=/rustroot\"" \
       -DC_INCLUDE_DIRS="$INC"
 
 hide_output make -j$(nproc)
diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md
index d0cee54adb6..c26d34273d7 100644
--- a/src/doc/rustc-dev-guide/src/stability.md
+++ b/src/doc/rustc-dev-guide/src/stability.md
@@ -181,4 +181,7 @@ the `deprecated_in_future` lint is triggered which is default `allow`, but most
 of the standard library raises it to a warning with
 `#![warn(deprecated_in_future)]`.
 
+## unstable_feature_bound
+The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. In std/core, an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate. Currently, only `impl`s and free functions can be annotated with `#[unstable_feature_bound]`.
+
 [blog]: https://www.ralfj.de/blog/2018/07/19/const.html
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 99b3da8b2cd..c48863b4681 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1838,6 +1838,10 @@ instead, we check that it's not a "finger" cursor.
 	border-right: 3px solid var(--target-border-color);
 }
 
+a.tooltip {
+	font-family: var(--font-family);
+}
+
 .code-header a.tooltip {
 	color: inherit;
 	margin-right: 15px;
diff --git a/src/llvm-project b/src/llvm-project
-Subproject e8a2ffcf322f45b8dce82c65ab27a3e2430a6b5
+Subproject d35840afa50d2615835d6a836f1967c57008188
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 35670ba89e9..84c6df5c744 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -356,7 +356,7 @@ impl<'test> TestCx<'test> {
             if proc_res.status.success() {
                 let err = &format!("{} test did not emit an error", self.config.mode);
                 let extra_note = (self.config.mode == crate::common::TestMode::Ui)
-                    .then_some("note: by default, ui tests are expected not to compile");
+                    .then_some("note: by default, ui tests are expected not to compile.\nhint: use check-pass, build-pass, or run-pass directive to change this behavior.");
                 self.fatal_proc_rec_general(err, extra_note, proc_res, || ());
             }
 
diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml
index 7fc70e0675d..423a273fde7 100644
--- a/tests/rustdoc-gui/notable-trait.goml
+++ b/tests/rustdoc-gui/notable-trait.goml
@@ -8,10 +8,10 @@ define-function: (
     [x, i_x],
     block {
         // Checking they have the same y position.
-        compare-elements-position: (
+        compare-elements-position-near: (
             "//*[@id='method.create_an_iterator_from_read']//a[normalize-space()='NotableStructWithLongName']",
             "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-            ["y"],
+            {"y": 1},
         )
         // Checking they don't have the same x position.
         compare-elements-position-false: (
diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs
index e8afe8b5687..623f5b33e9b 100644
--- a/tests/rustdoc-gui/src/test_docs/lib.rs
+++ b/tests/rustdoc-gui/src/test_docs/lib.rs
@@ -767,3 +767,17 @@ pub mod impls_indent {
         pub fn bar() {}
     }
 }
+
+pub mod tooltips {
+    pub struct X;
+
+    impl X {
+        pub fn bar() -> Vec<u8> {
+            Vec::new()
+        }
+    }
+
+    pub fn bar() -> Vec<u8> {
+        Vec::new()
+    }
+}
diff --git a/tests/rustdoc-gui/tooltips.goml b/tests/rustdoc-gui/tooltips.goml
new file mode 100644
index 00000000000..6e79196be27
--- /dev/null
+++ b/tests/rustdoc-gui/tooltips.goml
@@ -0,0 +1,15 @@
+// This test checks that the right font is applied to the `i` tooltip element.
+
+define-function: (
+    "check-font",
+    [path],
+    block {
+        go-to: "file://" + |DOC_PATH| + "/test_docs/" + |path|
+        assert-css: (
+            "a.tooltip", {"font-family": '"Source Serif 4", NanumBarunGothic, serif'}, ALL,
+        )
+    }
+)
+
+call-function: ("check-font", {"path": "tooltips/fn.bar.html"})
+call-function: ("check-font", {"path": "tooltips/struct.X.html"})
diff --git a/tests/ui/issues/issue-5754.rs b/tests/ui/abi/extern-c-two-doubles-x86_64-5754.rs
index 0aa09882959..7f44ef9c685 100644
--- a/tests/ui/issues/issue-5754.rs
+++ b/tests/ui/abi/extern-c-two-doubles-x86_64-5754.rs
@@ -1,8 +1,8 @@
+// https://github.com/rust-lang/rust/issues/5754
 //@ build-pass
 #![allow(dead_code)]
 #![allow(improper_ctypes)]
 
-
 struct TwoDoubles {
     r: f64,
     i: f64
diff --git a/tests/ui/issues/issue-8898.rs b/tests/ui/array-slice-vec/fixed-size-arrays-zero-size-types-8898.rs
index 4447704f059..fe627b00f53 100644
--- a/tests/ui/issues/issue-8898.rs
+++ b/tests/ui/array-slice-vec/fixed-size-arrays-zero-size-types-8898.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/8898
 //@ run-pass
 
 fn assert_repr_eq<T: std::fmt::Debug>(obj : T, expected : String) {
diff --git a/tests/ui/issues/issue-43483.rs b/tests/ui/associated-consts/trait-associated-const-usage-43483.rs
index 2c62671d0c7..c24157d45e7 100644
--- a/tests/ui/issues/issue-43483.rs
+++ b/tests/ui/associated-consts/trait-associated-const-usage-43483.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/43483
 //@ check-pass
 #![allow(dead_code)]
 #![allow(unused_variables)]
diff --git a/tests/ui/issues/issue-21946.rs b/tests/ui/associated-types/recursive-associated-type-overflow-21946.rs
index d0c052cb2fd..ba84ea11300 100644
--- a/tests/ui/issues/issue-21946.rs
+++ b/tests/ui/associated-types/recursive-associated-type-overflow-21946.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/21946
 trait Foo {
     type A;
 }
diff --git a/tests/ui/issues/issue-21946.stderr b/tests/ui/associated-types/recursive-associated-type-overflow-21946.stderr
index d1b4a808d2e..37fb0ed7eb8 100644
--- a/tests/ui/issues/issue-21946.stderr
+++ b/tests/ui/associated-types/recursive-associated-type-overflow-21946.stderr
@@ -1,5 +1,5 @@
 error[E0275]: overflow evaluating the requirement `<FooStruct as Foo>::A == _`
-  --> $DIR/issue-21946.rs:8:14
+  --> $DIR/recursive-associated-type-overflow-21946.rs:9:14
    |
 LL |     type A = <FooStruct as Foo>::A;
    |              ^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/issues/issue-7061.rs b/tests/ui/borrowck/mismatched-pointer-type-in-self-7061.rs
index c5d5a9d9498..521a5ee9c37 100644
--- a/tests/ui/issues/issue-7061.rs
+++ b/tests/ui/borrowck/mismatched-pointer-type-in-self-7061.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/7061
 //@ dont-require-annotations: NOTE
 
 struct BarStruct;
diff --git a/tests/ui/issues/issue-7061.stderr b/tests/ui/borrowck/mismatched-pointer-type-in-self-7061.stderr
index b4c0ebfbdd5..39b9072dc30 100644
--- a/tests/ui/issues/issue-7061.stderr
+++ b/tests/ui/borrowck/mismatched-pointer-type-in-self-7061.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/issue-7061.rs:6:46
+  --> $DIR/mismatched-pointer-type-in-self-7061.rs:7:46
    |
 LL |     fn foo(&'a mut self) -> Box<BarStruct> { self }
    |                             --------------   ^^^^ expected `Box<BarStruct>`, found `&mut BarStruct`
diff --git a/tests/ui/issues/issue-13665.rs b/tests/ui/borrowck/region-checker-map-closure-13665.rs
index e1d8be16f45..72efa42fe38 100644
--- a/tests/ui/issues/issue-13665.rs
+++ b/tests/ui/borrowck/region-checker-map-closure-13665.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/13665
 //@ run-pass
 
 fn foo<'r>() {
diff --git a/tests/ui/issues/issue-21600.rs b/tests/ui/closures/aliasability-violation-with-closure-21600.rs
index 2e22e5e6fa2..d4c658319ab 100644
--- a/tests/ui/issues/issue-21600.rs
+++ b/tests/ui/closures/aliasability-violation-with-closure-21600.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/21600
 fn call_it<F>(f: F) where F: Fn() { f(); }
 
 struct A;
diff --git a/tests/ui/issues/issue-21600.stderr b/tests/ui/closures/aliasability-violation-with-closure-21600.stderr
index f7905934424..2d2397a2141 100644
--- a/tests/ui/issues/issue-21600.stderr
+++ b/tests/ui/closures/aliasability-violation-with-closure-21600.stderr
@@ -1,5 +1,5 @@
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
-  --> $DIR/issue-21600.rs:14:20
+  --> $DIR/aliasability-violation-with-closure-21600.rs:15:20
    |
 LL | fn call_it<F>(f: F) where F: Fn() { f(); }
    |                  - change this to accept `FnMut` instead of `Fn`
@@ -11,7 +11,7 @@ LL |         call_it(|| x.gen_mut());
    |         expects `Fn` instead of `FnMut`
 
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
-  --> $DIR/issue-21600.rs:14:17
+  --> $DIR/aliasability-violation-with-closure-21600.rs:15:17
    |
 LL | fn call_it<F>(f: F) where F: Fn() { f(); }
    |                  - change this to accept `FnMut` instead of `Fn`
diff --git a/tests/ui/issues/issue-32086.rs b/tests/ui/consts/const-pattern-rewrite-error-32086.rs
index d595d1dd7e6..d35dfe57687 100644
--- a/tests/ui/issues/issue-32086.rs
+++ b/tests/ui/consts/const-pattern-rewrite-error-32086.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/32086
 struct S(u8);
 const C: S = S(10);
 
diff --git a/tests/ui/issues/issue-32086.stderr b/tests/ui/consts/const-pattern-rewrite-error-32086.stderr
index e566dea8908..47616b06632 100644
--- a/tests/ui/issues/issue-32086.stderr
+++ b/tests/ui/consts/const-pattern-rewrite-error-32086.stderr
@@ -1,5 +1,5 @@
 error[E0532]: expected tuple struct or tuple variant, found constant `C`
-  --> $DIR/issue-32086.rs:5:9
+  --> $DIR/const-pattern-rewrite-error-32086.rs:6:9
    |
 LL | struct S(u8);
    | ------------- similarly named tuple struct `S` defined here
@@ -8,7 +8,7 @@ LL |     let C(a) = S(11);
    |         ^ help: a tuple struct with a similar name exists: `S`
 
 error[E0532]: expected tuple struct or tuple variant, found constant `C`
-  --> $DIR/issue-32086.rs:6:9
+  --> $DIR/const-pattern-rewrite-error-32086.rs:7:9
    |
 LL | struct S(u8);
    | ------------- similarly named tuple struct `S` defined here
diff --git a/tests/ui/issues/auxiliary/issue-5521.rs b/tests/ui/cross-crate/auxiliary/aux-5521.rs
index c2f81779b35..c2f81779b35 100644
--- a/tests/ui/issues/auxiliary/issue-5521.rs
+++ b/tests/ui/cross-crate/auxiliary/aux-5521.rs
diff --git a/tests/ui/issues/issue-5521.rs b/tests/ui/cross-crate/cross-crate-map-usage-5521.rs
index 45896ae8128..ffce846be2c 100644
--- a/tests/ui/issues/issue-5521.rs
+++ b/tests/ui/cross-crate/cross-crate-map-usage-5521.rs
@@ -1,10 +1,9 @@
+// https://github.com/rust-lang/rust/issues/5521
 //@ run-pass
 #![allow(dead_code)]
-//@ aux-build:issue-5521.rs
+//@ aux-build:aux-5521.rs
 
-
-
-extern crate issue_5521 as foo;
+extern crate aux_5521 as foo;
 
 fn bar(a: foo::map) {
     if false {
diff --git a/tests/ui/issues/issue-46332.rs b/tests/ui/did_you_mean/typo-suggestion-improvement-46332.rs
index bed74e3138a..3dd686a66b0 100644
--- a/tests/ui/issues/issue-46332.rs
+++ b/tests/ui/did_you_mean/typo-suggestion-improvement-46332.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/46332
 // Original Levenshtein distance for both of this is 1. We improved accuracy with
 // additional case insensitive comparison.
 
diff --git a/tests/ui/issues/issue-46332.stderr b/tests/ui/did_you_mean/typo-suggestion-improvement-46332.stderr
index 8c0c1dfa6ee..502f8151878 100644
--- a/tests/ui/issues/issue-46332.stderr
+++ b/tests/ui/did_you_mean/typo-suggestion-improvement-46332.stderr
@@ -1,5 +1,5 @@
 error[E0422]: cannot find struct, variant or union type `TyUInt` in this scope
-  --> $DIR/issue-46332.rs:9:5
+  --> $DIR/typo-suggestion-improvement-46332.rs:10:5
    |
 LL | struct TyUint {}
    | ------------- similarly named struct `TyUint` defined here
diff --git a/tests/ui/issues/issue-6892.rs b/tests/ui/drop/destructor-run-for-let-ignore-6892.rs
index 7d99aef4ac5..0fcf133c2b1 100644
--- a/tests/ui/issues/issue-6892.rs
+++ b/tests/ui/drop/destructor-run-for-let-ignore-6892.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/6892
 //@ run-pass
 #![allow(dead_code)]
 // Ensures that destructors are run for expressions of the form "let _ = e;"
diff --git a/tests/ui/issues/issue-16151.rs b/tests/ui/drop/drop-count-assertion-16151.rs
index b18108e0a8a..ede6bc23e73 100644
--- a/tests/ui/issues/issue-16151.rs
+++ b/tests/ui/drop/drop-count-assertion-16151.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/16151
 //@ run-pass
 
 // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
diff --git a/tests/ui/issues/issue-20939.rs b/tests/ui/dyn-compatibility/dyn-compatible-trait-implementation-20939.rs
index c0c22297897..c7938b275e9 100644
--- a/tests/ui/issues/issue-20939.rs
+++ b/tests/ui/dyn-compatibility/dyn-compatible-trait-implementation-20939.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/20939
 trait Foo {}
 
 impl<'a> Foo for dyn Foo + 'a {}
diff --git a/tests/ui/issues/issue-20939.stderr b/tests/ui/dyn-compatibility/dyn-compatible-trait-implementation-20939.stderr
index 00357155c8a..196d8b6a880 100644
--- a/tests/ui/issues/issue-20939.stderr
+++ b/tests/ui/dyn-compatibility/dyn-compatible-trait-implementation-20939.stderr
@@ -1,5 +1,5 @@
 error[E0371]: the object type `(dyn Foo + 'a)` automatically implements the trait `Foo`
-  --> $DIR/issue-20939.rs:3:1
+  --> $DIR/dyn-compatible-trait-implementation-20939.rs:4:1
    |
 LL | impl<'a> Foo for dyn Foo + 'a {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Foo + 'a)` automatically implements trait `Foo`
diff --git a/tests/ui/issues/issue-36075.rs b/tests/ui/generics/unused-type-parameter-regression-36075.rs
index a563332ad78..4d6eb617b59 100644
--- a/tests/ui/issues/issue-36075.rs
+++ b/tests/ui/generics/unused-type-parameter-regression-36075.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/36075
 //@ check-pass
 #![allow(dead_code)]
 trait DeclarationParser {
diff --git a/tests/ui/issues/issue-9188.rs b/tests/ui/issues/issue-9188.rs
deleted file mode 100644
index df2f90a0f16..00000000000
--- a/tests/ui/issues/issue-9188.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//@ run-pass
-//@ aux-build:issue-9188.rs
-
-
-extern crate issue_9188;
-
-pub fn main() {
-    let a = issue_9188::bar();
-    let b = issue_9188::foo::<isize>();
-    assert_eq!(*a, *b);
-}
diff --git a/tests/ui/issues/issue-23966.rs b/tests/ui/iterators/fold-iterator-error-23966.rs
index 5fdec28ac2c..970c943da0b 100644
--- a/tests/ui/issues/issue-23966.rs
+++ b/tests/ui/iterators/fold-iterator-error-23966.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/23966
 fn main() {
     "".chars().fold(|_, _| (), ());
     //~^ ERROR E0277
diff --git a/tests/ui/issues/issue-23966.stderr b/tests/ui/iterators/fold-iterator-error-23966.stderr
index 3f7a4fa312f..15249a93597 100644
--- a/tests/ui/issues/issue-23966.stderr
+++ b/tests/ui/iterators/fold-iterator-error-23966.stderr
@@ -1,5 +1,5 @@
 error[E0277]: expected a `FnMut(_, char)` closure, found `()`
-  --> $DIR/issue-23966.rs:2:32
+  --> $DIR/fold-iterator-error-23966.rs:3:32
    |
 LL |     "".chars().fold(|_, _| (), ());
    |                ----            ^^ expected an `FnMut(_, char)` closure, found `()`
diff --git a/tests/ui/issues/issue-15735.rs b/tests/ui/lifetimes/lifetime-inference-miss-15735.rs
index f5b3803f155..c75d59a2f4b 100644
--- a/tests/ui/issues/issue-15735.rs
+++ b/tests/ui/lifetimes/lifetime-inference-miss-15735.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/15735
 //@ check-pass
 #![allow(dead_code)]
 struct A<'a> {
diff --git a/tests/ui/issues/issue-75704.rs b/tests/ui/loops/infinite-loop-simplify-cfg-75704.rs
index 1672bf0b4c3..8bc5fba7a68 100644
--- a/tests/ui/issues/issue-75704.rs
+++ b/tests/ui/loops/infinite-loop-simplify-cfg-75704.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/75704
 // Caused an infinite loop during SimlifyCfg MIR transform previously.
 //
 //@ build-pass
diff --git a/tests/ui/issues/issue-19100.fixed b/tests/ui/match/unreachable-pattern-if-variant-not-imported-19100.fixed
index 1162490048c..40c92322519 100644
--- a/tests/ui/issues/issue-19100.fixed
+++ b/tests/ui/match/unreachable-pattern-if-variant-not-imported-19100.fixed
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/19100
 //@ run-rustfix
 
 #![allow(non_snake_case)]
diff --git a/tests/ui/issues/issue-19100.rs b/tests/ui/match/unreachable-pattern-if-variant-not-imported-19100.rs
index fefed0daa72..9ef6fd77b50 100644
--- a/tests/ui/issues/issue-19100.rs
+++ b/tests/ui/match/unreachable-pattern-if-variant-not-imported-19100.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/19100
 //@ run-rustfix
 
 #![allow(non_snake_case)]
diff --git a/tests/ui/issues/issue-19100.stderr b/tests/ui/match/unreachable-pattern-if-variant-not-imported-19100.stderr
index ebbf083b7de..7356a4a003b 100644
--- a/tests/ui/issues/issue-19100.stderr
+++ b/tests/ui/match/unreachable-pattern-if-variant-not-imported-19100.stderr
@@ -1,5 +1,5 @@
 error[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
-  --> $DIR/issue-19100.rs:17:1
+  --> $DIR/unreachable-pattern-if-variant-not-imported-19100.rs:18:1
    |
 LL | Bar if true
    | ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
@@ -7,7 +7,7 @@ LL | Bar if true
    = note: `#[deny(bindings_with_variant_name)]` on by default
 
 error[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
-  --> $DIR/issue-19100.rs:21:1
+  --> $DIR/unreachable-pattern-if-variant-not-imported-19100.rs:22:1
    |
 LL | Baz if false
    | ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
diff --git a/tests/ui/issues/issue-22684.rs b/tests/ui/methods/trait-method-resolution-over-inherent-22684.rs
index a5b042706ed..9f317b576e6 100644
--- a/tests/ui/issues/issue-22684.rs
+++ b/tests/ui/methods/trait-method-resolution-over-inherent-22684.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/22684
 mod foo {
     pub struct Foo;
     impl Foo {
diff --git a/tests/ui/issues/issue-22684.stderr b/tests/ui/methods/trait-method-resolution-over-inherent-22684.stderr
index e2ca54caeac..0ab7701fa9e 100644
--- a/tests/ui/issues/issue-22684.stderr
+++ b/tests/ui/methods/trait-method-resolution-over-inherent-22684.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/issue-22684.rs:17:17
+  --> $DIR/trait-method-resolution-over-inherent-22684.rs:18:17
    |
 LL |     let _: () = foo::Foo.bar();
    |            --   ^^^^^^^^^^^^^^ expected `()`, found `bool`
diff --git a/tests/ui/issues/issue-24352.rs b/tests/ui/mismatched_types/float-integer-subtraction-error-24352.rs
index 5c8246d179f..eb632398c62 100644
--- a/tests/ui/issues/issue-24352.rs
+++ b/tests/ui/mismatched_types/float-integer-subtraction-error-24352.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/24352
 fn main() {
     1.0f64 - 1.0;
     1.0f64 - 1 //~ ERROR E0277
diff --git a/tests/ui/issues/issue-24352.stderr b/tests/ui/mismatched_types/float-integer-subtraction-error-24352.stderr
index 3e0f812b5c7..7dc1fa777fc 100644
--- a/tests/ui/issues/issue-24352.stderr
+++ b/tests/ui/mismatched_types/float-integer-subtraction-error-24352.stderr
@@ -1,5 +1,5 @@
 error[E0277]: cannot subtract `{integer}` from `f64`
-  --> $DIR/issue-24352.rs:3:12
+  --> $DIR/float-integer-subtraction-error-24352.rs:4:12
    |
 LL |     1.0f64 - 1
    |            ^ no implementation for `f64 - {integer}`
diff --git a/tests/ui/issues/issue-50585.rs b/tests/ui/mismatched_types/for-loop-in-vec-type-mismatchrs-50585.rs
index ca2ece8d53b..4abef0bee81 100644
--- a/tests/ui/issues/issue-50585.rs
+++ b/tests/ui/mismatched_types/for-loop-in-vec-type-mismatchrs-50585.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/50585
 fn main() {
     |y: Vec<[(); for x in 0..2 {}]>| {};
     //~^ ERROR mismatched types
diff --git a/tests/ui/issues/issue-50585.stderr b/tests/ui/mismatched_types/for-loop-in-vec-type-mismatchrs-50585.stderr
index 7e83ea35fbb..d60d97a02ab 100644
--- a/tests/ui/issues/issue-50585.stderr
+++ b/tests/ui/mismatched_types/for-loop-in-vec-type-mismatchrs-50585.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/issue-50585.rs:2:18
+  --> $DIR/for-loop-in-vec-type-mismatchrs-50585.rs:3:18
    |
 LL |     |y: Vec<[(); for x in 0..2 {}]>| {};
    |                  ^^^^^^^^^^^^^^^^ expected `usize`, found `()`
diff --git a/tests/ui/issues/issue-48364.rs b/tests/ui/mismatched_types/starts-with-stringify-type-mismatch-48364.rs
index 14ee75e7c9c..13ba8b1e59c 100644
--- a/tests/ui/issues/issue-48364.rs
+++ b/tests/ui/mismatched_types/starts-with-stringify-type-mismatch-48364.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/48364
 fn foo() -> bool {
     b"".starts_with(stringify!(foo))
     //~^ ERROR mismatched types
diff --git a/tests/ui/issues/issue-48364.stderr b/tests/ui/mismatched_types/starts-with-stringify-type-mismatch-48364.stderr
index 74bfa1e0693..bb767d50bb0 100644
--- a/tests/ui/issues/issue-48364.stderr
+++ b/tests/ui/mismatched_types/starts-with-stringify-type-mismatch-48364.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/issue-48364.rs:2:21
+  --> $DIR/starts-with-stringify-type-mismatch-48364.rs:3:21
    |
 LL |     b"".starts_with(stringify!(foo))
    |         ----------- ^^^^^^^^^^^^^^^ expected `&[u8]`, found `&str`
diff --git a/tests/ui/issues/issue-32797.rs b/tests/ui/modules/circular-module-imports-32797.rs
index 470d661cb28..5a2f2990795 100644
--- a/tests/ui/issues/issue-32797.rs
+++ b/tests/ui/modules/circular-module-imports-32797.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/32797
 //@ check-pass
 
 pub use bar::*;
diff --git a/tests/ui/issues/issue-17385.rs b/tests/ui/moves/matching-partially-moved-value-17385.rs
index 7400aadb059..c50fe048bfb 100644
--- a/tests/ui/issues/issue-17385.rs
+++ b/tests/ui/moves/matching-partially-moved-value-17385.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/17385
 struct X(isize);
 
 enum Enum {
diff --git a/tests/ui/issues/issue-17385.stderr b/tests/ui/moves/matching-partially-moved-value-17385.stderr
index 3c451a859e9..906f7868bcc 100644
--- a/tests/ui/issues/issue-17385.stderr
+++ b/tests/ui/moves/matching-partially-moved-value-17385.stderr
@@ -1,5 +1,5 @@
 error[E0382]: use of moved value: `foo`
-  --> $DIR/issue-17385.rs:18:5
+  --> $DIR/matching-partially-moved-value-17385.rs:19:5
    |
 LL |     let foo = X(1);
    |         --- move occurs because `foo` has type `X`, which does not implement the `Copy` trait
@@ -9,7 +9,7 @@ LL |     match foo {
    |     ^^^^^^^^^ value used here after move
    |
 note: if `X` implemented `Clone`, you could clone the value
-  --> $DIR/issue-17385.rs:1:1
+  --> $DIR/matching-partially-moved-value-17385.rs:2:1
    |
 LL | struct X(isize);
    | ^^^^^^^^ consider implementing `Clone` for this type
@@ -18,7 +18,7 @@ LL |     drop(foo);
    |          --- you could clone this value
 
 error[E0382]: use of moved value: `e`
-  --> $DIR/issue-17385.rs:25:11
+  --> $DIR/matching-partially-moved-value-17385.rs:26:11
    |
 LL |     let e = Enum::Variant2;
    |         - move occurs because `e` has type `Enum`, which does not implement the `Copy` trait
@@ -28,7 +28,7 @@ LL |     match e {
    |           ^ value used here after move
    |
 note: if `Enum` implemented `Clone`, you could clone the value
-  --> $DIR/issue-17385.rs:3:1
+  --> $DIR/matching-partially-moved-value-17385.rs:4:1
    |
 LL | enum Enum {
    | ^^^^^^^^^ consider implementing `Clone` for this type
diff --git a/tests/ui/issues/issue-32805.rs b/tests/ui/numbers-arithmetic/f32-literal-rounding-32805.rs
index 717c00a248a..59efdb9a1aa 100644
--- a/tests/ui/issues/issue-32805.rs
+++ b/tests/ui/numbers-arithmetic/f32-literal-rounding-32805.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/32805
 //@ run-pass
 fn const_mir() -> f32 { 9007199791611905.0 }
 
diff --git a/tests/ui/issues/issue-3763.rs b/tests/ui/privacy/private-field-access-violation-3763.rs
index 893009a2cd9..3fc2337086d 100644
--- a/tests/ui/issues/issue-3763.rs
+++ b/tests/ui/privacy/private-field-access-violation-3763.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/3763
 // Regression test for #3763
 
 mod my_mod {
diff --git a/tests/ui/issues/issue-3763.stderr b/tests/ui/privacy/private-field-access-violation-3763.stderr
index d101e4c33ad..6ba83dc4d78 100644
--- a/tests/ui/issues/issue-3763.stderr
+++ b/tests/ui/privacy/private-field-access-violation-3763.stderr
@@ -1,17 +1,17 @@
 error[E0616]: field `priv_field` of struct `MyStruct` is private
-  --> $DIR/issue-3763.rs:17:32
+  --> $DIR/private-field-access-violation-3763.rs:18:32
    |
 LL |     let _woohoo = (&my_struct).priv_field;
    |                                ^^^^^^^^^^ private field
 
 error[E0616]: field `priv_field` of struct `MyStruct` is private
-  --> $DIR/issue-3763.rs:20:41
+  --> $DIR/private-field-access-violation-3763.rs:21:41
    |
 LL |     let _woohoo = (Box::new(my_struct)).priv_field;
    |                                         ^^^^^^^^^^ private field
 
 error[E0624]: method `happyfun` is private
-  --> $DIR/issue-3763.rs:23:18
+  --> $DIR/private-field-access-violation-3763.rs:24:18
    |
 LL |         fn happyfun(&self) {}
    |         ------------------ private method defined here
@@ -20,7 +20,7 @@ LL |     (&my_struct).happyfun();
    |                  ^^^^^^^^ private method
 
 error[E0624]: method `happyfun` is private
-  --> $DIR/issue-3763.rs:25:27
+  --> $DIR/private-field-access-violation-3763.rs:26:27
    |
 LL |         fn happyfun(&self) {}
    |         ------------------ private method defined here
@@ -29,7 +29,7 @@ LL |     (Box::new(my_struct)).happyfun();
    |                           ^^^^^^^^ private method
 
 error[E0616]: field `priv_field` of struct `MyStruct` is private
-  --> $DIR/issue-3763.rs:26:26
+  --> $DIR/private-field-access-violation-3763.rs:27:26
    |
 LL |     let nope = my_struct.priv_field;
    |                          ^^^^^^^^^^ private field
diff --git a/tests/ui/issues/issue-2281-part1.rs b/tests/ui/resolve/cannot-find-value-in-scope-22811.rs
index 8340ade2239..fc07ae5e60e 100644
--- a/tests/ui/issues/issue-2281-part1.rs
+++ b/tests/ui/resolve/cannot-find-value-in-scope-22811.rs
@@ -1 +1,2 @@
+// https://github.com/rust-lang/rust/issues/22811
 fn main() { println!("{}", foobar); } //~ ERROR cannot find value `foobar` in this scope
diff --git a/tests/ui/issues/issue-2281-part1.stderr b/tests/ui/resolve/cannot-find-value-in-scope-22811.stderr
index 47a1ef8cc02..529fd7e8f49 100644
--- a/tests/ui/issues/issue-2281-part1.stderr
+++ b/tests/ui/resolve/cannot-find-value-in-scope-22811.stderr
@@ -1,5 +1,5 @@
 error[E0425]: cannot find value `foobar` in this scope
-  --> $DIR/issue-2281-part1.rs:1:28
+  --> $DIR/cannot-find-value-in-scope-22811.rs:2:28
    |
 LL | fn main() { println!("{}", foobar); }
    |                            ^^^^^^ not found in this scope
diff --git a/tests/ui/issues/issue-33293.rs b/tests/ui/resolve/unresolved-module-error-33293.rs
index 115ae3aad20..354f9914d44 100644
--- a/tests/ui/issues/issue-33293.rs
+++ b/tests/ui/resolve/unresolved-module-error-33293.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/33293
 fn main() {
     match 0 {
         aaa::bbb(_) => ()
diff --git a/tests/ui/issues/issue-33293.stderr b/tests/ui/resolve/unresolved-module-error-33293.stderr
index a82813194d7..28528148387 100644
--- a/tests/ui/issues/issue-33293.stderr
+++ b/tests/ui/resolve/unresolved-module-error-33293.stderr
@@ -1,5 +1,5 @@
 error[E0433]: failed to resolve: use of unresolved module or unlinked crate `aaa`
-  --> $DIR/issue-33293.rs:3:9
+  --> $DIR/unresolved-module-error-33293.rs:4:9
    |
 LL |         aaa::bbb(_) => ()
    |         ^^^ use of unresolved module or unlinked crate `aaa`
diff --git a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.rs b/tests/ui/structs/tuple-struct-field-naming-47073.rs
index 6cd1f144359..6cf27e55c4b 100644
--- a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.rs
+++ b/tests/ui/structs/tuple-struct-field-naming-47073.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/47073
 type Guilty = bool;
 type FineDollars = u32;
 
diff --git a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr b/tests/ui/structs/tuple-struct-field-naming-47073.stderr
index 0a6fe24d5e3..efbdaeca4ea 100644
--- a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr
+++ b/tests/ui/structs/tuple-struct-field-naming-47073.stderr
@@ -1,5 +1,5 @@
 error[E0609]: no field `00` on type `Verdict`
-  --> $DIR/issue-47073-zero-padded-tuple-struct-indices.rs:8:30
+  --> $DIR/tuple-struct-field-naming-47073.rs:9:30
    |
 LL |     let _condemned = justice.00;
    |                              ^^ unknown field
@@ -11,7 +11,7 @@ LL +     let _condemned = justice.0;
    |
 
 error[E0609]: no field `001` on type `Verdict`
-  --> $DIR/issue-47073-zero-padded-tuple-struct-indices.rs:10:31
+  --> $DIR/tuple-struct-field-naming-47073.rs:11:31
    |
 LL |     let _punishment = justice.001;
    |                               ^^^ unknown field
diff --git a/tests/ui/issues/auxiliary/issue-9188.rs b/tests/ui/symbol-names/auxiliary/aux-9188.rs
index 3bc5697a1a6..3bc5697a1a6 100644
--- a/tests/ui/issues/auxiliary/issue-9188.rs
+++ b/tests/ui/symbol-names/auxiliary/aux-9188.rs
diff --git a/tests/ui/symbol-names/same-symbol-name-for-inner-statics-9188.rs b/tests/ui/symbol-names/same-symbol-name-for-inner-statics-9188.rs
new file mode 100644
index 00000000000..a1014bb813f
--- /dev/null
+++ b/tests/ui/symbol-names/same-symbol-name-for-inner-statics-9188.rs
@@ -0,0 +1,11 @@
+// https://github.com/rust-lang/rust/issues/9188
+//@ run-pass
+//@ aux-build:aux-9188.rs
+
+extern crate aux_9188 as lib;
+
+pub fn main() {
+    let a = lib::bar();
+    let b = lib::foo::<isize>();
+    assert_eq!(*a, *b);
+}
diff --git a/tests/ui/issues/issue-72839-error-overflow.rs b/tests/ui/trait-bounds/trait-selection-overflow-prevention-72839.rs
index 6562d228409..436657e3de5 100644
--- a/tests/ui/issues/issue-72839-error-overflow.rs
+++ b/tests/ui/trait-bounds/trait-selection-overflow-prevention-72839.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/72839
 // Regression test for issue #72839
 // Tests that we do not overflow during trait selection after
 // a type error occurs
diff --git a/tests/ui/issues/issue-72839-error-overflow.stderr b/tests/ui/trait-bounds/trait-selection-overflow-prevention-72839.stderr
index 35be632f579..da7500f77e1 100644
--- a/tests/ui/issues/issue-72839-error-overflow.stderr
+++ b/tests/ui/trait-bounds/trait-selection-overflow-prevention-72839.stderr
@@ -1,5 +1,5 @@
 error[E0425]: cannot find value `missing_var` in this scope
-  --> $DIR/issue-72839-error-overflow.rs:18:8
+  --> $DIR/trait-selection-overflow-prevention-72839.rs:19:8
    |
 LL |     if missing_var % 8 == 0 {}
    |        ^^^^^^^^^^^ not found in this scope
diff --git a/tests/ui/issues/issue-47638.rs b/tests/ui/trait-objects/trait-object-lifetime-conversion-47638.rs
index e5a51ce0c06..c70dc74f3e5 100644
--- a/tests/ui/issues/issue-47638.rs
+++ b/tests/ui/trait-objects/trait-object-lifetime-conversion-47638.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/47638
 //@ run-pass
 #![allow(unused_variables)]
 fn id<'c, 'b>(f: &'c &'b dyn Fn(&i32)) -> &'c &'b dyn Fn(&'static i32) {
diff --git a/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs b/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs
new file mode 100644
index 00000000000..7a44920bb72
--- /dev/null
+++ b/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs
@@ -0,0 +1,24 @@
+#![allow(incomplete_features)]
+#![feature(const_closures, const_trait_impl)]
+
+const fn create_array<const N: usize>(mut f: impl FnMut(usize) -> u32 + Copy) -> [u32; N] {
+    let mut array = [0; N];
+    let mut i = 0;
+    loop {
+        array[i] = f(i);
+        //~^ ERROR the trait bound `impl FnMut(usize) -> u32 + Copy: [const] FnMut(usize)` is not satisfied [E0277]
+        i += 1;
+        if i == N {
+            break;
+        }
+    }
+    array
+}
+
+fn main() {
+    let x = create_array(const |i| 2 * i as u32);
+    assert_eq!(x, [0, 2, 4, 6, 8]);
+
+    let y = create_array(const |i| 2 * i as u32 + 1);
+    assert_eq!(y, [1, 3, 5, 7, 9]);
+}
diff --git a/tests/ui/traits/const-traits/const-closure-issue-125866-error.stderr b/tests/ui/traits/const-traits/const-closure-issue-125866-error.stderr
new file mode 100644
index 00000000000..1eadd1d8426
--- /dev/null
+++ b/tests/ui/traits/const-traits/const-closure-issue-125866-error.stderr
@@ -0,0 +1,11 @@
+error[E0277]: the trait bound `impl FnMut(usize) -> u32 + Copy: [const] FnMut(usize)` is not satisfied
+  --> $DIR/const-closure-issue-125866-error.rs:8:22
+   |
+LL |         array[i] = f(i);
+   |                    - ^
+   |                    |
+   |                    required by a bound introduced by this call
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/const-traits/const-closure-issue-125866-pass.rs b/tests/ui/traits/const-traits/const-closure-issue-125866-pass.rs
new file mode 100644
index 00000000000..af7375172e6
--- /dev/null
+++ b/tests/ui/traits/const-traits/const-closure-issue-125866-pass.rs
@@ -0,0 +1,25 @@
+//@ check-pass
+
+#![allow(incomplete_features)]
+#![feature(const_closures, const_trait_impl)]
+
+const fn create_array<const N: usize>(mut f: impl [const] FnMut(usize) -> u32 + Copy) -> [u32; N] {
+    let mut array = [0; N];
+    let mut i = 0;
+    loop {
+        array[i] = f(i);
+        i += 1;
+        if i == N {
+            break;
+        }
+    }
+    array
+}
+
+fn main() {
+    let x = create_array(const |i| 2 * i as u32);
+    assert_eq!(x, [0, 2, 4, 6, 8]);
+
+    let y = create_array(const |i| 2 * i as u32 + 1);
+    assert_eq!(y, [1, 3, 5, 7, 9]);
+}
diff --git a/tests/ui/traits/const-traits/const-fn-trait-bound-issue-104314.rs b/tests/ui/traits/const-traits/const-fn-trait-bound-issue-104314.rs
new file mode 100644
index 00000000000..09c89c9cecd
--- /dev/null
+++ b/tests/ui/traits/const-traits/const-fn-trait-bound-issue-104314.rs
@@ -0,0 +1,13 @@
+//@ check-pass
+
+#![feature(const_trait_impl, const_destruct, const_clone)]
+
+use std::marker::Destruct;
+
+const fn f<T, F: [const] Fn(&T) -> T + [const] Destruct>(_: F) {}
+
+const fn g<T: [const] Clone>() {
+    f(<T as Clone>::clone);
+}
+
+fn main() {}
diff --git a/tests/ui/issues/issue-20413.rs b/tests/ui/traits/trait-impl-overflow-with-where-clause-20413.rs
index 138a235e675..e8c27ff5cc8 100644
--- a/tests/ui/issues/issue-20413.rs
+++ b/tests/ui/traits/trait-impl-overflow-with-where-clause-20413.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/20413
 trait Foo {
     fn answer(self);
 }
diff --git a/tests/ui/issues/issue-20413.stderr b/tests/ui/traits/trait-impl-overflow-with-where-clause-20413.stderr
index 42f3cd2d062..72aff1b9ee8 100644
--- a/tests/ui/issues/issue-20413.stderr
+++ b/tests/ui/traits/trait-impl-overflow-with-where-clause-20413.stderr
@@ -1,5 +1,5 @@
 error[E0392]: type parameter `T` is never used
-  --> $DIR/issue-20413.rs:5:15
+  --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:6:15
    |
 LL | struct NoData<T>;
    |               ^ unused type parameter
@@ -8,14 +8,14 @@ LL | struct NoData<T>;
    = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
 
 error[E0275]: overflow evaluating the requirement `NoData<NoData<NoData<NoData<NoData<NoData<NoData<...>>>>>>>: Foo`
-  --> $DIR/issue-20413.rs:8:36
+  --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:9:36
    |
 LL | impl<T> Foo for T where NoData<T>: Foo {
    |                                    ^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_impl_overflow_with_where_clause_20413`)
 note: required for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Foo`
-  --> $DIR/issue-20413.rs:8:9
+  --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:9:9
    |
 LL | impl<T> Foo for T where NoData<T>: Foo {
    |         ^^^     ^                  --- unsatisfied trait bound introduced here
@@ -23,19 +23,19 @@ LL | impl<T> Foo for T where NoData<T>: Foo {
    = note: required for `NoData<T>` to implement `Foo`
 
 error[E0275]: overflow evaluating the requirement `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<...>>>>>>>: Bar`
-  --> $DIR/issue-20413.rs:27:42
+  --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:28:42
    |
 LL | impl<T> Bar for T where EvenLessData<T>: Baz {
    |                                          ^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_impl_overflow_with_where_clause_20413`)
 note: required for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Baz`
-  --> $DIR/issue-20413.rs:34:9
+  --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:35:9
    |
 LL | impl<T> Baz for T where AlmostNoData<T>: Bar {
    |         ^^^     ^                        --- unsatisfied trait bound introduced here
 note: required for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Bar`
-  --> $DIR/issue-20413.rs:27:9
+  --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:28:9
    |
 LL | impl<T> Bar for T where EvenLessData<T>: Baz {
    |         ^^^     ^                        --- unsatisfied trait bound introduced here
@@ -43,19 +43,19 @@ LL | impl<T> Bar for T where EvenLessData<T>: Baz {
    = note: required for `EvenLessData<T>` to implement `Baz`
 
 error[E0275]: overflow evaluating the requirement `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<...>>>>>>>: Baz`
-  --> $DIR/issue-20413.rs:34:42
+  --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:35:42
    |
 LL | impl<T> Baz for T where AlmostNoData<T>: Bar {
    |                                          ^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_impl_overflow_with_where_clause_20413`)
 note: required for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Bar`
-  --> $DIR/issue-20413.rs:27:9
+  --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:28:9
    |
 LL | impl<T> Bar for T where EvenLessData<T>: Baz {
    |         ^^^     ^                        --- unsatisfied trait bound introduced here
 note: required for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Baz`
-  --> $DIR/issue-20413.rs:34:9
+  --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:35:9
    |
 LL | impl<T> Baz for T where AlmostNoData<T>: Bar {
    |         ^^^     ^                        --- unsatisfied trait bound introduced here
diff --git a/tests/ui/issues/issue-51632-try-desugar-incompatible-types.rs b/tests/ui/try-trait/incompatible-types-with-question-mark-51632.rs
index 35402dff675..4b2081d14d0 100644
--- a/tests/ui/issues/issue-51632-try-desugar-incompatible-types.rs
+++ b/tests/ui/try-trait/incompatible-types-with-question-mark-51632.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/51632
 #![allow(dead_code)]
 
 fn missing_discourses() -> Result<isize, ()> {
diff --git a/tests/ui/issues/issue-51632-try-desugar-incompatible-types.stderr b/tests/ui/try-trait/incompatible-types-with-question-mark-51632.stderr
index 99fce1eeea6..c624605d76a 100644
--- a/tests/ui/issues/issue-51632-try-desugar-incompatible-types.stderr
+++ b/tests/ui/try-trait/incompatible-types-with-question-mark-51632.stderr
@@ -1,5 +1,5 @@
 error[E0308]: `?` operator has incompatible types
-  --> $DIR/issue-51632-try-desugar-incompatible-types.rs:8:5
+  --> $DIR/incompatible-types-with-question-mark-51632.rs:9:5
    |
 LL | fn forbidden_narratives() -> Result<isize, ()> {
    |                              ----------------- expected `Result<isize, ()>` because of return type
diff --git a/triagebot.toml b/triagebot.toml
index fefff78d64e..e8c15bb9bfd 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -287,6 +287,11 @@ trigger_files = [
     "compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs",
 ]
 
+[autolabel."F-explicit_tail_calls"]
+trigger_files = [
+    "tests/ui/explicit-tail-calls",
+]
+
 [autolabel."T-rustdoc-frontend"]
 trigger_labels = [
     "A-rustdoc-search",