about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/defaults/bootstrap.dist.toml2
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs53
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs21
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs29
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs18
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs12
-rw-r--r--src/bootstrap/src/core/builder/mod.rs2
-rw-r--r--src/bootstrap/src/core/builder/tests.rs44
-rw-r--r--src/bootstrap/src/core/config/config.rs7
-rw-r--r--src/bootstrap/src/core/config/mod.rs10
-rw-r--r--src/bootstrap/src/core/config/toml/build.rs2
-rw-r--r--src/bootstrap/src/lib.rs4
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs7
-rw-r--r--src/ci/citool/tests/test-jobs.yml2
-rw-r--r--src/ci/docker/host-x86_64/pr-check-1/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile1
-rw-r--r--src/ci/github-actions/jobs.yml14
-rw-r--r--src/doc/rustc/src/lints/levels.md2
-rw-r--r--src/doc/unstable-book/src/compiler-flags/split-dwarf-out-dir.md7
-rw-r--r--src/etc/lldb_providers.py5
-rw-r--r--src/librustdoc/clean/types.rs18
-rw-r--r--src/librustdoc/html/highlight.rs3
-rw-r--r--src/librustdoc/html/highlight/tests.rs14
-rw-r--r--src/librustdoc/html/render/context.rs3
-rw-r--r--src/librustdoc/html/render/mod.rs2
-rw-r--r--src/librustdoc/html/render/span_map.rs68
-rw-r--r--src/librustdoc/html/sources.rs7
-rw-r--r--src/librustdoc/lib.rs1
-rw-r--r--src/librustdoc/passes/mod.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs2
-rw-r--r--src/tools/compiletest/src/common.rs9
-rw-r--r--src/tools/compiletest/src/directives.rs217
-rw-r--r--src/tools/compiletest/src/directives/auxiliary.rs35
-rw-r--r--src/tools/compiletest/src/directives/cfg.rs14
-rw-r--r--src/tools/compiletest/src/directives/needs.rs6
-rw-r--r--src/tools/compiletest/src/executor.rs35
-rw-r--r--src/tools/compiletest/src/lib.rs32
-rw-r--r--src/tools/compiletest/src/runtest/debugger.rs27
-rw-r--r--src/tools/compiletest/src/runtest/debuginfo.rs6
-rw-r--r--src/tools/miri/src/machine.rs2
-rw-r--r--src/tools/run-make-support/src/external_deps/rustc.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs2
46 files changed, 359 insertions, 416 deletions
diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml
index b111a20f8d8..bb0592ce947 100644
--- a/src/bootstrap/defaults/bootstrap.dist.toml
+++ b/src/bootstrap/defaults/bootstrap.dist.toml
@@ -7,8 +7,6 @@ test-stage = 2
 doc-stage = 2
 # When compiling from source, you usually want all tools.
 extended = true
-# Use libtest built from the source tree instead of the precompiled one from stage 0.
-compiletest-use-stage0-libtest = false
 
 # Most users installing from source want to build all parts of the project from source.
 [llvm]
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 043457f64e5..20c54a28dda 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -8,8 +8,8 @@ use crate::core::build_steps::compile::{
 };
 use crate::core::build_steps::tool;
 use crate::core::build_steps::tool::{
-    COMPILETEST_ALLOW_FEATURES, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, ToolTargetBuildMode,
-    get_tool_target_compiler, prepare_tool_cargo,
+    SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, ToolTargetBuildMode, get_tool_target_compiler,
+    prepare_tool_cargo,
 };
 use crate::core::builder::{
     self, Alias, Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
@@ -654,7 +654,7 @@ macro_rules! tool_check_step {
             // The part of this path after the final '/' is also used as a display name.
             path: $path:literal
             $(, alt_path: $alt_path:literal )*
-            // Closure that returns `Mode` based on the passed `&Builder<'_>`
+            // `Mode` to use when checking this tool
             , mode: $mode:expr
             // Subset of nightly features that are allowed to be used when checking
             $(, allow_features: $allow_features:expr )?
@@ -682,8 +682,7 @@ macro_rules! tool_check_step {
 
             fn make_run(run: RunConfig<'_>) {
                 let target = run.target;
-                let builder = run.builder;
-                let mode = $mode(builder);
+                let mode: Mode = $mode;
 
                 let compiler = prepare_compiler_for_check(run.builder, target, mode);
 
@@ -704,7 +703,7 @@ macro_rules! tool_check_step {
                     _value
                 };
                 let extra_features: &[&str] = &[$($($enable_features),*)?];
-                let mode = $mode(builder);
+                let mode: Mode = $mode;
                 run_tool_check_step(builder, compiler, target, $path, mode, allow_features, extra_features);
             }
 
@@ -767,57 +766,50 @@ fn run_tool_check_step(
 tool_check_step!(Rustdoc {
     path: "src/tools/rustdoc",
     alt_path: "src/librustdoc",
-    mode: |_builder| Mode::ToolRustcPrivate
+    mode: Mode::ToolRustcPrivate
 });
 // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
 // of a submodule. Since the SourceType only drives the deny-warnings
 // behavior, treat it as in-tree so that any new warnings in clippy will be
 // rejected.
-tool_check_step!(Clippy { path: "src/tools/clippy", mode: |_builder| Mode::ToolRustcPrivate });
-tool_check_step!(Miri { path: "src/tools/miri", mode: |_builder| Mode::ToolRustcPrivate });
-tool_check_step!(CargoMiri {
-    path: "src/tools/miri/cargo-miri",
-    mode: |_builder| Mode::ToolRustcPrivate
-});
-tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: |_builder| Mode::ToolRustcPrivate });
+tool_check_step!(Clippy { path: "src/tools/clippy", mode: Mode::ToolRustcPrivate });
+tool_check_step!(Miri { path: "src/tools/miri", mode: Mode::ToolRustcPrivate });
+tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: Mode::ToolRustcPrivate });
+tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: Mode::ToolRustcPrivate });
 tool_check_step!(RustAnalyzer {
     path: "src/tools/rust-analyzer",
-    mode: |_builder| Mode::ToolRustcPrivate,
+    mode: Mode::ToolRustcPrivate,
     allow_features: tool::RustAnalyzer::ALLOW_FEATURES,
     enable_features: ["in-rust-tree"],
 });
 tool_check_step!(MiroptTestTools {
     path: "src/tools/miropt-test-tools",
-    mode: |_builder| Mode::ToolBootstrap
+    mode: Mode::ToolBootstrap
 });
 // We want to test the local std
 tool_check_step!(TestFloatParse {
     path: "src/tools/test-float-parse",
-    mode: |_builder| Mode::ToolStd,
+    mode: Mode::ToolStd,
     allow_features: TEST_FLOAT_PARSE_ALLOW_FEATURES
 });
 tool_check_step!(FeaturesStatusDump {
     path: "src/tools/features-status-dump",
-    mode: |_builder| Mode::ToolBootstrap
+    mode: Mode::ToolBootstrap
 });
 
-tool_check_step!(Bootstrap {
-    path: "src/bootstrap",
-    mode: |_builder| Mode::ToolBootstrap,
-    default: false
-});
+tool_check_step!(Bootstrap { path: "src/bootstrap", mode: Mode::ToolBootstrap, default: false });
 
 // `run-make-support` will be built as part of suitable run-make compiletest test steps, but support
 // check to make it easier to work on.
 tool_check_step!(RunMakeSupport {
     path: "src/tools/run-make-support",
-    mode: |_builder| Mode::ToolBootstrap,
+    mode: Mode::ToolBootstrap,
     default: false
 });
 
 tool_check_step!(CoverageDump {
     path: "src/tools/coverage-dump",
-    mode: |_builder| Mode::ToolBootstrap,
+    mode: Mode::ToolBootstrap,
     default: false
 });
 
@@ -825,23 +817,18 @@ tool_check_step!(CoverageDump {
 // so this is mainly for people working on compiletest to run locally.
 tool_check_step!(Compiletest {
     path: "src/tools/compiletest",
-    mode: |builder: &Builder<'_>| if builder.config.compiletest_use_stage0_libtest {
-        Mode::ToolBootstrap
-    } else {
-        Mode::ToolStd
-    },
-    allow_features: COMPILETEST_ALLOW_FEATURES,
+    mode: Mode::ToolBootstrap,
     default: false,
 });
 
 tool_check_step!(Linkchecker {
     path: "src/tools/linkchecker",
-    mode: |_builder| Mode::ToolBootstrap,
+    mode: Mode::ToolBootstrap,
     default: false
 });
 
 tool_check_step!(BumpStage0 {
     path: "src/tools/bump-stage0",
-    mode: |_builder| Mode::ToolBootstrap,
+    mode: Mode::ToolBootstrap,
     default: false
 });
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 7865b685659..37462c63f1b 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -1024,12 +1024,9 @@ macro_rules! tool_doc {
                         run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
                         compilers.build_compiler()
                     }
-                    Mode::ToolBootstrap => {
-                        // bootstrap/host tools should be documented with the stage 0 compiler
-                        prepare_doc_compiler(run.builder, run.builder.host_target, 1)
-                    }
                     Mode::ToolTarget => {
-                        // target tools should be documented with the in-tree compiler
+                        // when shipping multiple docs together in one folder,
+                        // they all need to use the same rustdoc version
                         prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage)
                     }
                     _ => {
@@ -1132,7 +1129,11 @@ macro_rules! tool_doc {
 tool_doc!(
     BuildHelper,
     "src/build_helper",
-    mode = Mode::ToolBootstrap,
+    // ideally, this would use ToolBootstrap,
+    // but we distribute these docs together in the same folder
+    // as a bunch of stage1 tools, and you can't mix rustdoc versions
+    // because that breaks cross-crate data (particularly search)
+    mode = Mode::ToolTarget,
     is_library = true,
     crates = ["build_helper"]
 );
@@ -1175,25 +1176,25 @@ tool_doc!(
     // "specialization" feature in its build script when it detects a nightly toolchain.
     allow_features: "specialization"
 );
-tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolBootstrap, crates = ["tidy"]);
+tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolTarget, crates = ["tidy"]);
 tool_doc!(
     Bootstrap,
     "src/bootstrap",
-    mode = Mode::ToolBootstrap,
+    mode = Mode::ToolTarget,
     is_library = true,
     crates = ["bootstrap"]
 );
 tool_doc!(
     RunMakeSupport,
     "src/tools/run-make-support",
-    mode = Mode::ToolBootstrap,
+    mode = Mode::ToolTarget,
     is_library = true,
     crates = ["run_make_support"]
 );
 tool_doc!(
     Compiletest,
     "src/tools/compiletest",
-    mode = Mode::ToolBootstrap,
+    mode = Mode::ToolTarget,
     is_library = true,
     crates = ["compiletest"]
 );
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index ca2731819e7..00aea8feab7 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -18,8 +18,8 @@ use crate::core::build_steps::llvm::get_llvm_version;
 use crate::core::build_steps::run::get_completion_paths;
 use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget;
 use crate::core::build_steps::tool::{
-    self, COMPILETEST_ALLOW_FEATURES, RustcPrivateCompilers, SourceType,
-    TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, ToolTargetBuildMode, get_tool_target_compiler,
+    self, RustcPrivateCompilers, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool,
+    ToolTargetBuildMode, get_tool_target_compiler,
 };
 use crate::core::build_steps::toolstate::ToolState;
 use crate::core::build_steps::{compile, dist, llvm};
@@ -36,7 +36,7 @@ use crate::utils::helpers::{
     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};
+use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, envify};
 
 const ADB_TEST_DIR: &str = "/data/local/tmp/work";
 
@@ -786,26 +786,26 @@ impl Step for CompiletestTest {
     fn run(self, builder: &Builder<'_>) {
         let host = self.host;
 
+        // Now that compiletest uses only stable Rust, building it always uses
+        // the stage 0 compiler. However, some of its unit tests need to be able
+        // to query information from an in-tree compiler, so we treat `--stage`
+        // as selecting the stage of that secondary compiler.
+
         if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 {
             eprintln!("\
-ERROR: `--stage 0` runs compiletest self-tests against the stage0 (precompiled) compiler, not the in-tree compiler, and will almost always cause tests to fail
+ERROR: `--stage 0` causes compiletest to query information from the stage0 (precompiled) compiler, instead of the in-tree compiler, which can cause some tests to fail inappropriately
 NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`."
             );
             crate::exit!(1);
         }
 
-        let compiler = builder.compiler(builder.top_stage, host);
-        debug!(?compiler);
+        let bootstrap_compiler = builder.compiler(0, host);
+        let staged_compiler = builder.compiler(builder.top_stage, host);
 
-        // We need `ToolStd` for the locally-built sysroot because
-        // compiletest uses unstable features of the `test` crate.
-        builder.std(compiler, host);
         let mut cargo = tool::prepare_tool_cargo(
             builder,
-            compiler,
-            // compiletest uses libtest internals; make it use the in-tree std to make sure it never
-            // breaks when std sources change.
-            Mode::ToolStd,
+            bootstrap_compiler,
+            Mode::ToolBootstrap,
             host,
             Kind::Test,
             "src/tools/compiletest",
@@ -816,9 +816,8 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         // Used for `compiletest` self-tests to have the path to the *staged* compiler. Getting this
         // right is important, as `compiletest` is intended to only support one target spec JSON
         // format, namely that of the staged compiler.
-        cargo.env("TEST_RUSTC", builder.rustc(compiler));
+        cargo.env("TEST_RUSTC", builder.rustc(staged_compiler));
 
-        cargo.allow_features(COMPILETEST_ALLOW_FEATURES);
         run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder);
     }
 }
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 4f096d50ea5..819e903020c 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -380,7 +380,6 @@ macro_rules! bootstrap_tool {
     ($(
         $name:ident, $path:expr, $tool_name:expr
         $(,is_external_tool = $external:expr)*
-        $(,is_unstable_tool = $unstable:expr)*
         $(,allow_features = $allow_features:expr)?
         $(,submodules = $submodules:expr)?
         $(,artifact_kind = $artifact_kind:expr)?
@@ -438,19 +437,11 @@ macro_rules! bootstrap_tool {
                     }
                 )*
 
-                let is_unstable = false $(|| $unstable)*;
-                let compiletest_wants_stage0 = $tool_name == "compiletest" && builder.config.compiletest_use_stage0_libtest;
-
                 builder.ensure(ToolBuild {
                     build_compiler: self.compiler,
                     target: self.target,
                     tool: $tool_name,
-                    mode: if is_unstable && !compiletest_wants_stage0 {
-                        // use in-tree libraries for unstable features
-                        Mode::ToolStd
-                    } else {
-                        Mode::ToolBootstrap
-                    },
+                    mode: Mode::ToolBootstrap,
                     path: $path,
                     source_type: if false $(|| $external)* {
                         SourceType::Submodule
@@ -483,8 +474,6 @@ macro_rules! bootstrap_tool {
     }
 }
 
-pub(crate) const COMPILETEST_ALLOW_FEATURES: &str = "internal_output_capture";
-
 bootstrap_tool!(
     // This is marked as an external tool because it includes dependencies
     // from submodules. Trying to keep the lints in sync between all the repos
@@ -495,7 +484,7 @@ bootstrap_tool!(
     Tidy, "src/tools/tidy", "tidy";
     Linkchecker, "src/tools/linkchecker", "linkchecker";
     CargoTest, "src/tools/cargotest", "cargotest";
-    Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES;
+    Compiletest, "src/tools/compiletest", "compiletest";
     BuildManifest, "src/tools/build-manifest", "build-manifest";
     RemoteTestClient, "src/tools/remote-test-client", "remote-test-client";
     RustInstaller, "src/tools/rust-installer", "rust-installer";
@@ -509,8 +498,7 @@ bootstrap_tool!(
     CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
     GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
     GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
-    // rustdoc-gui-test has a crate dependency on compiletest, so it needs the same unstable features.
-    RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES;
+    RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test";
     CoverageDump, "src/tools/coverage-dump", "coverage-dump";
     UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator";
     FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump";
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index 9fc4ce669c2..a404aec5120 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -10,7 +10,7 @@ use crate::core::config::flags::Color;
 use crate::utils::build_stamp;
 use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags};
 use crate::{
-    BootstrapCommand, CLang, Compiler, Config, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode,
+    BootstrapCommand, CLang, Compiler, Config, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode,
     RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t,
 };
 
@@ -851,8 +851,6 @@ impl Builder<'_> {
 
         rustflags.arg("-Zmacro-backtrace");
 
-        let want_rustdoc = self.doc_tests != DocTests::No;
-
         // Clear the output directory if the real rustc we're using has changed;
         // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc.
         //
@@ -881,7 +879,8 @@ impl Builder<'_> {
             .env("RUSTC_REAL", self.rustc(compiler))
             .env("RUSTC_STAGE", build_compiler_stage.to_string())
             .env("RUSTC_SYSROOT", sysroot)
-            .env("RUSTC_LIBDIR", libdir)
+            .env("RUSTC_LIBDIR", &libdir)
+            .env("RUSTDOC_LIBDIR", libdir)
             .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
             .env("RUSTDOC_REAL", rustdoc_path)
             .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir());
@@ -919,11 +918,6 @@ impl Builder<'_> {
             rustflags.arg(&format!("-Zstack-protector={stack_protector}"));
         }
 
-        if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc
-        {
-            cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
-        }
-
         let debuginfo_level = match mode {
             Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
             Mode::Std => self.config.rust_debuginfo_level_std,
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index 006dea4b98d..fc06db8f80b 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -1145,7 +1145,7 @@ impl<'a> Builder<'a> {
                 test::RunMakeCargo,
             ),
             Kind::Miri => describe!(test::Crate),
-            Kind::Bench => describe!(test::Crate, test::CrateLibrustc),
+            Kind::Bench => describe!(test::Crate, test::CrateLibrustc, test::CrateRustdoc),
             Kind::Doc => describe!(
                 doc::UnstableBook,
                 doc::UnstableBookGen,
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index a3c8380a3c7..3306435758b 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -1158,13 +1158,12 @@ mod snapshot {
         [doc] embedded-book (book) <host>
         [doc] edition-guide (book) <host>
         [doc] style-guide (book) <host>
-        [build] rustdoc 0 <host>
-        [doc] rustc 0 <host> -> Tidy 1 <host>
-        [doc] rustc 0 <host> -> Bootstrap 1 <host>
+        [doc] rustc 1 <host> -> Tidy 2 <host>
+        [doc] rustc 1 <host> -> Bootstrap 2 <host>
         [doc] rustc 1 <host> -> releases 2 <host>
-        [doc] rustc 0 <host> -> RunMakeSupport 1 <host>
-        [doc] rustc 0 <host> -> BuildHelper 1 <host>
-        [doc] rustc 0 <host> -> Compiletest 1 <host>
+        [doc] rustc 1 <host> -> RunMakeSupport 2 <host>
+        [doc] rustc 1 <host> -> BuildHelper 2 <host>
+        [doc] rustc 1 <host> -> Compiletest 2 <host>
         [build] rustc 0 <host> -> RustInstaller 1 <host>
         "
         );
@@ -2005,21 +2004,6 @@ mod snapshot {
     }
 
     #[test]
-    fn check_compiletest_stage1_libtest() {
-        let ctx = TestCtx::new();
-        insta::assert_snapshot!(
-            ctx.config("check")
-                .path("compiletest")
-                .args(&["--set", "build.compiletest-use-stage0-libtest=false"])
-                .render_steps(), @r"
-        [build] llvm <host>
-        [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustc 1 <host> -> std 1 <host>
-        [check] rustc 1 <host> -> Compiletest 2 <host>
-        ");
-    }
-
-    #[test]
     fn check_codegen() {
         let ctx = TestCtx::new();
         insta::assert_snapshot!(
@@ -2146,6 +2130,17 @@ mod snapshot {
     }
 
     #[test]
+    fn test_compiletest_self_test() {
+        let ctx = TestCtx::new();
+        let steps = ctx.config("test").arg("compiletest").render_steps();
+        insta::assert_snapshot!(steps, @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustdoc 0 <host>
+        ");
+    }
+
+    #[test]
     fn test_compiletest_suites_stage1() {
         let ctx = TestCtx::new();
         insta::assert_snapshot!(
@@ -2690,8 +2685,11 @@ mod snapshot {
                 .path("src/tools/compiletest")
                 .stage(2)
                 .render_steps(), @r"
-        [build] rustdoc 0 <host>
-        [doc] rustc 0 <host> -> Compiletest 1 <host>
+        [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> -> Compiletest 2 <host>
         ");
     }
 
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 271ce4cb950..1fcc1174e85 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -310,9 +310,6 @@ pub struct Config {
     /// sources.
     pub compiletest_allow_stage0: bool,
 
-    /// Whether to use the precompiled stage0 libtest with compiletest.
-    pub compiletest_use_stage0_libtest: bool,
-
     /// Default value for `--extra-checks`
     pub tidy_extra_checks: Option<String>,
     pub is_running_on_ci: bool,
@@ -497,7 +494,8 @@ impl Config {
             optimized_compiler_builtins: build_optimized_compiler_builtins,
             jobs: build_jobs,
             compiletest_diff_tool: build_compiletest_diff_tool,
-            compiletest_use_stage0_libtest: build_compiletest_use_stage0_libtest,
+            // No longer has any effect; kept (for now) to avoid breaking people's configs.
+            compiletest_use_stage0_libtest: _,
             tidy_extra_checks: build_tidy_extra_checks,
             ccache: build_ccache,
             exclude: build_exclude,
@@ -1197,7 +1195,6 @@ impl Config {
             compiler_docs: build_compiler_docs.unwrap_or(false),
             compiletest_allow_stage0: build_compiletest_allow_stage0.unwrap_or(false),
             compiletest_diff_tool: build_compiletest_diff_tool,
-            compiletest_use_stage0_libtest: build_compiletest_use_stage0_libtest.unwrap_or(true),
             config: toml_path,
             configure_args: build_configure_args.unwrap_or_default(),
             control_flow_guard: rust_control_flow_guard.unwrap_or(false),
diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs
index 56b87823a36..007ed4aaba1 100644
--- a/src/bootstrap/src/core/config/mod.rs
+++ b/src/bootstrap/src/core/config/mod.rs
@@ -47,11 +47,17 @@ use crate::str::FromStr;
 #[macro_export]
 macro_rules! define_config {
     ($(#[$attr:meta])* struct $name:ident {
-        $($field:ident: Option<$field_ty:ty> = $field_key:literal,)*
+        $(
+            $(#[$field_attr:meta])*
+            $field:ident: Option<$field_ty:ty> = $field_key:literal,
+        )*
     }) => {
         $(#[$attr])*
         pub struct $name {
-            $(pub $field: Option<$field_ty>,)*
+            $(
+                $(#[$field_attr])*
+                pub $field: Option<$field_ty>,
+            )*
         }
 
         impl Merge for $name {
diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs
index a9d4d3961c9..c63673dd980 100644
--- a/src/bootstrap/src/core/config/toml/build.rs
+++ b/src/bootstrap/src/core/config/toml/build.rs
@@ -70,6 +70,8 @@ define_config! {
         jobs: Option<u32> = "jobs",
         compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
         compiletest_allow_stage0: Option<bool> = "compiletest-allow-stage0",
+        /// No longer has any effect; kept (for now) to avoid breaking people's configs.
+        /// FIXME(#146929): Remove this in 2026.
         compiletest_use_stage0_libtest: Option<bool> = "compiletest-use-stage0-libtest",
         tidy_extra_checks: Option<String> = "tidy-extra-checks",
         ccache: Option<StringOrBool> = "ccache",
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index d798639cc96..dd30f05b728 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -327,8 +327,8 @@ pub enum Mode {
     ToolTarget,
 
     /// Build a tool which uses the locally built std, placing output in the
-    /// "stageN-tools" directory. Its usage is quite rare, mainly used by
-    /// compiletest which needs libtest.
+    /// "stageN-tools" directory. Its usage is quite rare; historically it was
+    /// needed by compiletest, but now it is mainly used by `test-float-parse`.
     ToolStd,
 
     /// Build a tool which uses the `rustc_private` mechanism, and thus
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index f311c84bec9..853fc4e6623 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -557,8 +557,13 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         summary: "New option `build.windows-rc` that will override which resource compiler on Windows will be used to compile Rust.",
     },
     ChangeInfo {
-        change_id: 99999,
+        change_id: 147046,
         severity: ChangeSeverity::Warning,
         summary: "The `rust.use-lld` option has been renamed to `rust.bootstrap-override-lld`. Note that it only serves for overriding the linker used when building Rust code in bootstrap to be LLD.",
     },
+    ChangeInfo {
+        change_id: 146929,
+        severity: ChangeSeverity::Info,
+        summary: "`compiletest` is now always built with the stage 0 compiler, so `build.compiletest-use-stage0-libtest` has no effect.",
+    },
 ];
diff --git a/src/ci/citool/tests/test-jobs.yml b/src/ci/citool/tests/test-jobs.yml
index d82b3e7648e..512c8062857 100644
--- a/src/ci/citool/tests/test-jobs.yml
+++ b/src/ci/citool/tests/test-jobs.yml
@@ -27,7 +27,7 @@ runners:
     <<: *base-job
 envs:
   env-x86_64-apple-tests: &env-x86_64-apple-tests
-    SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact
+    SCRIPT: ./x.py check compiletest && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact
     RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc
     RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
     # Ensure that host tooling is tested on our minimum supported macOS version.
diff --git a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile
index 04ac0f33daf..776bbb12e44 100644
--- a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile
+++ b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile
@@ -43,7 +43,6 @@ ENV SCRIPT \
   python3 ../x.py check bootstrap && \
   /scripts/check-default-config-profiles.sh && \
   python3 ../x.py build src/tools/build-manifest && \
-  python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \
   python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
   python3 ../x.py check --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \
   /scripts/validate-toolstate.sh && \
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
index 95357d22937..278e40eb71f 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
@@ -90,5 +90,4 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu
 COPY scripts/shared.sh /scripts/
 
 ENV SCRIPT /tmp/checktools.sh ../x.py && \
-  python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \
   python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--jobs 1'"
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 8303699ce8a..4384ec76769 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -430,7 +430,8 @@ auto:
       # Ensure that host tooling is built to support our minimum support macOS version.
       MACOSX_DEPLOYMENT_TARGET: 10.12
       MACOSX_STD_DEPLOYMENT_TARGET: 10.12
-      SELECT_XCODE: /Applications/Xcode_15.2.app
+      SELECT_XCODE: /Applications/Xcode_15.4.app
+      USE_XCODE_CLANG: 1
       DIST_REQUIRE_ALL_TOOLS: 1
       CODEGEN_BACKENDS: llvm,cranelift
     <<: *job-macos
@@ -450,21 +451,24 @@ auto:
 
   - name: dist-aarch64-apple
     env:
-      SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin
+      SCRIPT: >-
+        ./x.py dist bootstrap
+        --include-default-paths
+        --host=aarch64-apple-darwin
+        --target=aarch64-apple-darwin
       RUST_CONFIGURE_ARGS: >-
         --enable-full-tools
         --enable-sanitizers
         --enable-profiler
         --set rust.jemalloc
-        --set llvm.ninja=false
         --set rust.lto=thin
         --set rust.codegen-units=1
-      SELECT_XCODE: /Applications/Xcode_15.4.app
-      USE_XCODE_CLANG: 1
       # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else
       # supports the hardware.
       MACOSX_DEPLOYMENT_TARGET: 11.0
       MACOSX_STD_DEPLOYMENT_TARGET: 11.0
+      SELECT_XCODE: /Applications/Xcode_15.4.app
+      USE_XCODE_CLANG: 1
       DIST_REQUIRE_ALL_TOOLS: 1
       CODEGEN_BACKENDS: llvm,cranelift
     <<: *job-macos
diff --git a/src/doc/rustc/src/lints/levels.md b/src/doc/rustc/src/lints/levels.md
index 5b002b435a5..09b55da741d 100644
--- a/src/doc/rustc/src/lints/levels.md
+++ b/src/doc/rustc/src/lints/levels.md
@@ -38,7 +38,7 @@ talk about later in this section.
 
 Sometimes, it can be helpful to suppress lints, but at the same time ensure that
 the code in question still emits them. The 'expect' level does exactly this. If
-the lint in question is not emitted, the `unfulfilled_lint_expectation` lint
+the lint in question is not emitted, the `unfulfilled_lint_expectations` lint
 triggers on the `expect` attribute, notifying you that the expectation is no
 longer fulfilled.
 
diff --git a/src/doc/unstable-book/src/compiler-flags/split-dwarf-out-dir.md b/src/doc/unstable-book/src/compiler-flags/split-dwarf-out-dir.md
new file mode 100644
index 00000000000..a2070730b42
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/split-dwarf-out-dir.md
@@ -0,0 +1,7 @@
+# `split-dwarf-out-dir`
+
+On systems which use DWARF debug info this flag causes `.dwo` files produced
+by `-C split-debuginfo` to be written to the specified directory rather than
+placed next to the object files. This is mostly useful if you have a build
+system which needs to control where to find compile outputs without running the
+compiler and have to put your `.dwo` files in a separate directory.
diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py
index 65f18baa937..3eb964d2fba 100644
--- a/src/etc/lldb_providers.py
+++ b/src/etc/lldb_providers.py
@@ -761,7 +761,8 @@ class MSVCTupleSyntheticProvider:
 
     def get_child_at_index(self, index: int) -> SBValue:
         child: SBValue = self.valobj.GetChildAtIndex(index)
-        return child.CreateChildAtOffset(str(index), 0, child.GetType())
+        offset = self.valobj.GetType().GetFieldAtIndex(index).byte_offset
+        return self.valobj.CreateChildAtOffset(str(index), offset, child.GetType())
 
     def update(self):
         pass
@@ -772,7 +773,7 @@ class MSVCTupleSyntheticProvider:
     def get_type_name(self) -> str:
         name = self.valobj.GetTypeName()
         # remove "tuple$<" and ">", str.removeprefix and str.removesuffix require python 3.9+
-        name = name[7:-1]
+        name = name[7:-1].strip()
         return "(" + name + ")"
 
 
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index c2cf39c4be0..6e3dfa0ac22 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1084,8 +1084,8 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
 
     let mut changed_auto_active_status = None;
 
-    // First we get all `doc(auto_cfg)` attributes.
-    for attr in attrs.clone() {
+    // We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes.
+    for attr in attrs {
         if let Some(ident) = attr.ident()
             && ident.name == sym::doc
             && let Some(attrs) = attr.meta_item_list()
@@ -1146,15 +1146,9 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
                     }
                 }
             }
-        }
-    }
-
-    // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because
-    // `doc(cfg())` overrides `cfg()`).
-    for attr in attrs {
-        if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
-            // treat #[target_feature(enable = "feat")] attributes as if they were
-            // #[doc(cfg(target_feature = "feat"))] attributes as well
+        } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
+            // Treat `#[target_feature(enable = "feat")]` attributes as if they were
+            // `#[doc(cfg(target_feature = "feat"))]` attributes as well.
             for (feature, _) in features {
                 cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
             }
@@ -1685,7 +1679,7 @@ impl Type {
         match (self_cleared, other_cleared) {
             // Recursive cases.
             (Type::Tuple(a), Type::Tuple(b)) => {
-                a.len() == b.len() && a.iter().zip(b).all(|(a, b)| a.is_doc_subtype_of(b, cache))
+                a.iter().eq_by(b, |a, b| a.is_doc_subtype_of(b, cache))
             }
             (Type::Slice(a), Type::Slice(b)) => a.is_doc_subtype_of(b, cache),
             (Type::Array(a, al), Type::Array(b, bl)) => al == bl && a.is_doc_subtype_of(b, cache),
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index fad15573cde..1dcb4dcc3ff 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -12,15 +12,16 @@ use std::iter;
 
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind};
+use rustc_span::BytePos;
 use rustc_span::edition::Edition;
 use rustc_span::symbol::Symbol;
-use rustc_span::{BytePos, DUMMY_SP, Span};
 
 use super::format;
 use crate::clean::PrimitiveType;
 use crate::display::Joined as _;
 use crate::html::escape::EscapeBodyText;
 use crate::html::macro_expansion::ExpandedCode;
+use crate::html::render::span_map::{DUMMY_SP, Span};
 use crate::html::render::{Context, LinkFromSrc};
 
 /// This type is needed in case we want to render links on items to allow to go to their definition.
diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs
index 2603e887bea..4d1bee9b3a1 100644
--- a/src/librustdoc/html/highlight/tests.rs
+++ b/src/librustdoc/html/highlight/tests.rs
@@ -1,6 +1,7 @@
 use expect_test::expect_file;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_span::create_default_session_globals_then;
+use test::Bencher;
 
 use super::{DecorationInfo, write_code};
 
@@ -81,3 +82,16 @@ let a = 4;";
         expect_file!["fixtures/decorations.html"].assert_eq(&html);
     });
 }
+
+#[bench]
+fn bench_html_highlighting(b: &mut Bencher) {
+    let src = include_str!("../../../../compiler/rustc_ast/src/visit.rs");
+
+    create_default_session_globals_then(|| {
+        b.iter(|| {
+            let mut out = String::new();
+            write_code(&mut out, src, None, None, None);
+            out
+        });
+    });
+}
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 5f92ab2fada..4c06d0da470 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -30,6 +30,7 @@ use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
 use crate::html::macro_expansion::ExpandedCode;
 use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary};
+use crate::html::render::span_map::Span;
 use crate::html::render::write_shared::write_shared;
 use crate::html::url_parts_builder::UrlPartsBuilder;
 use crate::html::{layout, sources, static_files};
@@ -139,7 +140,7 @@ pub(crate) struct SharedContext<'tcx> {
 
     /// Correspondence map used to link types used in the source code pages to allow to click on
     /// links to jump to the type's definition.
-    pub(crate) span_correspondence_map: FxHashMap<rustc_span::Span, LinkFromSrc>,
+    pub(crate) span_correspondence_map: FxHashMap<Span, LinkFromSrc>,
     pub(crate) expanded_codes: FxHashMap<BytePos, Vec<ExpandedCode>>,
     /// The [`Cache`] used during rendering.
     pub(crate) cache: Cache,
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 97dcaf57cdf..d6371e4dbab 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -36,7 +36,7 @@ mod ordered_json;
 mod print_item;
 pub(crate) mod sidebar;
 mod sorted_template;
-mod span_map;
+pub(crate) mod span_map;
 mod type_layout;
 mod write_shared;
 
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index ef7ce33298d..bc9417b1bb1 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -8,11 +8,48 @@ use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::hygiene::MacroKind;
-use rustc_span::{BytePos, ExpnKind, Span};
+use rustc_span::{BytePos, ExpnKind};
 
 use crate::clean::{self, PrimitiveType, rustc_span};
 use crate::html::sources;
 
+/// This is a stripped down version of [`rustc_span::Span`] that only contains the start and end byte positions of the span.
+///
+/// Profiling showed that the `Span` interner was taking up a lot of the run-time when highlighting, and since we
+/// never actually use the context and parent that are stored in a normal `Span`, we can replace its usages with this
+/// one, which is much cheaper to construct.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub(crate) struct Span {
+    lo: BytePos,
+    hi: BytePos,
+}
+
+impl From<rustc_span::Span> for Span {
+    fn from(value: rustc_span::Span) -> Self {
+        Self { lo: value.lo(), hi: value.hi() }
+    }
+}
+
+impl Span {
+    pub(crate) fn lo(self) -> BytePos {
+        self.lo
+    }
+
+    pub(crate) fn hi(self) -> BytePos {
+        self.hi
+    }
+
+    pub(crate) fn with_lo(self, lo: BytePos) -> Self {
+        Self { lo, hi: self.hi() }
+    }
+
+    pub(crate) fn with_hi(self, hi: BytePos) -> Self {
+        Self { lo: self.lo(), hi }
+    }
+}
+
+pub(crate) const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0) };
+
 /// This enum allows us to store two different kinds of information:
 ///
 /// In case the `span` definition comes from the same crate, we can simply get the `span` and use
@@ -96,7 +133,7 @@ impl SpanMapVisitor<'_> {
                         })
                         .unwrap_or(path.span)
                 };
-                self.matches.insert(span, link);
+                self.matches.insert(span.into(), link);
             }
             Res::Local(_) if let Some(span) = self.tcx.hir_res_span(path.res) => {
                 let path_span = if only_use_last_segment
@@ -106,11 +143,12 @@ impl SpanMapVisitor<'_> {
                 } else {
                     path.span
                 };
-                self.matches.insert(path_span, LinkFromSrc::Local(clean::Span::new(span)));
+                self.matches.insert(path_span.into(), LinkFromSrc::Local(clean::Span::new(span)));
             }
             Res::PrimTy(p) => {
                 // FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
-                self.matches.insert(path.span, LinkFromSrc::Primitive(PrimitiveType::from(p)));
+                self.matches
+                    .insert(path.span.into(), LinkFromSrc::Primitive(PrimitiveType::from(p)));
             }
             Res::Err => {}
             _ => {}
@@ -127,7 +165,7 @@ impl SpanMapVisitor<'_> {
             if cspan.inner().is_dummy() || cspan.cnum(self.tcx.sess) != LOCAL_CRATE {
                 return;
             }
-            self.matches.insert(span, LinkFromSrc::Doc(item.owner_id.to_def_id()));
+            self.matches.insert(span.into(), LinkFromSrc::Doc(item.owner_id.to_def_id()));
         }
     }
 
@@ -138,7 +176,7 @@ impl SpanMapVisitor<'_> {
     /// so, we loop until we find the macro definition by using `outer_expn_data` in a loop.
     /// Finally, we get the information about the macro itself (`span` if "local", `DefId`
     /// otherwise) and store it inside the span map.
-    fn handle_macro(&mut self, span: Span) -> bool {
+    fn handle_macro(&mut self, span: rustc_span::Span) -> bool {
         if !span.from_expansion() {
             return false;
         }
@@ -176,7 +214,7 @@ impl SpanMapVisitor<'_> {
         // The "call_site" includes the whole macro with its "arguments". We only want
         // the macro name.
         let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32));
-        self.matches.insert(new_span, link_from_src);
+        self.matches.insert(new_span.into(), link_from_src);
         true
     }
 
@@ -233,7 +271,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
         intravisit::walk_path(self, path);
     }
 
-    fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: Span) {
+    fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: rustc_span::Span) {
         match *qpath {
             QPath::TypeRelative(qself, path) => {
                 if matches!(path.res, Res::Err) {
@@ -249,7 +287,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
                         self.handle_path(&path, false);
                     }
                 } else {
-                    self.infer_id(path.hir_id, Some(id), path.ident.span);
+                    self.infer_id(path.hir_id, Some(id), path.ident.span.into());
                 }
 
                 rustc_ast::visit::try_visit!(self.visit_ty_unambig(qself));
@@ -267,7 +305,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
         }
     }
 
-    fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) {
+    fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: rustc_span::Span, id: HirId) {
         // To make the difference between "mod foo {}" and "mod foo;". In case we "import" another
         // file, we want to link to it. Otherwise no need to create a link.
         if !span.overlaps(m.spans.inner_span) {
@@ -275,8 +313,10 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
             // name only and not all the "mod foo;".
             if let Node::Item(item) = self.tcx.hir_node(id) {
                 let (ident, _) = item.expect_mod();
-                self.matches
-                    .insert(ident.span, LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)));
+                self.matches.insert(
+                    ident.span.into(),
+                    LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)),
+                );
             }
         } else {
             // If it's a "mod foo {}", we want to look to its documentation page.
@@ -288,9 +328,9 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
     fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
         match expr.kind {
             ExprKind::MethodCall(segment, ..) => {
-                self.infer_id(segment.hir_id, Some(expr.hir_id), segment.ident.span)
+                self.infer_id(segment.hir_id, Some(expr.hir_id), segment.ident.span.into())
             }
-            ExprKind::Call(call, ..) => self.infer_id(call.hir_id, None, call.span),
+            ExprKind::Call(call, ..) => self.infer_id(call.hir_id, None, call.span.into()),
             _ => {
                 if self.handle_macro(expr.span) {
                     // We don't want to go deeper into the macro.
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 9c5518a780e..c79f63fbc20 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -348,7 +348,12 @@ pub(crate) fn print_src(
         highlight::write_code(
             fmt,
             s,
-            Some(highlight::HrefContext { context, file_span, root_path, current_href }),
+            Some(highlight::HrefContext {
+                context,
+                file_span: file_span.into(),
+                root_path,
+                current_href,
+            }),
             Some(decoration_info),
             Some(line_info),
         );
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index c4f24e09ddb..d7ffb25f8bd 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -13,6 +13,7 @@
 #![feature(if_let_guard)]
 #![feature(iter_advance_by)]
 #![feature(iter_intersperse)]
+#![feature(iter_order_by)]
 #![feature(rustc_private)]
 #![feature(test)]
 #![warn(rustc::internal)]
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 475d05b7d0e..f45df8d2d0d 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -77,11 +77,11 @@ pub(crate) enum Condition {
 pub(crate) const PASSES: &[Pass] = &[
     CHECK_DOC_CFG,
     CHECK_DOC_TEST_VISIBILITY,
+    PROPAGATE_DOC_CFG,
     STRIP_ALIASED_NON_LOCAL,
     STRIP_HIDDEN,
     STRIP_PRIVATE,
     STRIP_PRIV_IMPORTS,
-    PROPAGATE_DOC_CFG,
     PROPAGATE_STABILITY,
     COLLECT_INTRA_DOC_LINKS,
     COLLECT_TRAIT_IMPLS,
@@ -94,11 +94,11 @@ pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[
     ConditionalPass::always(COLLECT_TRAIT_IMPLS),
     ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY),
     ConditionalPass::always(CHECK_DOC_CFG),
+    ConditionalPass::always(COLLECT_INTRA_DOC_LINKS),
     ConditionalPass::always(STRIP_ALIASED_NON_LOCAL),
     ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
     ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
     ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate),
-    ConditionalPass::always(COLLECT_INTRA_DOC_LINKS),
     ConditionalPass::always(PROPAGATE_DOC_CFG),
     ConditionalPass::always(PROPAGATE_STABILITY),
     ConditionalPass::always(RUN_LINTS),
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
index d78da9396fa..7d14ba7fcf1 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -43,8 +43,8 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f
             expr.span,
             format!(
                 "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)",
-                from_layout.align.abi.bytes(),
-                to_layout.align.abi.bytes(),
+                from_layout.align.bytes(),
+                to_layout.align.bytes(),
             ),
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs
index 92910cf8adf..ff5320719aa 100644
--- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs
@@ -72,7 +72,7 @@ fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned<LitKind>, to: &Ty<'_>)
     cx.tcx
         .layout_of(cx.typing_env().as_query_input(to_mid_ty))
         .is_ok_and(|layout| {
-            let align = u128::from(layout.align.abi.bytes());
+            let align = u128::from(layout.align.bytes());
             u128::from(val) <= align
         })
 }
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 6da102b1b5f..6d959948918 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -661,18 +661,10 @@ pub struct Config {
     pub builtin_cfg_names: OnceLock<HashSet<String>>,
     pub supported_crate_types: OnceLock<HashSet<String>>,
 
-    /// FIXME: this is why we still need to depend on *staged* `std`, it's because we currently rely
-    /// on `#![feature(internal_output_capture)]` for [`std::io::set_output_capture`] to implement
-    /// `libtest`-esque `--no-capture`.
-    ///
     /// FIXME: rename this to the more canonical `no_capture`, or better, invert this to `capture`
     /// to avoid `!nocapture` double-negatives.
     pub nocapture: bool,
 
-    /// True if the experimental new output-capture implementation should be
-    /// used, avoiding the need for `#![feature(internal_output_capture)]`.
-    pub new_output_capture: bool,
-
     /// Needed both to construct [`build_helper::git::GitConfig`].
     pub nightly_branch: String,
     pub git_merge_commit_email: String,
@@ -790,7 +782,6 @@ impl Config {
             builtin_cfg_names: Default::default(),
             supported_crate_types: Default::default(),
             nocapture: Default::default(),
-            new_output_capture: Default::default(),
             nightly_branch: Default::default(),
             git_merge_commit_email: Default::default(),
             profiler_runtime: Default::default(),
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index e84a2278766..e6916610190 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -63,9 +63,10 @@ impl EarlyProps {
             &mut poisoned,
             testfile,
             rdr,
-            &mut |DirectiveLine { line_number, raw_directive: ln, .. }| {
-                parse_and_update_aux(config, ln, testfile, line_number, &mut props.aux);
-                config.parse_and_update_revisions(testfile, line_number, ln, &mut props.revisions);
+            // (dummy comment to force args into vertical layout)
+            &mut |ref ln: DirectiveLine<'_>| {
+                parse_and_update_aux(config, ln, testfile, &mut props.aux);
+                config.parse_and_update_revisions(testfile, ln, &mut props.revisions);
             },
         );
 
@@ -367,8 +368,8 @@ impl TestProps {
                 &mut poisoned,
                 testfile,
                 file,
-                &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| {
-                    if !directive.applies_to_test_revision(test_revision) {
+                &mut |ref ln: DirectiveLine<'_>| {
+                    if !ln.applies_to_test_revision(test_revision) {
                         return;
                     }
 
@@ -378,7 +379,6 @@ impl TestProps {
                         ln,
                         ERROR_PATTERN,
                         testfile,
-                        line_number,
                         &mut self.error_patterns,
                         |r| r,
                     );
@@ -386,7 +386,6 @@ impl TestProps {
                         ln,
                         REGEX_ERROR_PATTERN,
                         testfile,
-                        line_number,
                         &mut self.regex_error_patterns,
                         |r| r,
                     );
@@ -395,7 +394,6 @@ impl TestProps {
                         ln,
                         DOC_FLAGS,
                         testfile,
-                        line_number,
                         &mut self.doc_flags,
                         |r| r,
                     );
@@ -414,7 +412,7 @@ impl TestProps {
                     }
 
                     if let Some(flags) =
-                        config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile, line_number)
+                        config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile)
                     {
                         let flags = split_flags(&flags);
                         for flag in &flags {
@@ -425,39 +423,28 @@ impl TestProps {
                         self.compile_flags.extend(flags);
                     }
                     if config
-                        .parse_name_value_directive(
-                            ln,
-                            INCORRECT_COMPILER_FLAGS,
-                            testfile,
-                            line_number,
-                        )
+                        .parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS, testfile)
                         .is_some()
                     {
                         panic!("`compiler-flags` directive should be spelled `compile-flags`");
                     }
 
-                    if let Some(edition) = config.parse_edition(ln, testfile, line_number) {
+                    if let Some(edition) = config.parse_edition(ln, testfile) {
                         // The edition is added at the start, since flags from //@compile-flags must
                         // be passed to rustc last.
                         self.compile_flags.insert(0, format!("--edition={}", edition.trim()));
                         has_edition = true;
                     }
 
-                    config.parse_and_update_revisions(
-                        testfile,
-                        line_number,
-                        ln,
-                        &mut self.revisions,
-                    );
+                    config.parse_and_update_revisions(testfile, ln, &mut self.revisions);
 
-                    if let Some(flags) =
-                        config.parse_name_value_directive(ln, RUN_FLAGS, testfile, line_number)
+                    if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS, testfile)
                     {
                         self.run_flags.extend(split_flags(&flags));
                     }
 
                     if self.pp_exact.is_none() {
-                        self.pp_exact = config.parse_pp_exact(ln, testfile, line_number);
+                        self.pp_exact = config.parse_pp_exact(ln, testfile);
                     }
 
                     config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice);
@@ -479,9 +466,7 @@ impl TestProps {
                     );
                     config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic);
 
-                    if let Some(m) =
-                        config.parse_name_value_directive(ln, PRETTY_MODE, testfile, line_number)
-                    {
+                    if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE, testfile) {
                         self.pretty_mode = m;
                     }
 
@@ -492,13 +477,12 @@ impl TestProps {
                     );
 
                     // Call a helper method to deal with aux-related directives.
-                    parse_and_update_aux(config, ln, testfile, line_number, &mut self.aux);
+                    parse_and_update_aux(config, ln, testfile, &mut self.aux);
 
                     config.push_name_value_directive(
                         ln,
                         EXEC_ENV,
                         testfile,
-                        line_number,
                         &mut self.exec_env,
                         Config::parse_env,
                     );
@@ -506,7 +490,6 @@ impl TestProps {
                         ln,
                         UNSET_EXEC_ENV,
                         testfile,
-                        line_number,
                         &mut self.unset_exec_env,
                         |r| r.trim().to_owned(),
                     );
@@ -514,7 +497,6 @@ impl TestProps {
                         ln,
                         RUSTC_ENV,
                         testfile,
-                        line_number,
                         &mut self.rustc_env,
                         Config::parse_env,
                     );
@@ -522,7 +504,6 @@ impl TestProps {
                         ln,
                         UNSET_RUSTC_ENV,
                         testfile,
-                        line_number,
                         &mut self.unset_rustc_env,
                         |r| r.trim().to_owned(),
                     );
@@ -530,7 +511,6 @@ impl TestProps {
                         ln,
                         FORBID_OUTPUT,
                         testfile,
-                        line_number,
                         &mut self.forbid_output,
                         |r| r,
                     );
@@ -566,7 +546,7 @@ impl TestProps {
                     }
 
                     if let Some(code) = config
-                        .parse_name_value_directive(ln, FAILURE_STATUS, testfile, line_number)
+                        .parse_name_value_directive(ln, FAILURE_STATUS, testfile)
                         .and_then(|code| code.trim().parse::<i32>().ok())
                     {
                         self.failure_status = Some(code);
@@ -588,7 +568,6 @@ impl TestProps {
                         ln,
                         ASSEMBLY_OUTPUT,
                         testfile,
-                        line_number,
                         &mut self.assembly_output,
                         |r| r.trim().to_string(),
                     );
@@ -602,7 +581,7 @@ impl TestProps {
                     // Unlike the other `name_value_directive`s this needs to be handled manually,
                     // because it sets a `bool` flag.
                     if let Some(known_bug) =
-                        config.parse_name_value_directive(ln, KNOWN_BUG, testfile, line_number)
+                        config.parse_name_value_directive(ln, KNOWN_BUG, testfile)
                     {
                         let known_bug = known_bug.trim();
                         if known_bug == "unknown"
@@ -632,24 +611,20 @@ impl TestProps {
                         ln,
                         TEST_MIR_PASS,
                         testfile,
-                        line_number,
                         &mut self.mir_unit_test,
                         |s| s.trim().to_string(),
                     );
                     config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base);
 
                     if let Some(flags) =
-                        config.parse_name_value_directive(ln, LLVM_COV_FLAGS, testfile, line_number)
+                        config.parse_name_value_directive(ln, LLVM_COV_FLAGS, testfile)
                     {
                         self.llvm_cov_flags.extend(split_flags(&flags));
                     }
 
-                    if let Some(flags) = config.parse_name_value_directive(
-                        ln,
-                        FILECHECK_FLAGS,
-                        testfile,
-                        line_number,
-                    ) {
+                    if let Some(flags) =
+                        config.parse_name_value_directive(ln, FILECHECK_FLAGS, testfile)
+                    {
                         self.filecheck_flags.extend(split_flags(&flags));
                     }
 
@@ -661,7 +636,6 @@ impl TestProps {
                         ln,
                         directives::CORE_STUBS_COMPILE_FLAGS,
                         testfile,
-                        line_number,
                     ) {
                         let flags = split_flags(&flags);
                         for flag in &flags {
@@ -672,12 +646,9 @@ impl TestProps {
                         self.core_stubs_compile_flags.extend(flags);
                     }
 
-                    if let Some(err_kind) = config.parse_name_value_directive(
-                        ln,
-                        DONT_REQUIRE_ANNOTATIONS,
-                        testfile,
-                        line_number,
-                    ) {
+                    if let Some(err_kind) =
+                        config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS, testfile)
+                    {
                         self.dont_require_annotations
                             .insert(ErrorKind::expect_from_user_str(err_kind.trim()));
                     }
@@ -734,7 +705,7 @@ impl TestProps {
         }
     }
 
-    fn update_fail_mode(&mut self, ln: &str, config: &Config) {
+    fn update_fail_mode(&mut self, ln: &DirectiveLine<'_>, config: &Config) {
         let check_ui = |mode: &str| {
             // Mode::Crashes may need build-fail in order to trigger llvm errors or stack overflows
             if config.mode != TestMode::Ui && config.mode != TestMode::Crashes {
@@ -769,7 +740,12 @@ impl TestProps {
         }
     }
 
-    fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) {
+    fn update_pass_mode(
+        &mut self,
+        ln: &DirectiveLine<'_>,
+        revision: Option<&str>,
+        config: &Config,
+    ) {
         let check_no_run = |s| match (config.mode, s) {
             (TestMode::Ui, _) => (),
             (TestMode::Crashes, _) => (),
@@ -814,7 +790,7 @@ impl TestProps {
         self.pass_mode
     }
 
-    pub fn update_add_core_stubs(&mut self, ln: &str, config: &Config) {
+    fn update_add_core_stubs(&mut self, ln: &DirectiveLine<'_>, config: &Config) {
         let add_core_stubs = config.parse_name_directive(ln, directives::ADD_CORE_STUBS);
         if add_core_stubs {
             if !matches!(config.mode, TestMode::Ui | TestMode::Codegen | TestMode::Assembly) {
@@ -905,10 +881,12 @@ pub(crate) struct CheckDirectiveResult<'ln> {
     trailing_directive: Option<&'ln str>,
 }
 
-pub(crate) fn check_directive<'a>(
-    directive_ln: &'a str,
+fn check_directive<'a>(
+    directive_ln: &DirectiveLine<'a>,
     mode: TestMode,
 ) -> CheckDirectiveResult<'a> {
+    let &DirectiveLine { raw_directive: directive_ln, .. } = directive_ln;
+
     let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, ""));
 
     let is_known_directive = KNOWN_DIRECTIVE_NAMES.contains(&directive_name)
@@ -980,7 +958,7 @@ fn iter_directives(
         // Perform unknown directive check on Rust files.
         if testfile.extension() == Some("rs") {
             let CheckDirectiveResult { is_known_directive, trailing_directive } =
-                check_directive(directive_line.raw_directive, mode);
+                check_directive(&directive_line, mode);
 
             if !is_known_directive {
                 *poisoned = true;
@@ -1014,8 +992,7 @@ impl Config {
     fn parse_and_update_revisions(
         &self,
         testfile: &Utf8Path,
-        line_number: usize,
-        line: &str,
+        line: &DirectiveLine<'_>,
         existing: &mut Vec<String>,
     ) {
         const FORBIDDEN_REVISION_NAMES: [&str; 2] = [
@@ -1028,8 +1005,7 @@ impl Config {
         const FILECHECK_FORBIDDEN_REVISION_NAMES: [&str; 9] =
             ["CHECK", "COM", "NEXT", "SAME", "EMPTY", "NOT", "COUNT", "DAG", "LABEL"];
 
-        if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile, line_number)
-        {
+        if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile) {
             if self.mode == TestMode::RunMake {
                 panic!("`run-make` mode tests do not support revisions: {}", testfile);
             }
@@ -1074,13 +1050,8 @@ impl Config {
         (name.to_owned(), value.to_owned())
     }
 
-    fn parse_pp_exact(
-        &self,
-        line: &str,
-        testfile: &Utf8Path,
-        line_number: usize,
-    ) -> Option<Utf8PathBuf> {
-        if let Some(s) = self.parse_name_value_directive(line, "pp-exact", testfile, line_number) {
+    fn parse_pp_exact(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option<Utf8PathBuf> {
+        if let Some(s) = self.parse_name_value_directive(line, "pp-exact", testfile) {
             Some(Utf8PathBuf::from(&s))
         } else if self.parse_name_directive(line, "pp-exact") {
             testfile.file_name().map(Utf8PathBuf::from)
@@ -1089,7 +1060,9 @@ impl Config {
         }
     }
 
-    fn parse_custom_normalization(&self, raw_directive: &str) -> Option<NormalizeRule> {
+    fn parse_custom_normalization(&self, line: &DirectiveLine<'_>) -> Option<NormalizeRule> {
+        let &DirectiveLine { raw_directive, .. } = line;
+
         // FIXME(Zalathar): Integrate name/value splitting into `DirectiveLine`
         // instead of doing it here.
         let (directive_name, raw_value) = raw_directive.split_once(':')?;
@@ -1110,24 +1083,23 @@ impl Config {
         Some(NormalizeRule { kind, regex, replacement })
     }
 
-    fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
+    fn parse_name_directive(&self, line: &DirectiveLine<'_>, directive: &str) -> bool {
+        let &DirectiveLine { raw_directive: line, .. } = line;
+
         // Ensure the directive is a whole word. Do not match "ignore-x86" when
         // the line says "ignore-x86_64".
         line.starts_with(directive)
             && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':'))
     }
 
-    fn parse_negative_name_directive(&self, line: &str, directive: &str) -> bool {
-        line.starts_with("no-") && self.parse_name_directive(&line[3..], directive)
-    }
-
-    pub fn parse_name_value_directive(
+    fn parse_name_value_directive(
         &self,
-        line: &str,
+        line: &DirectiveLine<'_>,
         directive: &str,
         testfile: &Utf8Path,
-        line_number: usize,
     ) -> Option<String> {
+        let &DirectiveLine { line_number, raw_directive: line, .. } = line;
+
         let colon = directive.len();
         if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
             let value = line[(colon + 1)..].to_owned();
@@ -1144,52 +1116,37 @@ impl Config {
         }
     }
 
-    fn parse_edition(&self, line: &str, testfile: &Utf8Path, line_number: usize) -> Option<String> {
-        self.parse_name_value_directive(line, "edition", testfile, line_number)
+    fn parse_edition(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option<String> {
+        self.parse_name_value_directive(line, "edition", testfile)
     }
 
-    fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) {
-        match value {
-            true => {
-                if self.parse_negative_name_directive(line, directive) {
-                    *value = false;
-                }
-            }
-            false => {
-                if self.parse_name_directive(line, directive) {
-                    *value = true;
-                }
-            }
-        }
+    fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) {
+        // If the flag is already true, don't bother looking at the directive.
+        *value = *value || self.parse_name_directive(line, directive);
     }
 
     fn set_name_value_directive<T>(
         &self,
-        line: &str,
+        line: &DirectiveLine<'_>,
         directive: &str,
         testfile: &Utf8Path,
-        line_number: usize,
         value: &mut Option<T>,
         parse: impl FnOnce(String) -> T,
     ) {
         if value.is_none() {
-            *value =
-                self.parse_name_value_directive(line, directive, testfile, line_number).map(parse);
+            *value = self.parse_name_value_directive(line, directive, testfile).map(parse);
         }
     }
 
     fn push_name_value_directive<T>(
         &self,
-        line: &str,
+        line: &DirectiveLine<'_>,
         directive: &str,
         testfile: &Utf8Path,
-        line_number: usize,
         values: &mut Vec<T>,
         parse: impl FnOnce(String) -> T,
     ) {
-        if let Some(value) =
-            self.parse_name_value_directive(line, directive, testfile, line_number).map(parse)
-        {
+        if let Some(value) = self.parse_name_value_directive(line, directive, testfile).map(parse) {
             values.push(value);
         }
     }
@@ -1482,8 +1439,8 @@ pub(crate) fn make_test_description<R: Read>(
         &mut local_poisoned,
         path,
         src,
-        &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| {
-            if !directive.applies_to_test_revision(test_revision) {
+        &mut |ref ln @ DirectiveLine { line_number, .. }| {
+            if !ln.applies_to_test_revision(test_revision) {
                 return;
             }
 
@@ -1507,9 +1464,9 @@ pub(crate) fn make_test_description<R: Read>(
             decision!(cfg::handle_ignore(config, ln));
             decision!(cfg::handle_only(config, ln));
             decision!(needs::handle_needs(&cache.needs, config, ln));
-            decision!(ignore_llvm(config, path, ln, line_number));
-            decision!(ignore_backends(config, path, ln, line_number));
-            decision!(needs_backends(config, path, ln, line_number));
+            decision!(ignore_llvm(config, path, ln));
+            decision!(ignore_backends(config, path, ln));
+            decision!(needs_backends(config, path, ln));
             decision!(ignore_cdb(config, ln));
             decision!(ignore_gdb(config, ln));
             decision!(ignore_lldb(config, ln));
@@ -1549,7 +1506,9 @@ pub(crate) fn make_test_description<R: Read>(
     }
 }
 
-fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision {
+fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     if config.debugger != Some(Debugger::Cdb) {
         return IgnoreDecision::Continue;
     }
@@ -1572,7 +1531,9 @@ fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision {
     IgnoreDecision::Continue
 }
 
-fn ignore_gdb(config: &Config, line: &str) -> IgnoreDecision {
+fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     if config.debugger != Some(Debugger::Gdb) {
         return IgnoreDecision::Continue;
     }
@@ -1620,7 +1581,9 @@ fn ignore_gdb(config: &Config, line: &str) -> IgnoreDecision {
     IgnoreDecision::Continue
 }
 
-fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision {
+fn ignore_lldb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     if config.debugger != Some(Debugger::Lldb) {
         return IgnoreDecision::Continue;
     }
@@ -1642,14 +1605,9 @@ fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision {
     IgnoreDecision::Continue
 }
 
-fn ignore_backends(
-    config: &Config,
-    path: &Utf8Path,
-    line: &str,
-    line_number: usize,
-) -> IgnoreDecision {
+fn ignore_backends(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision {
     if let Some(backends_to_ignore) =
-        config.parse_name_value_directive(line, "ignore-backends", path, line_number)
+        config.parse_name_value_directive(line, "ignore-backends", path)
     {
         for backend in backends_to_ignore.split_whitespace().map(|backend| {
             match CodegenBackend::try_from(backend) {
@@ -1669,15 +1627,8 @@ fn ignore_backends(
     IgnoreDecision::Continue
 }
 
-fn needs_backends(
-    config: &Config,
-    path: &Utf8Path,
-    line: &str,
-    line_number: usize,
-) -> IgnoreDecision {
-    if let Some(needed_backends) =
-        config.parse_name_value_directive(line, "needs-backends", path, line_number)
-    {
+fn needs_backends(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision {
+    if let Some(needed_backends) = config.parse_name_value_directive(line, "needs-backends", path) {
         if !needed_backends
             .split_whitespace()
             .map(|backend| match CodegenBackend::try_from(backend) {
@@ -1699,9 +1650,9 @@ fn needs_backends(
     IgnoreDecision::Continue
 }
 
-fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) -> IgnoreDecision {
+fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision {
     if let Some(needed_components) =
-        config.parse_name_value_directive(line, "needs-llvm-components", path, line_number)
+        config.parse_name_value_directive(line, "needs-llvm-components", path)
     {
         let components: HashSet<_> = config.llvm_components.split_whitespace().collect();
         if let Some(missing_component) = needed_components
@@ -1723,7 +1674,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
         // Note that these `min` versions will check for not just major versions.
 
         if let Some(version_string) =
-            config.parse_name_value_directive(line, "min-llvm-version", path, line_number)
+            config.parse_name_value_directive(line, "min-llvm-version", path)
         {
             let min_version = extract_llvm_version(&version_string);
             // Ignore if actual version is smaller than the minimum required version.
@@ -1735,7 +1686,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
                 };
             }
         } else if let Some(version_string) =
-            config.parse_name_value_directive(line, "max-llvm-major-version", path, line_number)
+            config.parse_name_value_directive(line, "max-llvm-major-version", path)
         {
             let max_version = extract_llvm_version(&version_string);
             // Ignore if actual major version is larger than the maximum required major version.
@@ -1749,7 +1700,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
                 };
             }
         } else if let Some(version_string) =
-            config.parse_name_value_directive(line, "min-system-llvm-version", path, line_number)
+            config.parse_name_value_directive(line, "min-system-llvm-version", path)
         {
             let min_version = extract_llvm_version(&version_string);
             // Ignore if using system LLVM and actual version
@@ -1762,7 +1713,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
                 };
             }
         } else if let Some(version_range) =
-            config.parse_name_value_directive(line, "ignore-llvm-version", path, line_number)
+            config.parse_name_value_directive(line, "ignore-llvm-version", path)
         {
             // Syntax is: "ignore-llvm-version: <version1> [- <version2>]"
             let (v_min, v_max) =
@@ -1788,7 +1739,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
                 }
             }
         } else if let Some(version_string) =
-            config.parse_name_value_directive(line, "exact-llvm-major-version", path, line_number)
+            config.parse_name_value_directive(line, "exact-llvm-major-version", path)
         {
             // Syntax is "exact-llvm-major-version: <version>"
             let version = extract_llvm_version(&version_string);
diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs
index 7c1ed2e7006..0675a6feac3 100644
--- a/src/tools/compiletest/src/directives/auxiliary.rs
+++ b/src/tools/compiletest/src/directives/auxiliary.rs
@@ -7,6 +7,7 @@ use camino::Utf8Path;
 
 use super::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE, PROC_MACRO};
 use crate::common::Config;
+use crate::directives::DirectiveLine;
 
 /// Properties parsed from `aux-*` test directives.
 #[derive(Clone, Debug, Default)]
@@ -45,40 +46,28 @@ impl AuxProps {
 /// and update [`AuxProps`] accordingly.
 pub(super) fn parse_and_update_aux(
     config: &Config,
-    ln: &str,
+    directive_line: &DirectiveLine<'_>,
     testfile: &Utf8Path,
-    line_number: usize,
     aux: &mut AuxProps,
 ) {
+    let &DirectiveLine { raw_directive: ln, .. } = directive_line;
+
     if !(ln.starts_with("aux-") || ln.starts_with("proc-macro")) {
         return;
     }
 
-    config.push_name_value_directive(ln, AUX_BUILD, testfile, line_number, &mut aux.builds, |r| {
+    let ln = directive_line;
+
+    config.push_name_value_directive(ln, AUX_BUILD, testfile, &mut aux.builds, |r| {
         r.trim().to_string()
     });
-    config.push_name_value_directive(ln, AUX_BIN, testfile, line_number, &mut aux.bins, |r| {
+    config
+        .push_name_value_directive(ln, AUX_BIN, testfile, &mut aux.bins, |r| r.trim().to_string());
+    config.push_name_value_directive(ln, AUX_CRATE, testfile, &mut aux.crates, parse_aux_crate);
+    config.push_name_value_directive(ln, PROC_MACRO, testfile, &mut aux.proc_macros, |r| {
         r.trim().to_string()
     });
-    config.push_name_value_directive(
-        ln,
-        AUX_CRATE,
-        testfile,
-        line_number,
-        &mut aux.crates,
-        parse_aux_crate,
-    );
-    config.push_name_value_directive(
-        ln,
-        PROC_MACRO,
-        testfile,
-        line_number,
-        &mut aux.proc_macros,
-        |r| r.trim().to_string(),
-    );
-    if let Some(r) =
-        config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND, testfile, line_number)
-    {
+    if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND, testfile) {
         aux.codegen_backend = Some(r.trim().to_owned());
     }
 }
diff --git a/src/tools/compiletest/src/directives/cfg.rs b/src/tools/compiletest/src/directives/cfg.rs
index 802a1d63d1f..62a4b88a33a 100644
--- a/src/tools/compiletest/src/directives/cfg.rs
+++ b/src/tools/compiletest/src/directives/cfg.rs
@@ -1,12 +1,14 @@
 use std::collections::HashSet;
 
 use crate::common::{CompareMode, Config, Debugger};
-use crate::directives::IgnoreDecision;
+use crate::directives::{DirectiveLine, IgnoreDecision};
 
 const EXTRA_ARCHS: &[&str] = &["spirv"];
 
-pub(super) fn handle_ignore(config: &Config, line: &str) -> IgnoreDecision {
+pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
     let parsed = parse_cfg_name_directive(config, line, "ignore");
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     match parsed.outcome {
         MatchOutcome::NoMatch => IgnoreDecision::Continue,
         MatchOutcome::Match => IgnoreDecision::Ignore {
@@ -21,8 +23,10 @@ pub(super) fn handle_ignore(config: &Config, line: &str) -> IgnoreDecision {
     }
 }
 
-pub(super) fn handle_only(config: &Config, line: &str) -> IgnoreDecision {
+pub(super) fn handle_only(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
     let parsed = parse_cfg_name_directive(config, line, "only");
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     match parsed.outcome {
         MatchOutcome::Match => IgnoreDecision::Continue,
         MatchOutcome::NoMatch => IgnoreDecision::Ignore {
@@ -43,9 +47,11 @@ pub(super) fn handle_only(config: &Config, line: &str) -> IgnoreDecision {
 /// or `only-windows`.
 fn parse_cfg_name_directive<'a>(
     config: &Config,
-    line: &'a str,
+    line: &'a DirectiveLine<'a>,
     prefix: &str,
 ) -> ParsedNameDirective<'a> {
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     if !line.as_bytes().starts_with(prefix.as_bytes()) {
         return ParsedNameDirective::not_a_directive();
     }
diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs
index 3b7a9478717..c8a729d8aab 100644
--- a/src/tools/compiletest/src/directives/needs.rs
+++ b/src/tools/compiletest/src/directives/needs.rs
@@ -1,10 +1,10 @@
 use crate::common::{Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer};
-use crate::directives::{IgnoreDecision, llvm_has_libzstd};
+use crate::directives::{DirectiveLine, IgnoreDecision, llvm_has_libzstd};
 
 pub(super) fn handle_needs(
     cache: &CachedNeedsConditions,
     config: &Config,
-    ln: &str,
+    ln: &DirectiveLine<'_>,
 ) -> IgnoreDecision {
     // Note that we intentionally still put the needs- prefix here to make the file show up when
     // grepping for a directive name, even though we could technically strip that.
@@ -181,6 +181,8 @@ pub(super) fn handle_needs(
         },
     ];
 
+    let &DirectiveLine { raw_directive: ln, .. } = ln;
+
     let (name, rest) = match ln.split_once([':', ' ']) {
         Some((name, rest)) => (name, Some(rest)),
         None => (ln, None),
diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs
index c8e13d44573..c7aca6d1c5a 100644
--- a/src/tools/compiletest/src/executor.rs
+++ b/src/tools/compiletest/src/executor.rs
@@ -9,8 +9,8 @@ use std::borrow::Cow;
 use std::collections::HashMap;
 use std::hash::{BuildHasherDefault, DefaultHasher};
 use std::num::NonZero;
-use std::sync::{Arc, Mutex, mpsc};
-use std::{env, hint, io, mem, panic, thread};
+use std::sync::{Arc, mpsc};
+use std::{env, hint, mem, panic, thread};
 
 use camino::Utf8PathBuf;
 
@@ -130,10 +130,6 @@ fn run_test_inner(
         panic_hook::set_capture_buf(Default::default());
     }
 
-    if let CaptureKind::Old { ref buf } = capture {
-        io::set_output_capture(Some(Arc::clone(buf)));
-    }
-
     let stdout = capture.stdout();
     let stderr = capture.stderr();
 
@@ -144,9 +140,6 @@ fn run_test_inner(
         // Forward any captured panic message to (captured) stderr.
         write!(stderr, "{panic_buf}");
     }
-    if matches!(capture, CaptureKind::Old { .. }) {
-        io::set_output_capture(None);
-    }
 
     let outcome = match (should_panic, panic_payload) {
         (ShouldPanic::No, None) | (ShouldPanic::Yes, Some(_)) => TestOutcome::Succeeded,
@@ -167,31 +160,24 @@ enum CaptureKind {
     /// runners, whose output is always captured.)
     None,
 
-    /// Use the old output-capture implementation, which relies on the unstable
-    /// library feature `#![feature(internal_output_capture)]`.
-    Old { buf: Arc<Mutex<Vec<u8>>> },
-
-    /// Use the new output-capture implementation, which only uses stable Rust.
-    New { buf: output_capture::CaptureBuf },
+    /// Capture all console output that would be printed by test runners via
+    /// their `stdout` and `stderr` trait objects, or via the custom panic hook.
+    Capture { buf: output_capture::CaptureBuf },
 }
 
 impl CaptureKind {
     fn for_config(config: &Config) -> Self {
         if config.nocapture {
             Self::None
-        } else if config.new_output_capture {
-            Self::New { buf: output_capture::CaptureBuf::new() }
         } else {
-            // Create a capure buffer for `io::set_output_capture`.
-            Self::Old { buf: Default::default() }
+            Self::Capture { buf: output_capture::CaptureBuf::new() }
         }
     }
 
     fn should_set_panic_hook(&self) -> bool {
         match self {
             Self::None => false,
-            Self::Old { .. } => true,
-            Self::New { .. } => true,
+            Self::Capture { .. } => true,
         }
     }
 
@@ -205,16 +191,15 @@ impl CaptureKind {
 
     fn capture_buf_or<'a>(&'a self, fallback: &'a dyn ConsoleOut) -> &'a dyn ConsoleOut {
         match self {
-            Self::None | Self::Old { .. } => fallback,
-            Self::New { buf } => buf,
+            Self::None => fallback,
+            Self::Capture { buf } => buf,
         }
     }
 
     fn into_inner(self) -> Option<Vec<u8>> {
         match self {
             Self::None => None,
-            Self::Old { buf } => Some(buf.lock().unwrap_or_else(|e| e.into_inner()).to_vec()),
-            Self::New { buf } => Some(buf.into_inner().into()),
+            Self::Capture { buf } => Some(buf.into_inner().into()),
         }
     }
 }
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index ce2a3d4b5fb..15e31dadf97 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -1,9 +1,4 @@
 #![crate_name = "compiletest"]
-// Needed by the "new" test executor that does not depend on libtest.
-// FIXME(Zalathar): We should be able to get rid of `internal_output_capture`,
-// by having `runtest` manually capture all of its println-like output instead.
-// That would result in compiletest being written entirely in stable Rust!
-#![feature(internal_output_capture)]
 
 #[cfg(test)]
 mod tests;
@@ -178,12 +173,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
         // FIXME: Temporarily retained so we can point users to `--no-capture`
         .optflag("", "nocapture", "")
         .optflag("", "no-capture", "don't capture stdout/stderr of tests")
-        .optopt(
-            "N",
-            "new-output-capture",
-            "enables or disables the new output-capture implementation",
-            "off|on",
-        )
         .optflag("", "profiler-runtime", "is the profiler runtime enabled for this target")
         .optflag("h", "help", "show this message")
         .reqopt("", "channel", "current Rust channel", "CHANNEL")
@@ -480,14 +469,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
         supported_crate_types: OnceLock::new(),
 
         nocapture: matches.opt_present("no-capture"),
-        new_output_capture: {
-            let value = matches
-                .opt_str("new-output-capture")
-                .or_else(|| env::var("COMPILETEST_NEW_OUTPUT_CAPTURE").ok())
-                .unwrap_or_else(|| "on".to_owned());
-            parse_bool_option(&value)
-                .unwrap_or_else(|| panic!("unknown `--new-output-capture` value `{value}` given"))
-        },
 
         nightly_branch: matches.opt_str("nightly-branch").unwrap(),
         git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(),
@@ -503,19 +484,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
     }
 }
 
-/// Parses the same set of boolean values accepted by rustc command-line arguments.
-///
-/// Accepting all of these values is more complicated than just picking one
-/// pair, but has the advantage that contributors who are used to rustc
-/// shouldn't have to think about which values are legal.
-fn parse_bool_option(value: &str) -> Option<bool> {
-    match value {
-        "off" | "no" | "n" | "false" => Some(false),
-        "on" | "yes" | "y" | "true" => Some(true),
-        _ => None,
-    }
-}
-
 pub fn opt_str(maybestr: &Option<String>) -> &str {
     match *maybestr {
         None => "(none)",
diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs
index ba824124e87..3d439e98eb7 100644
--- a/src/tools/compiletest/src/runtest/debugger.rs
+++ b/src/tools/compiletest/src/runtest/debugger.rs
@@ -4,7 +4,6 @@ use std::io::{BufRead, BufReader};
 
 use camino::{Utf8Path, Utf8PathBuf};
 
-use crate::common::Config;
 use crate::runtest::ProcRes;
 
 /// Representation of information to invoke a debugger and check its output
@@ -20,11 +19,7 @@ pub(super) struct DebuggerCommands {
 }
 
 impl DebuggerCommands {
-    pub fn parse_from(
-        file: &Utf8Path,
-        config: &Config,
-        debugger_prefix: &str,
-    ) -> Result<Self, String> {
+    pub fn parse_from(file: &Utf8Path, debugger_prefix: &str) -> Result<Self, String> {
         let command_directive = format!("{debugger_prefix}-command");
         let check_directive = format!("{debugger_prefix}-check");
 
@@ -47,14 +42,10 @@ impl DebuggerCommands {
                 continue;
             };
 
-            if let Some(command) =
-                config.parse_name_value_directive(&line, &command_directive, file, line_no)
-            {
+            if let Some(command) = parse_name_value(&line, &command_directive) {
                 commands.push(command);
             }
-            if let Some(pattern) =
-                config.parse_name_value_directive(&line, &check_directive, file, line_no)
-            {
+            if let Some(pattern) = parse_name_value(&line, &check_directive) {
                 check_lines.push((line_no, pattern));
             }
         }
@@ -114,6 +105,18 @@ impl DebuggerCommands {
     }
 }
 
+/// Split off from the main `parse_name_value_directive`, so that improvements
+/// to directive handling aren't held back by debuginfo test commands.
+fn parse_name_value(line: &str, name: &str) -> Option<String> {
+    if let Some(after_name) = line.strip_prefix(name)
+        && let Some(value) = after_name.strip_prefix(':')
+    {
+        Some(value.to_owned())
+    } else {
+        None
+    }
+}
+
 /// Check that the pattern in `check_line` applies to `line`. Returns `true` if they do match.
 fn check_single_line(line: &str, check_line: &str) -> bool {
     // Allow check lines to leave parts unspecified (e.g., uninitialized
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
index 071c0863b7e..9175a38ffa5 100644
--- a/src/tools/compiletest/src/runtest/debuginfo.rs
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -59,7 +59,7 @@ impl TestCx<'_> {
         }
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "cdb")
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "cdb")
             .unwrap_or_else(|e| self.fatal(&e));
 
         // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
@@ -130,7 +130,7 @@ impl TestCx<'_> {
     }
 
     fn run_debuginfo_gdb_test_no_opt(&self) {
-        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "gdb")
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "gdb")
             .unwrap_or_else(|e| self.fatal(&e));
         let mut cmds = dbg_cmds.commands.join("\n");
 
@@ -397,7 +397,7 @@ impl TestCx<'_> {
         }
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "lldb")
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "lldb")
             .unwrap_or_else(|e| self.fatal(&e));
 
         // Write debugger script:
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index d307636e782..412640a112c 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -1341,7 +1341,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
                     name = ecx.tcx.def_path_str(def_id),
                     krate = ecx.tcx.crate_name(def_id.krate),
                     decl_size = extern_decl_layout.size.bytes(),
-                    decl_align = extern_decl_layout.align.abi.bytes(),
+                    decl_align = extern_decl_layout.align.bytes(),
                     shim_size = info.size.bytes(),
                     shim_align = info.align.bytes(),
                 )
diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs
index b74b1d5e166..b461a24a061 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -366,6 +366,13 @@ impl Rustc {
         self
     }
 
+    pub fn split_dwarf_out_dir(&mut self, out_dir: Option<&str>) -> &mut Self {
+        if let Some(out_dir) = out_dir {
+            self.cmd.arg(format!("-Zsplit-dwarf-out-dir={out_dir}"));
+        }
+        self
+    }
+
     /// Pass the `--verbose` flag.
     pub fn verbose(&mut self) -> &mut Self {
         self.cmd.arg("--verbose");
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index 6960e230a68..275ad841f4b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -149,7 +149,7 @@ fn check_size_and_align(
 ) {
     let l = eval_goal(ra_fixture, minicore).unwrap();
     assert_eq!(l.size.bytes(), size, "size mismatch");
-    assert_eq!(l.align.abi.bytes(), align, "align mismatch");
+    assert_eq!(l.align.bytes(), align, "align mismatch");
 }
 
 #[track_caller]
@@ -161,7 +161,7 @@ fn check_size_and_align_expr(
 ) {
     let l = eval_expr(ra_fixture, minicore).unwrap();
     assert_eq!(l.size.bytes(), size, "size mismatch");
-    assert_eq!(l.align.abi.bytes(), align, "align mismatch");
+    assert_eq!(l.align.bytes(), align, "align mismatch");
 }
 
 #[track_caller]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index fa63baa5d2a..c93165a04c0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -2088,7 +2088,7 @@ impl<'db> Evaluator<'db> {
         if let Some(layout) = self.layout_cache.borrow().get(&ty.to_nextsolver(interner)) {
             return Ok(layout
                 .is_sized()
-                .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize)));
+                .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize)));
         }
         if let DefWithBodyId::VariantId(f) = locals.body.owner
             && let Some((AdtId::EnumId(e), _)) = ty.as_adt()
@@ -2107,7 +2107,7 @@ impl<'db> Evaluator<'db> {
         let layout = layout?;
         Ok(layout
             .is_sized()
-            .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize)))
+            .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize)))
     }
 
     /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should
@@ -2800,7 +2800,7 @@ impl<'db> Evaluator<'db> {
                     )?;
                     // FIXME: there is some leak here
                     let size = layout.size.bytes_usize();
-                    let addr = self.heap_allocate(size, layout.align.abi.bytes() as usize)?;
+                    let addr = self.heap_allocate(size, layout.align.bytes() as usize)?;
                     self.write_memory(addr, &result)?;
                     IntervalAndTy { interval: Interval { addr, size }, ty }
                 };
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
index bb0d1f70fbb..40d76bf42e9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -768,7 +768,7 @@ impl Evaluator<'_> {
                         "align_of generic arg is not provided".into(),
                     ));
                 };
-                let align = self.layout(ty.to_nextsolver(interner))?.align.abi.bytes();
+                let align = self.layout(ty.to_nextsolver(interner))?.align.bytes();
                 destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
             }
             "size_of_val" => {
@@ -1431,7 +1431,7 @@ impl Evaluator<'_> {
                     field_types.iter().next_back().unwrap().1.clone().substitute(Interner, subst);
                 let sized_part_size =
                     layout.fields.offset(field_types.iter().count() - 1).bytes_usize();
-                let sized_part_align = layout.align.abi.bytes() as usize;
+                let sized_part_align = layout.align.bytes() as usize;
                 let (unsized_part_size, unsized_part_align) =
                     self.size_align_of_unsized(&last_field_ty, metadata, locals)?;
                 let align = sized_part_align.max(unsized_part_align) as isize;
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 9198086e9e6..4342624dd64 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -6174,7 +6174,7 @@ impl Layout {
     }
 
     pub fn align(&self) -> u64 {
-        self.0.align.abi.bytes()
+        self.0.align.bytes()
     }
 
     pub fn niches(&self) -> Option<u128> {