about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/clean.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs30
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs265
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs38
-rw-r--r--src/bootstrap/src/core/build_steps/format.rs38
-rw-r--r--src/bootstrap/src/core/build_steps/install.rs6
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs14
-rw-r--r--src/bootstrap/src/core/build_steps/perf.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs19
-rw-r--r--src/bootstrap/src/core/build_steps/suggest.rs12
-rw-r--r--src/bootstrap/src/core/build_steps/synthetic_targets.rs6
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs120
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs17
-rw-r--r--src/bootstrap/src/core/build_steps/vendor.rs6
-rw-r--r--src/bootstrap/src/core/builder.rs17
-rw-r--r--src/bootstrap/src/core/download.rs4
-rw-r--r--src/bootstrap/src/core/metadata.rs6
-rw-r--r--src/bootstrap/src/core/sanity.rs9
-rw-r--r--src/bootstrap/src/lib.rs71
-rw-r--r--src/bootstrap/src/utils/cc_detect.rs6
-rw-r--r--src/bootstrap/src/utils/exec.rs29
-rw-r--r--src/bootstrap/src/utils/helpers.rs10
-rw-r--r--src/bootstrap/src/utils/tarball.rs2
-rw-r--r--src/tools/run-make-support/src/diff/mod.rs34
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt2
-rw-r--r--src/tools/tidy/src/style.rs36
-rw-r--r--src/tools/tidy/src/style/tests.rs19
-rw-r--r--tests/crashes/125099.rs24
-rw-r--r--tests/run-make/emit-path-unhashed/Makefile37
-rw-r--r--tests/run-make/emit-path-unhashed/rmake.rs34
-rw-r--r--tests/run-make/emit-shared-files/Makefile46
-rw-r--r--tests/run-make/emit-shared-files/rmake.rs102
-rw-r--r--tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr173
-rw-r--r--tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs51
35 files changed, 784 insertions, 507 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index a0a8e5963f1..52edffce614 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -3472,6 +3472,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     )
                 });
             }
+            // Suppress `compare_type_predicate_entailment` errors for RPITITs, since they
+            // should be implied by the parent method.
+            ObligationCauseCode::CompareImplItem { trait_item_def_id, .. }
+                if tcx.is_impl_trait_in_trait(trait_item_def_id) => {}
             ObligationCauseCode::CompareImplItem { trait_item_def_id, kind, .. } => {
                 let item_name = tcx.item_name(trait_item_def_id);
                 let msg = format!(
diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs
index 479af4af666..a4be6bc56c5 100644
--- a/src/bootstrap/src/core/build_steps/clean.rs
+++ b/src/bootstrap/src/core/build_steps/clean.rs
@@ -85,7 +85,7 @@ macro_rules! clean_crate_tree {
 
                 // NOTE: doesn't use `run_cargo` because we don't want to save a stamp file,
                 // and doesn't use `stream_cargo` to avoid passing `--message-format` which `clean` doesn't accept.
-                builder.run(cargo);
+                cargo.run(builder);
             }
         }
     )+ }
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 01b25224de4..11a7a404535 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -27,7 +27,7 @@ use crate::core::builder::crate_description;
 use crate::core::builder::Cargo;
 use crate::core::builder::{Builder, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath};
 use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection};
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::command;
 use crate::utils::helpers::{
     exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date,
 };
@@ -773,20 +773,19 @@ impl Step for StartupObjects {
             let src_file = &src_dir.join(file.to_string() + ".rs");
             let dst_file = &dst_dir.join(file.to_string() + ".o");
             if !up_to_date(src_file, dst_file) {
-                let mut cmd = BootstrapCommand::new(&builder.initial_rustc);
+                let mut cmd = command(&builder.initial_rustc);
                 cmd.env("RUSTC_BOOTSTRAP", "1");
                 if !builder.local_rebuild {
                     // a local_rebuild compiler already has stage1 features
                     cmd.arg("--cfg").arg("bootstrap");
                 }
-                builder.run(
-                    cmd.arg("--target")
-                        .arg(target.rustc_target_arg())
-                        .arg("--emit=obj")
-                        .arg("-o")
-                        .arg(dst_file)
-                        .arg(src_file),
-                );
+                cmd.arg("--target")
+                    .arg(target.rustc_target_arg())
+                    .arg("--emit=obj")
+                    .arg("-o")
+                    .arg(dst_file)
+                    .arg(src_file)
+                    .run(builder);
             }
 
             let target = sysroot_dir.join((*file).to_string() + ".o");
@@ -1488,10 +1487,10 @@ pub fn compiler_file(
     if builder.config.dry_run() {
         return PathBuf::new();
     }
-    let mut cmd = BootstrapCommand::new(compiler);
+    let mut cmd = command(compiler);
     cmd.args(builder.cflags(target, GitRepo::Rustc, c));
     cmd.arg(format!("-print-file-name={file}"));
-    let out = builder.run(cmd.capture_stdout()).stdout();
+    let out = cmd.capture_stdout().run(builder).stdout();
     PathBuf::from(out.trim())
 }
 
@@ -1836,9 +1835,8 @@ impl Step for Assemble {
             let llvm::LlvmResult { llvm_config, .. } =
                 builder.ensure(llvm::Llvm { target: target_compiler.host });
             if !builder.config.dry_run() && builder.config.llvm_tools_enabled {
-                let llvm_bin_dir = builder
-                    .run(BootstrapCommand::new(llvm_config).capture_stdout().arg("--bindir"))
-                    .stdout();
+                let llvm_bin_dir =
+                    command(llvm_config).capture_stdout().arg("--bindir").run(builder).stdout();
                 let llvm_bin_dir = Path::new(llvm_bin_dir.trim());
 
                 // Since we've already built the LLVM tools, install them to the sysroot.
@@ -2163,7 +2161,7 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path)
     }
 
     let previous_mtime = FileTime::from_last_modification_time(&path.metadata().unwrap());
-    builder.run(BootstrapCommand::new("strip").capture().arg("--strip-debug").arg(path));
+    command("strip").capture().arg("--strip-debug").arg(path).run(builder);
 
     // After running `strip`, we have to set the file modification time to what it was before,
     // otherwise we risk Cargo invalidating its fingerprint and rebuilding the world next time
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 3dc871b1ca9..7bc5405e92f 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -25,7 +25,7 @@ use crate::core::build_steps::tool::{self, Tool};
 use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::utils::channel::{self, Info};
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::{command, BootstrapCommand};
 use crate::utils::helpers::{
     exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit,
 };
@@ -180,9 +180,9 @@ fn make_win_dist(
     }
 
     //Ask gcc where it keeps its stuff
-    let mut cmd = BootstrapCommand::new(builder.cc(target));
+    let mut cmd = command(builder.cc(target));
     cmd.arg("-print-search-dirs");
-    let gcc_out = builder.run(cmd.capture_stdout()).stdout();
+    let gcc_out = cmd.capture_stdout().run(builder).stdout();
 
     let mut bin_path: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect();
     let mut lib_path = Vec::new();
@@ -1023,7 +1023,7 @@ impl Step for PlainSourceTarball {
             }
 
             // Vendor all Cargo dependencies
-            let mut cmd = BootstrapCommand::new(&builder.initial_cargo);
+            let mut cmd = command(&builder.initial_cargo);
             cmd.arg("vendor")
                 .arg("--versioned-dirs")
                 .arg("--sync")
@@ -1061,7 +1061,7 @@ impl Step for PlainSourceTarball {
             }
 
             let config = if !builder.config.dry_run() {
-                builder.run(cmd.capture()).stdout()
+                cmd.capture().run(builder).stdout()
             } else {
                 String::new()
             };
@@ -1599,14 +1599,14 @@ impl Step for Extended {
             let _ = fs::remove_dir_all(&pkg);
 
             let pkgbuild = |component: &str| {
-                let mut cmd = BootstrapCommand::new("pkgbuild");
+                let mut cmd = command("pkgbuild");
                 cmd.arg("--identifier")
                     .arg(format!("org.rust-lang.{}", component))
                     .arg("--scripts")
                     .arg(pkg.join(component))
                     .arg("--nopayload")
                     .arg(pkg.join(component).with_extension("pkg"));
-                builder.run(cmd);
+                cmd.run(builder);
             };
 
             let prepare = |name: &str| {
@@ -1636,7 +1636,7 @@ impl Step for Extended {
             builder.create_dir(&pkg.join("res"));
             builder.create(&pkg.join("res/LICENSE.txt"), &license);
             builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644);
-            let mut cmd = BootstrapCommand::new("productbuild");
+            let mut cmd = command("productbuild");
             cmd.arg("--distribution")
                 .arg(xform(&etc.join("pkg/Distribution.xml")))
                 .arg("--resources")
@@ -1649,7 +1649,7 @@ impl Step for Extended {
                 .arg("--package-path")
                 .arg(&pkg);
             let _time = timeit(builder);
-            builder.run(cmd);
+            cmd.run(builder);
         }
 
         if target.is_windows() {
@@ -1703,168 +1703,159 @@ impl Step for Extended {
             let light = wix.join("bin/light.exe");
 
             let heat_flags = ["-nologo", "-gg", "-sfrag", "-srd", "-sreg"];
-            builder.run(
-                BootstrapCommand::new(&heat)
+            command(&heat)
+                .current_dir(&exe)
+                .arg("dir")
+                .arg("rustc")
+                .args(heat_flags)
+                .arg("-cg")
+                .arg("RustcGroup")
+                .arg("-dr")
+                .arg("Rustc")
+                .arg("-var")
+                .arg("var.RustcDir")
+                .arg("-out")
+                .arg(exe.join("RustcGroup.wxs"))
+                .run(builder);
+            if built_tools.contains("rust-docs") {
+                command(&heat)
                     .current_dir(&exe)
                     .arg("dir")
-                    .arg("rustc")
+                    .arg("rust-docs")
                     .args(heat_flags)
                     .arg("-cg")
-                    .arg("RustcGroup")
+                    .arg("DocsGroup")
                     .arg("-dr")
-                    .arg("Rustc")
+                    .arg("Docs")
                     .arg("-var")
-                    .arg("var.RustcDir")
+                    .arg("var.DocsDir")
                     .arg("-out")
-                    .arg(exe.join("RustcGroup.wxs")),
-            );
-            if built_tools.contains("rust-docs") {
-                builder.run(
-                    BootstrapCommand::new(&heat)
-                        .current_dir(&exe)
-                        .arg("dir")
-                        .arg("rust-docs")
-                        .args(heat_flags)
-                        .arg("-cg")
-                        .arg("DocsGroup")
-                        .arg("-dr")
-                        .arg("Docs")
-                        .arg("-var")
-                        .arg("var.DocsDir")
-                        .arg("-out")
-                        .arg(exe.join("DocsGroup.wxs"))
-                        .arg("-t")
-                        .arg(etc.join("msi/squash-components.xsl")),
-                );
+                    .arg(exe.join("DocsGroup.wxs"))
+                    .arg("-t")
+                    .arg(etc.join("msi/squash-components.xsl"))
+                    .run(builder);
             }
-            builder.run(
-                BootstrapCommand::new(&heat)
+            command(&heat)
+                .current_dir(&exe)
+                .arg("dir")
+                .arg("cargo")
+                .args(heat_flags)
+                .arg("-cg")
+                .arg("CargoGroup")
+                .arg("-dr")
+                .arg("Cargo")
+                .arg("-var")
+                .arg("var.CargoDir")
+                .arg("-out")
+                .arg(exe.join("CargoGroup.wxs"))
+                .arg("-t")
+                .arg(etc.join("msi/remove-duplicates.xsl"))
+                .run(builder);
+            command(&heat)
+                .current_dir(&exe)
+                .arg("dir")
+                .arg("rust-std")
+                .args(heat_flags)
+                .arg("-cg")
+                .arg("StdGroup")
+                .arg("-dr")
+                .arg("Std")
+                .arg("-var")
+                .arg("var.StdDir")
+                .arg("-out")
+                .arg(exe.join("StdGroup.wxs"))
+                .run(builder);
+            if built_tools.contains("rust-analyzer") {
+                command(&heat)
                     .current_dir(&exe)
                     .arg("dir")
-                    .arg("cargo")
+                    .arg("rust-analyzer")
                     .args(heat_flags)
                     .arg("-cg")
-                    .arg("CargoGroup")
+                    .arg("RustAnalyzerGroup")
                     .arg("-dr")
-                    .arg("Cargo")
+                    .arg("RustAnalyzer")
                     .arg("-var")
-                    .arg("var.CargoDir")
+                    .arg("var.RustAnalyzerDir")
                     .arg("-out")
-                    .arg(exe.join("CargoGroup.wxs"))
+                    .arg(exe.join("RustAnalyzerGroup.wxs"))
                     .arg("-t")
-                    .arg(etc.join("msi/remove-duplicates.xsl")),
-            );
-            builder.run(
-                BootstrapCommand::new(&heat)
+                    .arg(etc.join("msi/remove-duplicates.xsl"))
+                    .run(builder);
+            }
+            if built_tools.contains("clippy") {
+                command(&heat)
                     .current_dir(&exe)
                     .arg("dir")
-                    .arg("rust-std")
+                    .arg("clippy")
                     .args(heat_flags)
                     .arg("-cg")
-                    .arg("StdGroup")
+                    .arg("ClippyGroup")
                     .arg("-dr")
-                    .arg("Std")
+                    .arg("Clippy")
                     .arg("-var")
-                    .arg("var.StdDir")
+                    .arg("var.ClippyDir")
                     .arg("-out")
-                    .arg(exe.join("StdGroup.wxs")),
-            );
-            if built_tools.contains("rust-analyzer") {
-                builder.run(
-                    BootstrapCommand::new(&heat)
-                        .current_dir(&exe)
-                        .arg("dir")
-                        .arg("rust-analyzer")
-                        .args(heat_flags)
-                        .arg("-cg")
-                        .arg("RustAnalyzerGroup")
-                        .arg("-dr")
-                        .arg("RustAnalyzer")
-                        .arg("-var")
-                        .arg("var.RustAnalyzerDir")
-                        .arg("-out")
-                        .arg(exe.join("RustAnalyzerGroup.wxs"))
-                        .arg("-t")
-                        .arg(etc.join("msi/remove-duplicates.xsl")),
-                );
-            }
-            if built_tools.contains("clippy") {
-                builder.run(
-                    BootstrapCommand::new(&heat)
-                        .current_dir(&exe)
-                        .arg("dir")
-                        .arg("clippy")
-                        .args(heat_flags)
-                        .arg("-cg")
-                        .arg("ClippyGroup")
-                        .arg("-dr")
-                        .arg("Clippy")
-                        .arg("-var")
-                        .arg("var.ClippyDir")
-                        .arg("-out")
-                        .arg(exe.join("ClippyGroup.wxs"))
-                        .arg("-t")
-                        .arg(etc.join("msi/remove-duplicates.xsl")),
-                );
+                    .arg(exe.join("ClippyGroup.wxs"))
+                    .arg("-t")
+                    .arg(etc.join("msi/remove-duplicates.xsl"))
+                    .run(builder);
             }
             if built_tools.contains("miri") {
-                builder.run(
-                    BootstrapCommand::new(&heat)
-                        .current_dir(&exe)
-                        .arg("dir")
-                        .arg("miri")
-                        .args(heat_flags)
-                        .arg("-cg")
-                        .arg("MiriGroup")
-                        .arg("-dr")
-                        .arg("Miri")
-                        .arg("-var")
-                        .arg("var.MiriDir")
-                        .arg("-out")
-                        .arg(exe.join("MiriGroup.wxs"))
-                        .arg("-t")
-                        .arg(etc.join("msi/remove-duplicates.xsl")),
-                );
-            }
-            builder.run(
-                BootstrapCommand::new(&heat)
+                command(&heat)
                     .current_dir(&exe)
                     .arg("dir")
-                    .arg("rust-analysis")
+                    .arg("miri")
                     .args(heat_flags)
                     .arg("-cg")
-                    .arg("AnalysisGroup")
+                    .arg("MiriGroup")
                     .arg("-dr")
-                    .arg("Analysis")
+                    .arg("Miri")
                     .arg("-var")
-                    .arg("var.AnalysisDir")
+                    .arg("var.MiriDir")
                     .arg("-out")
-                    .arg(exe.join("AnalysisGroup.wxs"))
+                    .arg(exe.join("MiriGroup.wxs"))
                     .arg("-t")
-                    .arg(etc.join("msi/remove-duplicates.xsl")),
-            );
+                    .arg(etc.join("msi/remove-duplicates.xsl"))
+                    .run(builder);
+            }
+            command(&heat)
+                .current_dir(&exe)
+                .arg("dir")
+                .arg("rust-analysis")
+                .args(heat_flags)
+                .arg("-cg")
+                .arg("AnalysisGroup")
+                .arg("-dr")
+                .arg("Analysis")
+                .arg("-var")
+                .arg("var.AnalysisDir")
+                .arg("-out")
+                .arg(exe.join("AnalysisGroup.wxs"))
+                .arg("-t")
+                .arg(etc.join("msi/remove-duplicates.xsl"))
+                .run(builder);
             if target.ends_with("windows-gnu") {
-                builder.run(
-                    BootstrapCommand::new(&heat)
-                        .current_dir(&exe)
-                        .arg("dir")
-                        .arg("rust-mingw")
-                        .args(heat_flags)
-                        .arg("-cg")
-                        .arg("GccGroup")
-                        .arg("-dr")
-                        .arg("Gcc")
-                        .arg("-var")
-                        .arg("var.GccDir")
-                        .arg("-out")
-                        .arg(exe.join("GccGroup.wxs")),
-                );
+                command(&heat)
+                    .current_dir(&exe)
+                    .arg("dir")
+                    .arg("rust-mingw")
+                    .args(heat_flags)
+                    .arg("-cg")
+                    .arg("GccGroup")
+                    .arg("-dr")
+                    .arg("Gcc")
+                    .arg("-var")
+                    .arg("var.GccDir")
+                    .arg("-out")
+                    .arg(exe.join("GccGroup.wxs"))
+                    .run(builder);
             }
 
             let candle = |input: &Path| {
                 let output = exe.join(input.file_stem().unwrap()).with_extension("wixobj");
                 let arch = if target.contains("x86_64") { "x64" } else { "x86" };
-                let mut cmd = BootstrapCommand::new(&candle);
+                let mut cmd = command(&candle);
                 cmd.current_dir(&exe)
                     .arg("-nologo")
                     .arg("-dRustcDir=rustc")
@@ -1893,7 +1884,7 @@ impl Step for Extended {
                 if target.ends_with("windows-gnu") {
                     cmd.arg("-dGccDir=rust-mingw");
                 }
-                builder.run(cmd);
+                cmd.run(builder);
             };
             candle(&xform(&etc.join("msi/rust.wxs")));
             candle(&etc.join("msi/ui.wxs"));
@@ -1925,7 +1916,7 @@ impl Step for Extended {
 
             builder.info(&format!("building `msi` installer with {light:?}"));
             let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple);
-            let mut cmd = BootstrapCommand::new(&light);
+            let mut cmd = command(&light);
             cmd.arg("-nologo")
                 .arg("-ext")
                 .arg("WixUIExtension")
@@ -1962,7 +1953,7 @@ impl Step for Extended {
             cmd.arg("-sice:ICE57");
 
             let _time = timeit(builder);
-            builder.run(cmd);
+            cmd.run(builder);
 
             if !builder.config.dry_run() {
                 t!(move_file(exe.join(&filename), distdir(builder).join(&filename)));
@@ -2078,13 +2069,13 @@ fn maybe_install_llvm(
     } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) =
         llvm::prebuilt_llvm_config(builder, target)
     {
-        let mut cmd = BootstrapCommand::new(llvm_config);
+        let mut cmd = command(llvm_config);
         cmd.arg("--libfiles");
         builder.verbose(|| println!("running {cmd:?}"));
         let files = if builder.config.dry_run() {
             "".into()
         } else {
-            builder.run(cmd.capture_stdout()).stdout()
+            cmd.capture_stdout().run(builder).stdout()
         };
         let build_llvm_out = &builder.llvm_out(builder.config.build);
         let target_llvm_out = &builder.llvm_out(target);
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 823e842693e..4b35d6c5d4c 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -154,7 +154,7 @@ impl<P: Step> Step for RustbookSrc<P> {
             builder.info(&format!("Rustbook ({target}) - {name}"));
             let _ = fs::remove_dir_all(&out);
 
-            builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out));
+            rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out).run(builder);
 
             for lang in &self.languages {
                 let out = out.join(lang);
@@ -162,10 +162,15 @@ impl<P: Step> Step for RustbookSrc<P> {
                 builder.info(&format!("Rustbook ({target}) - {name} - {lang}"));
                 let _ = fs::remove_dir_all(&out);
 
-                let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
-                builder.run(
-                    rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out).arg("-l").arg(lang),
-                );
+                builder
+                    .tool_cmd(Tool::Rustbook)
+                    .arg("build")
+                    .arg(&src)
+                    .arg("-d")
+                    .arg(&out)
+                    .arg("-l")
+                    .arg(lang)
+                    .run(builder);
             }
         }
 
@@ -301,7 +306,7 @@ fn invoke_rustdoc(
         cmd.arg("-Z").arg("unstable-options").arg("--disable-minification");
     }
 
-    builder.run(cmd);
+    cmd.run(builder);
 }
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -395,7 +400,7 @@ impl Step for Standalone {
             } else {
                 cmd.arg("--markdown-css").arg("rust.css");
             }
-            builder.run(cmd);
+            cmd.run(builder);
         }
 
         // We open doc/index.html as the default if invoked as `x.py doc --open`
@@ -494,7 +499,7 @@ impl Step for Releases {
                 cmd.arg("--disable-minification");
             }
 
-            builder.run(cmd);
+            cmd.run(builder);
         }
 
         // We open doc/RELEASES.html as the default if invoked as `x.py doc --open RELEASES.md`
@@ -738,7 +743,7 @@ fn doc_std(
         format!("library{} in {} format", crate_description(requested_crates), format.as_str());
     let _guard = builder.msg_doc(compiler, description, target);
 
-    builder.run(cargo.into_cmd());
+    cargo.into_cmd().run(builder);
     builder.cp_link_r(&out_dir, out);
 }
 
@@ -863,7 +868,7 @@ impl Step for Rustc {
         let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc");
         symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
 
-        builder.run(cargo.into_cmd());
+        cargo.into_cmd().run(builder);
 
         if !builder.config.dry_run() {
             // Sanity check on linked compiler crates
@@ -996,7 +1001,7 @@ macro_rules! tool_doc {
                 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
 
                 let _guard = builder.msg_doc(compiler, stringify!($tool).to_lowercase(), target);
-                builder.run(cargo.into_cmd());
+                cargo.into_cmd().run(builder);
 
                 if !builder.config.dry_run() {
                     // Sanity check on linked doc directories
@@ -1075,12 +1080,7 @@ impl Step for ErrorIndex {
         builder.info(&format!("Documenting error index ({})", self.target));
         let out = builder.doc_out(self.target);
         t!(fs::create_dir_all(&out));
-        let mut index = tool::ErrorIndex::command(builder);
-        index.arg("html");
-        index.arg(out);
-        index.arg(&builder.version);
-
-        builder.run(index);
+        tool::ErrorIndex::command(builder).arg("html").arg(out).arg(&builder.version).run(builder);
     }
 }
 
@@ -1116,7 +1116,7 @@ impl Step for UnstableBookGen {
         cmd.arg(builder.src.join("src"));
         cmd.arg(out);
 
-        builder.run(cmd);
+        cmd.run(builder);
     }
 }
 
@@ -1211,7 +1211,7 @@ impl Step for RustcBook {
             self.compiler.host,
             self.target,
         );
-        builder.run(cmd);
+        cmd.run(builder);
         drop(doc_generator_guard);
 
         // Run rustbook/mdbook to generate the HTML pages.
diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs
index 66286a52813..d3ac2bae1e8 100644
--- a/src/bootstrap/src/core/build_steps/format.rs
+++ b/src/bootstrap/src/core/build_steps/format.rs
@@ -1,7 +1,7 @@
 //! Runs rustfmt on the repository.
 
 use crate::core::builder::Builder;
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::command;
 use crate::utils::helpers::{self, program_out_of_date, t};
 use build_helper::ci::CiEnv;
 use build_helper::git::get_git_modified_files;
@@ -54,13 +54,13 @@ fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl F
 fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, PathBuf)> {
     let stamp_file = build.out.join("rustfmt.stamp");
 
-    let mut cmd = BootstrapCommand::new(match build.initial_rustfmt() {
+    let mut cmd = command(match build.initial_rustfmt() {
         Some(p) => p,
         None => return None,
     });
     cmd.arg("--version");
 
-    let output = build.run(cmd.capture().allow_failure());
+    let output = cmd.capture().allow_failure().run(build);
     if output.is_failure() {
         return None;
     }
@@ -160,29 +160,25 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) {
         }
     }
     let git_available =
-        build.run(helpers::git(None).capture().allow_failure().arg("--version")).is_success();
+        helpers::git(None).capture().allow_failure().arg("--version").run(build).is_success();
 
     let mut adjective = None;
     if git_available {
-        let in_working_tree = build
-            .run(
-                helpers::git(Some(&build.src))
-                    .capture()
-                    .allow_failure()
-                    .arg("rev-parse")
-                    .arg("--is-inside-work-tree"),
-            )
+        let in_working_tree = helpers::git(Some(&build.src))
+            .capture()
+            .allow_failure()
+            .arg("rev-parse")
+            .arg("--is-inside-work-tree")
+            .run(build)
             .is_success();
         if in_working_tree {
-            let untracked_paths_output = build
-                .run(
-                    helpers::git(Some(&build.src))
-                        .capture_stdout()
-                        .arg("status")
-                        .arg("--porcelain")
-                        .arg("-z")
-                        .arg("--untracked-files=normal"),
-                )
+            let untracked_paths_output = helpers::git(Some(&build.src))
+                .capture_stdout()
+                .arg("status")
+                .arg("--porcelain")
+                .arg("-z")
+                .arg("--untracked-files=normal")
+                .run(build)
                 .stdout();
             let untracked_paths: Vec<_> = untracked_paths_output
                 .split_terminator('\0')
diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs
index 7ee1aca2abc..d3e9d6d7875 100644
--- a/src/bootstrap/src/core/build_steps/install.rs
+++ b/src/bootstrap/src/core/build_steps/install.rs
@@ -10,7 +10,7 @@ use std::path::{Component, Path, PathBuf};
 use crate::core::build_steps::dist;
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::core::config::{Config, TargetSelection};
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::command;
 use crate::utils::helpers::t;
 use crate::utils::tarball::GeneratedTarball;
 use crate::{Compiler, Kind};
@@ -102,7 +102,7 @@ fn install_sh(
     let empty_dir = builder.out.join("tmp/empty_dir");
     t!(fs::create_dir_all(&empty_dir));
 
-    let mut cmd = BootstrapCommand::new(SHELL);
+    let mut cmd = command(SHELL);
     cmd.current_dir(&empty_dir)
         .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
         .arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix)))
@@ -113,7 +113,7 @@ fn install_sh(
         .arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir)))
         .arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir)))
         .arg("--disable-ldconfig");
-    builder.run(cmd);
+    cmd.run(builder);
     t!(fs::remove_dir_all(&empty_dir));
 }
 
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index 4f1c1f87d1c..bff731e13b0 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -24,7 +24,7 @@ use crate::utils::helpers::{
 };
 use crate::{generate_smart_stamp_hash, CLang, GitRepo, Kind};
 
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::command;
 use build_helper::ci::CiEnv;
 use build_helper::git::get_git_merge_base;
 
@@ -478,9 +478,8 @@ impl Step for Llvm {
             let LlvmResult { llvm_config, .. } =
                 builder.ensure(Llvm { target: builder.config.build });
             if !builder.config.dry_run() {
-                let llvm_bindir = builder
-                    .run(BootstrapCommand::new(&llvm_config).capture_stdout().arg("--bindir"))
-                    .stdout();
+                let llvm_bindir =
+                    command(&llvm_config).capture_stdout().arg("--bindir").run(builder).stdout();
                 let host_bin = Path::new(llvm_bindir.trim());
                 cfg.define(
                     "LLVM_TABLEGEN",
@@ -530,8 +529,8 @@ impl Step for Llvm {
 
         // Helper to find the name of LLVM's shared library on darwin and linux.
         let find_llvm_lib_name = |extension| {
-            let cmd = BootstrapCommand::new(&res.llvm_config);
-            let version = builder.run(cmd.capture_stdout().arg("--version")).stdout();
+            let version =
+                command(&res.llvm_config).capture_stdout().arg("--version").run(builder).stdout();
             let major = version.split('.').next().unwrap();
 
             match &llvm_version_suffix {
@@ -587,8 +586,7 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
         return;
     }
 
-    let cmd = BootstrapCommand::new(llvm_config);
-    let version = builder.run(cmd.capture_stdout().arg("--version")).stdout();
+    let version = command(llvm_config).capture_stdout().arg("--version").run(builder).stdout();
     let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
     if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) {
         if major >= 17 {
diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs
index f41b5fe10f1..5b83080a326 100644
--- a/src/bootstrap/src/core/build_steps/perf.rs
+++ b/src/bootstrap/src/core/build_steps/perf.rs
@@ -31,5 +31,5 @@ Consider setting `rust.debuginfo-level = 1` in `config.toml`."#);
         .env("PERF_COLLECTOR", collector)
         .env("PERF_RESULT_DIR", profile_results_dir)
         .args(args);
-    builder.run(&mut cmd);
+    cmd.run(builder);
 }
diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index 316a03a2171..3a2d3f67522 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -11,7 +11,7 @@ use crate::core::build_steps::tool::{self, SourceType, Tool};
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::core::config::flags::get_completion;
 use crate::core::config::TargetSelection;
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::command;
 use crate::Mode;
 
 #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
@@ -40,8 +40,7 @@ impl Step for BuildManifest {
             panic!("\n\nfailed to specify `dist.upload-addr` in `config.toml`\n\n")
         });
 
-        let today =
-            builder.run(BootstrapCommand::new("date").capture_stdout().arg("+%Y-%m-%d")).stdout();
+        let today = command("date").capture_stdout().arg("+%Y-%m-%d").run(builder).stdout();
 
         cmd.arg(sign);
         cmd.arg(distdir(builder));
@@ -50,7 +49,7 @@ impl Step for BuildManifest {
         cmd.arg(&builder.config.channel);
 
         builder.create_dir(&distdir(builder));
-        builder.run(cmd);
+        cmd.run(builder);
     }
 }
 
@@ -72,7 +71,7 @@ impl Step for BumpStage0 {
     fn run(self, builder: &Builder<'_>) -> Self::Output {
         let mut cmd = builder.tool_cmd(Tool::BumpStage0);
         cmd.args(builder.config.args());
-        builder.run(cmd);
+        cmd.run(builder);
     }
 }
 
@@ -94,7 +93,7 @@ impl Step for ReplaceVersionPlaceholder {
     fn run(self, builder: &Builder<'_>) -> Self::Output {
         let mut cmd = builder.tool_cmd(Tool::ReplaceVersionPlaceholder);
         cmd.arg(&builder.src);
-        builder.run(cmd);
+        cmd.run(builder);
     }
 }
 
@@ -158,7 +157,7 @@ impl Step for Miri {
         // after another --, so this must be at the end.
         miri.args(builder.config.args());
 
-        builder.run(miri.into_cmd());
+        miri.into_cmd().run(builder);
     }
 }
 
@@ -188,7 +187,7 @@ impl Step for CollectLicenseMetadata {
         let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
         cmd.env("REUSE_EXE", reuse);
         cmd.env("DEST", &dest);
-        builder.run(cmd);
+        cmd.run(builder);
 
         dest
     }
@@ -218,7 +217,7 @@ impl Step for GenerateCopyright {
         let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
         cmd.env("LICENSE_METADATA", &license_metadata);
         cmd.env("DEST", &dest);
-        builder.run(cmd);
+        cmd.run(builder);
 
         dest
     }
@@ -242,7 +241,7 @@ impl Step for GenerateWindowsSys {
     fn run(self, builder: &Builder<'_>) {
         let mut cmd = builder.tool_cmd(Tool::GenerateWindowsSys);
         cmd.arg(&builder.src);
-        builder.run(cmd);
+        cmd.run(builder);
     }
 }
 
diff --git a/src/bootstrap/src/core/build_steps/suggest.rs b/src/bootstrap/src/core/build_steps/suggest.rs
index 5412b3c807b..2d4409d27c6 100644
--- a/src/bootstrap/src/core/build_steps/suggest.rs
+++ b/src/bootstrap/src/core/build_steps/suggest.rs
@@ -13,13 +13,11 @@ use crate::core::builder::Builder;
 pub fn suggest(builder: &Builder<'_>, run: bool) {
     let git_config = builder.config.git_config();
     let suggestions = builder
-        .run(
-            builder
-                .tool_cmd(Tool::SuggestTests)
-                .capture_stdout()
-                .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository)
-                .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch),
-        )
+        .tool_cmd(Tool::SuggestTests)
+        .capture_stdout()
+        .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository)
+        .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch)
+        .run(builder)
         .stdout();
 
     let suggestions = suggestions
diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs
index a115ba2d8b2..021680302cd 100644
--- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs
+++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs
@@ -9,7 +9,7 @@
 
 use crate::core::builder::{Builder, ShouldRun, Step};
 use crate::core::config::TargetSelection;
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::command;
 use crate::Compiler;
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -56,7 +56,7 @@ fn create_synthetic_target(
         return TargetSelection::create_synthetic(&name, path.to_str().unwrap());
     }
 
-    let mut cmd = BootstrapCommand::new(builder.rustc(compiler));
+    let mut cmd = command(builder.rustc(compiler));
     cmd.arg("--target").arg(base.rustc_target_arg());
     cmd.args(["-Zunstable-options", "--print", "target-spec-json"]);
 
@@ -64,7 +64,7 @@ fn create_synthetic_target(
     // we cannot use nightly features. So `RUSTC_BOOTSTRAP` is needed here.
     cmd.env("RUSTC_BOOTSTRAP", "1");
 
-    let output = builder.run(cmd.capture()).stdout();
+    let output = cmd.capture().run(builder).stdout();
     let mut spec: serde_json::Value = serde_json::from_slice(output.as_bytes()).unwrap();
     let spec_map = spec.as_object_mut().unwrap();
 
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index dc53bd3cfa7..7f4c4bd53df 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -26,7 +26,7 @@ use crate::core::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::flags::get_completion;
 use crate::core::config::flags::Subcommand;
 use crate::core::config::TargetSelection;
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::{command, BootstrapCommand};
 use crate::utils::helpers::{
     self, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var,
     linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date, LldThreads,
@@ -155,7 +155,7 @@ You can skip linkcheck with --skip src/tools/linkchecker"
         let _guard =
             builder.msg(Kind::Test, compiler.stage, "Linkcheck", bootstrap_host, bootstrap_host);
         let _time = helpers::timeit(builder);
-        builder.run(linkchecker.delay_failure().arg(builder.out.join(host.triple).join("doc")));
+        linkchecker.delay_failure().arg(builder.out.join(host.triple).join("doc")).run(builder);
     }
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -212,9 +212,11 @@ impl Step for HtmlCheck {
             builder,
         ));
 
-        builder.run(
-            builder.tool_cmd(Tool::HtmlChecker).delay_failure().arg(builder.doc_out(self.target)),
-        );
+        builder
+            .tool_cmd(Tool::HtmlChecker)
+            .delay_failure()
+            .arg(builder.doc_out(self.target))
+            .run(builder);
     }
 }
 
@@ -259,7 +261,7 @@ impl Step for Cargotest {
             .env("RUSTC", builder.rustc(compiler))
             .env("RUSTDOC", builder.rustdoc(compiler));
         add_rustdoc_cargo_linker_args(&mut cmd, builder, compiler.host, LldThreads::No);
-        builder.run(cmd.delay_failure());
+        cmd.delay_failure().run(builder);
     }
 }
 
@@ -460,7 +462,7 @@ impl Miri {
         let mut cargo = BootstrapCommand::from(cargo);
         let _guard =
             builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target);
-        builder.run(&mut cargo);
+        cargo.run(builder);
 
         // # Determine where Miri put its sysroot.
         // To this end, we run `cargo miri setup --print-sysroot` and capture the output.
@@ -473,7 +475,7 @@ impl Miri {
             String::new()
         } else {
             builder.verbose(|| println!("running: {cargo:?}"));
-            let stdout = builder.run(cargo.capture_stdout()).stdout();
+            let stdout = cargo.capture_stdout().run(builder).stdout();
             // Output is "<sysroot>\n".
             let sysroot = stdout.trim_end();
             builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
@@ -561,7 +563,7 @@ impl Step for Miri {
         {
             let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target);
             let _time = helpers::timeit(builder);
-            builder.run(&mut cargo);
+            cargo.run(builder);
         }
 
         // Run it again for mir-opt-level 4 to catch some miscompilations.
@@ -583,7 +585,7 @@ impl Step for Miri {
                     target,
                 );
                 let _time = helpers::timeit(builder);
-                builder.run(cargo);
+                cargo.run(builder);
             }
         }
     }
@@ -648,11 +650,11 @@ impl Step for CargoMiri {
 
         // Finally, pass test-args and run everything.
         cargo.arg("--").args(builder.config.test_args());
-        let cargo = BootstrapCommand::from(cargo);
+        let mut cargo = BootstrapCommand::from(cargo);
         {
             let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target);
             let _time = helpers::timeit(builder);
-            builder.run(cargo);
+            cargo.run(builder);
         }
     }
 }
@@ -753,7 +755,7 @@ impl Step for Clippy {
         let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host);
 
         // Clippy reports errors if it blessed the outputs
-        if builder.run(cargo.allow_failure()).is_success() {
+        if cargo.allow_failure().run(builder).is_success() {
             // The tests succeeded; nothing to do.
             return;
         }
@@ -806,7 +808,7 @@ impl Step for RustdocTheme {
             .env("RUSTC_BOOTSTRAP", "1");
         cmd.args(linker_args(builder, self.compiler.host, LldThreads::No));
 
-        builder.run(cmd.delay_failure());
+        cmd.delay_failure().run(builder);
     }
 }
 
@@ -832,7 +834,7 @@ impl Step for RustdocJSStd {
     fn run(self, builder: &Builder<'_>) {
         let nodejs =
             builder.config.nodejs.as_ref().expect("need nodejs to run rustdoc-js-std tests");
-        let mut command = BootstrapCommand::new(nodejs);
+        let mut command = command(nodejs);
         command
             .arg(builder.src.join("src/tools/rustdoc-js/tester.js"))
             .arg("--crate-name")
@@ -866,7 +868,7 @@ impl Step for RustdocJSStd {
             builder.config.build,
             self.target,
         );
-        builder.run(command);
+        command.run(builder);
     }
 }
 
@@ -908,12 +910,12 @@ fn get_browser_ui_test_version_inner(
     npm: &Path,
     global: bool,
 ) -> Option<String> {
-    let mut command = BootstrapCommand::new(npm).capture();
+    let mut command = command(npm).capture();
     command.arg("list").arg("--parseable").arg("--long").arg("--depth=0");
     if global {
         command.arg("--global");
     }
-    let lines = builder.run(command.allow_failure()).stdout();
+    let lines = command.allow_failure().run(builder).stdout();
     lines
         .lines()
         .find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))
@@ -1085,7 +1087,7 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
         }
 
         builder.info("tidy check");
-        builder.run(cmd.delay_failure());
+        cmd.delay_failure().run(builder);
 
         builder.info("x.py completions check");
         let [bash, zsh, fish, powershell] = ["x.py.sh", "x.py.zsh", "x.py.fish", "x.py.ps1"]
@@ -1292,7 +1294,7 @@ impl Step for RunMakeSupport {
             &[],
         );
 
-        builder.run(cargo.into_cmd());
+        cargo.into_cmd().run(builder);
 
         let lib_name = "librun_make_support.rlib";
         let lib = builder.tools_dir(self.compiler).join(lib_name);
@@ -1804,14 +1806,20 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         }
 
         let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb"));
-        let lldb_version = builder
-            .run(BootstrapCommand::new(&lldb_exe).capture().allow_failure().arg("--version"))
+        let lldb_version = command(&lldb_exe)
+            .capture()
+            .allow_failure()
+            .arg("--version")
+            .run(builder)
             .stdout_if_ok()
             .and_then(|v| if v.trim().is_empty() { None } else { Some(v) });
         if let Some(ref vers) = lldb_version {
             cmd.arg("--lldb-version").arg(vers);
-            let lldb_python_dir = builder
-                .run(BootstrapCommand::new(&lldb_exe).allow_failure().capture_stdout().arg("-P"))
+            let lldb_python_dir = command(&lldb_exe)
+                .allow_failure()
+                .capture_stdout()
+                .arg("-P")
+                .run(builder)
                 .stdout_if_ok()
                 .map(|p| p.lines().next().expect("lldb Python dir not found").to_string());
             if let Some(ref dir) = lldb_python_dir {
@@ -1869,11 +1877,10 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             let llvm::LlvmResult { llvm_config, .. } =
                 builder.ensure(llvm::Llvm { target: builder.config.build });
             if !builder.config.dry_run() {
-                let llvm_version = builder
-                    .run(BootstrapCommand::new(&llvm_config).capture_stdout().arg("--version"))
-                    .stdout();
+                let llvm_version =
+                    builder.run(command(&llvm_config).capture_stdout().arg("--version")).stdout();
                 let llvm_components = builder
-                    .run(BootstrapCommand::new(&llvm_config).capture_stdout().arg("--components"))
+                    .run(command(&llvm_config).capture_stdout().arg("--components"))
                     .stdout();
                 // Remove trailing newline from llvm-config output.
                 cmd.arg("--llvm-version")
@@ -1893,9 +1900,8 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             // separate compilations. We can add LLVM's library path to the
             // platform-specific environment variable as a workaround.
             if !builder.config.dry_run() && suite.ends_with("fulldeps") {
-                let llvm_libdir = builder
-                    .run(BootstrapCommand::new(&llvm_config).capture_stdout().arg("--libdir"))
-                    .stdout();
+                let llvm_libdir =
+                    builder.run(command(&llvm_config).capture_stdout().arg("--libdir")).stdout();
                 add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cmd);
             }
 
@@ -2169,9 +2175,11 @@ impl BookTest {
             compiler.host,
         );
         let _time = helpers::timeit(builder);
-        let cmd = rustbook_cmd.delay_failure();
-        let toolstate =
-            if builder.run(cmd).is_success() { ToolState::TestPass } else { ToolState::TestFail };
+        let toolstate = if rustbook_cmd.delay_failure().run(builder).is_success() {
+            ToolState::TestPass
+        } else {
+            ToolState::TestFail
+        };
         builder.save_toolstate(self.name, toolstate);
     }
 
@@ -2300,7 +2308,7 @@ impl Step for ErrorIndex {
         let guard =
             builder.msg(Kind::Test, compiler.stage, "error-index", compiler.host, compiler.host);
         let _time = helpers::timeit(builder);
-        builder.run(tool.capture());
+        tool.capture().run(builder);
         drop(guard);
         // The tests themselves need to link to std, so make sure it is
         // available.
@@ -2333,7 +2341,7 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) ->
     if !builder.config.verbose_tests {
         cmd = cmd.capture();
     }
-    builder.run(cmd).is_success()
+    cmd.run(builder).is_success()
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -2359,7 +2367,7 @@ impl Step for RustcGuide {
         let src = builder.src.join(relative_path);
         let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook).delay_failure();
         rustbook_cmd.arg("linkcheck").arg(&src);
-        let toolstate = if builder.run(rustbook_cmd).is_success() {
+        let toolstate = if rustbook_cmd.run(builder).is_success() {
             ToolState::TestPass
         } else {
             ToolState::TestFail
@@ -2863,19 +2871,19 @@ impl Step for RemoteCopyLibs {
 
         // Spawn the emulator and wait for it to come online
         let tool = builder.tool_exe(Tool::RemoteTestClient);
-        let mut cmd = BootstrapCommand::new(&tool);
+        let mut cmd = command(&tool);
         cmd.arg("spawn-emulator").arg(target.triple).arg(&server).arg(builder.tempdir());
         if let Some(rootfs) = builder.qemu_rootfs(target) {
             cmd.arg(rootfs);
         }
-        builder.run(cmd);
+        cmd.run(builder);
 
         // Push all our dylibs to the emulator
         for f in t!(builder.sysroot_libdir(compiler, target).read_dir()) {
             let f = t!(f);
             let name = f.file_name().into_string().unwrap();
             if helpers::is_dylib(&name) {
-                builder.run(BootstrapCommand::new(&tool).arg("push").arg(f.path()));
+                builder.run(command(&tool).arg("push").arg(f.path()));
             }
         }
     }
@@ -2906,22 +2914,20 @@ impl Step for Distcheck {
         builder.ensure(dist::PlainSourceTarball);
         builder.ensure(dist::Src);
 
-        let mut cmd = BootstrapCommand::new("tar");
+        let mut cmd = command("tar");
         cmd.arg("-xf")
             .arg(builder.ensure(dist::PlainSourceTarball).tarball())
             .arg("--strip-components=1")
             .current_dir(&dir);
-        builder.run(cmd);
+        cmd.run(builder);
         builder.run(
-            BootstrapCommand::new("./configure")
+            command("./configure")
                 .args(&builder.config.configure_args)
                 .arg("--enable-vendor")
                 .current_dir(&dir),
         );
         builder.run(
-            BootstrapCommand::new(helpers::make(&builder.config.build.triple))
-                .arg("check")
-                .current_dir(&dir),
+            command(helpers::make(&builder.config.build.triple)).arg("check").current_dir(&dir),
         );
 
         // Now make sure that rust-src has all of libstd's dependencies
@@ -2930,16 +2936,16 @@ impl Step for Distcheck {
         let _ = fs::remove_dir_all(&dir);
         t!(fs::create_dir_all(&dir));
 
-        let mut cmd = BootstrapCommand::new("tar");
+        let mut cmd = command("tar");
         cmd.arg("-xf")
             .arg(builder.ensure(dist::Src).tarball())
             .arg("--strip-components=1")
             .current_dir(&dir);
-        builder.run(cmd);
+        cmd.run(builder);
 
         let toml = dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml");
         builder.run(
-            BootstrapCommand::new(&builder.initial_cargo)
+            command(&builder.initial_cargo)
                 // Will read the libstd Cargo.toml
                 // which uses the unstable `public-dependency` feature.
                 .env("RUSTC_BOOTSTRAP", "1")
@@ -2968,7 +2974,7 @@ impl Step for Bootstrap {
         // Some tests require cargo submodule to be present.
         builder.build.update_submodule(Path::new("src/tools/cargo"));
 
-        let mut check_bootstrap = BootstrapCommand::new(builder.python());
+        let mut check_bootstrap = command(builder.python());
         check_bootstrap
             .args(["-m", "unittest", "bootstrap_test.py"])
             .env("BUILD_DIR", &builder.out)
@@ -2976,9 +2982,9 @@ impl Step for Bootstrap {
             .current_dir(builder.src.join("src/bootstrap/"));
         // NOTE: we intentionally don't pass test_args here because the args for unittest and cargo test are mutually incompatible.
         // Use `python -m unittest` manually if you want to pass arguments.
-        builder.run(check_bootstrap.delay_failure());
+        check_bootstrap.delay_failure().run(builder);
 
-        let mut cmd = BootstrapCommand::new(&builder.initial_cargo);
+        let mut cmd = command(&builder.initial_cargo);
         cmd.arg("test")
             .args(["--features", "bootstrap-self-test"])
             .current_dir(builder.src.join("src/bootstrap"))
@@ -3053,7 +3059,7 @@ impl Step for TierCheck {
             self.compiler.host,
             self.compiler.host,
         );
-        builder.run(BootstrapCommand::from(cargo).delay_failure());
+        BootstrapCommand::from(cargo).delay_failure().run(builder);
     }
 }
 
@@ -3129,7 +3135,7 @@ impl Step for RustInstaller {
             return;
         }
 
-        let mut cmd = BootstrapCommand::new(builder.src.join("src/tools/rust-installer/test.sh"));
+        let mut cmd = command(builder.src.join("src/tools/rust-installer/test.sh"));
         let tmpdir = testdir(builder, compiler.host).join("rust-installer");
         let _ = std::fs::remove_dir_all(&tmpdir);
         let _ = std::fs::create_dir_all(&tmpdir);
@@ -3138,7 +3144,7 @@ impl Step for RustInstaller {
         cmd.env("CARGO", &builder.initial_cargo);
         cmd.env("RUSTC", &builder.initial_rustc);
         cmd.env("TMP_DIR", &tmpdir);
-        builder.run(cmd.delay_failure());
+        cmd.delay_failure().run(builder);
     }
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -3332,7 +3338,7 @@ impl Step for CodegenCranelift {
             .arg("testsuite.extended_sysroot");
         cargo.args(builder.config.test_args());
 
-        builder.run(cargo.into_cmd());
+        cargo.into_cmd().run(builder);
     }
 }
 
@@ -3457,6 +3463,6 @@ impl Step for CodegenGCC {
             .arg("--std-tests");
         cargo.args(builder.config.test_args());
 
-        builder.run(cargo.into_cmd());
+        cargo.into_cmd().run(builder);
     }
 }
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 5fb282db90a..ad92a01bce7 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -8,7 +8,7 @@ use crate::core::builder;
 use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::utils::channel::GitInfo;
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::{command, BootstrapCommand};
 use crate::utils::helpers::{add_dylib_path, exe, t};
 use crate::Compiler;
 use crate::Mode;
@@ -438,7 +438,7 @@ impl ErrorIndex {
         // for rustc_private and libLLVM.so, and `sysroot_lib` for libstd, etc.
         let host = builder.config.build;
         let compiler = builder.compiler_for(builder.top_stage, host, host);
-        let mut cmd = BootstrapCommand::new(builder.ensure(ErrorIndex { compiler }));
+        let mut cmd = command(builder.ensure(ErrorIndex { compiler }));
         let mut dylib_paths = builder.rustc_lib_paths(compiler);
         dylib_paths.push(PathBuf::from(&builder.sysroot_libdir(compiler, compiler.host)));
         add_dylib_path(dylib_paths, &mut cmd);
@@ -602,7 +602,7 @@ impl Step for Rustdoc {
             &self.compiler.host,
             &target,
         );
-        builder.run(cargo.into_cmd());
+        cargo.into_cmd().run(builder);
 
         // Cargo adds a number of paths to the dylib search path on windows, which results in
         // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
@@ -857,7 +857,7 @@ impl Step for LlvmBitcodeLinker {
             &self.extra_features,
         );
 
-        builder.run(cargo.into_cmd());
+        cargo.into_cmd().run(builder);
 
         let tool_out = builder
             .cargo_out(self.compiler, Mode::ToolRustc, self.target)
@@ -912,21 +912,20 @@ impl Step for LibcxxVersionTool {
             }
 
             let compiler = builder.cxx(self.target).unwrap();
-            let mut cmd = BootstrapCommand::new(compiler);
+            let mut cmd = command(compiler);
 
             cmd.arg("-o")
                 .arg(&executable)
                 .arg(builder.src.join("src/tools/libcxx-version/main.cpp"));
 
-            builder.run(cmd);
+            cmd.run(builder);
 
             if !executable.exists() {
                 panic!("Something went wrong. {} is not present", executable.display());
             }
         }
 
-        let version_output =
-            builder.run(BootstrapCommand::new(executable).capture_stdout()).stdout();
+        let version_output = command(executable).capture_stdout().run(builder).stdout();
 
         let version_str = version_output.split_once("version:").unwrap().1;
         let version = version_str.trim().parse::<usize>().unwrap();
@@ -1050,7 +1049,7 @@ impl<'a> Builder<'a> {
     /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for
     /// `host`.
     pub fn tool_cmd(&self, tool: Tool) -> BootstrapCommand {
-        let mut cmd = BootstrapCommand::new(self.tool_exe(tool));
+        let mut cmd = command(self.tool_exe(tool));
         let compiler = self.compiler(0, self.config.build);
         let host = &compiler.host;
         // Prepares the `cmd` provided to be able to run the `compiler` provided.
diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs
index 0b999a24a1f..62342ee4792 100644
--- a/src/bootstrap/src/core/build_steps/vendor.rs
+++ b/src/bootstrap/src/core/build_steps/vendor.rs
@@ -1,5 +1,5 @@
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::command;
 use std::path::{Path, PathBuf};
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -27,7 +27,7 @@ impl Step for Vendor {
     }
 
     fn run(self, builder: &Builder<'_>) -> Self::Output {
-        let mut cmd = BootstrapCommand::new(&builder.initial_cargo);
+        let mut cmd = command(&builder.initial_cargo);
         cmd.arg("vendor");
 
         if self.versioned_dirs {
@@ -59,6 +59,6 @@ impl Step for Vendor {
 
         cmd.current_dir(self.root_dir);
 
-        builder.run(cmd);
+        cmd.run(builder);
     }
 }
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 4da912994c3..24bb96c6b88 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -23,7 +23,7 @@ use crate::utils::helpers::{check_cfg_arg, libdir, linker_flags, t, LldThreads};
 use crate::EXTRA_CHECK_CFGS;
 use crate::{Build, CLang, Crate, DocTests, GitRepo, Mode};
 
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::{command, BootstrapCommand};
 pub use crate::Compiler;
 
 use clap::ValueEnum;
@@ -1254,7 +1254,7 @@ impl<'a> Builder<'a> {
         if run_compiler.stage == 0 {
             // `ensure(Clippy { stage: 0 })` *builds* clippy with stage0, it doesn't use the beta clippy.
             let cargo_clippy = self.build.config.download_clippy();
-            let mut cmd = BootstrapCommand::new(cargo_clippy);
+            let mut cmd = command(cargo_clippy);
             cmd.env("CARGO", &self.initial_cargo);
             return cmd;
         }
@@ -1273,7 +1273,7 @@ impl<'a> Builder<'a> {
         let mut dylib_path = helpers::dylib_path();
         dylib_path.insert(0, self.sysroot(run_compiler).join("lib"));
 
-        let mut cmd = BootstrapCommand::new(cargo_clippy);
+        let mut cmd = command(cargo_clippy);
         cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
         cmd.env("CARGO", &self.initial_cargo);
         cmd
@@ -1295,7 +1295,7 @@ impl<'a> Builder<'a> {
             extra_features: Vec::new(),
         });
         // Invoke cargo-miri, make sure it can find miri and cargo.
-        let mut cmd = BootstrapCommand::new(cargo_miri);
+        let mut cmd = command(cargo_miri);
         cmd.env("MIRI", &miri);
         cmd.env("CARGO", &self.initial_cargo);
         // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler`,
@@ -1311,7 +1311,7 @@ impl<'a> Builder<'a> {
     }
 
     pub fn rustdoc_cmd(&self, compiler: Compiler) -> BootstrapCommand {
-        let mut cmd = BootstrapCommand::new(self.bootstrap_out.join("rustdoc"));
+        let mut cmd = command(self.bootstrap_out.join("rustdoc"));
         cmd.env("RUSTC_STAGE", compiler.stage.to_string())
             .env("RUSTC_SYSROOT", self.sysroot(compiler))
             // Note that this is *not* the sysroot_libdir because rustdoc must be linked
@@ -1365,7 +1365,7 @@ impl<'a> Builder<'a> {
             cargo = self.cargo_miri_cmd(compiler);
             cargo.arg("miri").arg(subcmd);
         } else {
-            cargo = BootstrapCommand::new(&self.initial_cargo);
+            cargo = command(&self.initial_cargo);
             cargo.arg(cmd);
         }
 
@@ -1918,9 +1918,8 @@ impl<'a> Builder<'a> {
         // platform-specific environment variable as a workaround.
         if mode == Mode::ToolRustc || mode == Mode::Codegen {
             if let Some(llvm_config) = self.llvm_config(target) {
-                let llvm_libdir = self
-                    .run(BootstrapCommand::new(llvm_config).capture_stdout().arg("--libdir"))
-                    .stdout();
+                let llvm_libdir =
+                    command(llvm_config).capture_stdout().arg("--libdir").run(self).stdout();
                 add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo);
             }
         }
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index c35398e2eb7..a7f4bb0cf14 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -11,7 +11,7 @@ use std::{
 use build_helper::ci::CiEnv;
 use xz2::bufread::XzDecoder;
 
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::{command, BootstrapCommand};
 use crate::utils::helpers::hex_encode;
 use crate::utils::helpers::{check_run, exe, move_file, program_out_of_date};
 use crate::{t, Config};
@@ -212,7 +212,7 @@ impl Config {
     fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
         println!("downloading {url}");
         // Try curl. If that fails and we are on windows, fallback to PowerShell.
-        let mut curl = BootstrapCommand::new("curl");
+        let mut curl = command("curl");
         curl.args([
             "-y",
             "30",
diff --git a/src/bootstrap/src/core/metadata.rs b/src/bootstrap/src/core/metadata.rs
index 49d17107125..b18da844014 100644
--- a/src/bootstrap/src/core/metadata.rs
+++ b/src/bootstrap/src/core/metadata.rs
@@ -2,7 +2,7 @@ use std::path::PathBuf;
 
 use serde_derive::Deserialize;
 
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::command;
 use crate::{t, Build, Crate};
 
 /// For more information, see the output of
@@ -70,7 +70,7 @@ pub fn build(build: &mut Build) {
 /// particular crate (e.g., `x build sysroot` to build library/sysroot).
 fn workspace_members(build: &Build) -> Vec<Package> {
     let collect_metadata = |manifest_path| {
-        let mut cargo = BootstrapCommand::new(&build.initial_cargo);
+        let mut cargo = command(&build.initial_cargo);
         cargo
             // Will read the libstd Cargo.toml
             // which uses the unstable `public-dependency` feature.
@@ -81,7 +81,7 @@ fn workspace_members(build: &Build) -> Vec<Package> {
             .arg("--no-deps")
             .arg("--manifest-path")
             .arg(build.src.join(manifest_path));
-        let metadata_output = build.run(cargo.capture_stdout().run_always()).stdout();
+        let metadata_output = cargo.capture_stdout().run_always().run(build).stdout();
         let Output { packages, .. } = t!(serde_json::from_str(&metadata_output));
         packages
     };
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index 78862ccb8cd..9995da3a1e5 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -23,7 +23,7 @@ use std::collections::HashSet;
 
 use crate::builder::Kind;
 use crate::core::config::Target;
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::command;
 use crate::Build;
 
 pub struct Finder {
@@ -209,9 +209,7 @@ than building it.
 
     #[cfg(not(feature = "bootstrap-self-test"))]
     let stage0_supported_target_list: HashSet<String> = crate::utils::helpers::output(
-        &mut BootstrapCommand::new(&build.config.initial_rustc)
-            .args(["--print", "target-list"])
-            .command,
+        &mut command(&build.config.initial_rustc).args(["--print", "target-list"]).command,
     )
     .lines()
     .map(|s| s.to_string())
@@ -354,8 +352,7 @@ than building it.
             // There are three builds of cmake on windows: MSVC, MinGW, and
             // Cygwin. The Cygwin build does not have generators for Visual
             // Studio, so detect that here and error.
-            let out =
-                build.run(BootstrapCommand::new("cmake").capture_stdout().arg("--help")).stdout();
+            let out = command("cmake").capture_stdout().arg("--help").run(build).stdout();
             if !out.contains("Visual Studio") {
                 panic!(
                     "
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index ffdd9bc885a..f16afb29796 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -41,7 +41,7 @@ use crate::core::builder::Kind;
 use crate::core::config::{flags, LldMode};
 use crate::core::config::{DryRun, Target};
 use crate::core::config::{LlvmLibunwind, TargetSelection};
-use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput};
+use crate::utils::exec::{command, BehaviorOnFailure, BootstrapCommand, CommandOutput};
 use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, symlink_dir};
 
 mod core;
@@ -510,9 +510,10 @@ impl Build {
         }
 
         println!("Updating submodule {}", relative_path.display());
-        self.run(
-            helpers::git(Some(&self.src)).args(["submodule", "-q", "sync"]).arg(relative_path),
-        );
+        helpers::git(Some(&self.src))
+            .args(["submodule", "-q", "sync"])
+            .arg(relative_path)
+            .run(self);
 
         // Try passing `--progress` to start, then run git again without if that fails.
         let update = |progress: bool| {
@@ -548,23 +549,25 @@ impl Build {
         };
         // NOTE: doesn't use `try_run` because this shouldn't print an error if it fails.
         if !update(true).command.status().map_or(false, |status| status.success()) {
-            self.run(update(false));
+            update(false).run(self);
         }
 
         // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
         // diff-index reports the modifications through the exit status
-        let has_local_modifications = self
-            .run(submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]))
+        let has_local_modifications = submodule_git()
+            .allow_failure()
+            .args(["diff-index", "--quiet", "HEAD"])
+            .run(self)
             .is_failure();
         if has_local_modifications {
-            self.run(submodule_git().args(["stash", "push"]));
+            submodule_git().args(["stash", "push"]).run(self);
         }
 
-        self.run(submodule_git().args(["reset", "-q", "--hard"]));
-        self.run(submodule_git().args(["clean", "-qdfx"]));
+        submodule_git().args(["reset", "-q", "--hard"]).run(self);
+        submodule_git().args(["clean", "-qdfx"]).run(self);
 
         if has_local_modifications {
-            self.run(submodule_git().args(["stash", "pop"]));
+            submodule_git().args(["stash", "pop"]).run(self);
         }
     }
 
@@ -575,14 +578,12 @@ impl Build {
         if !self.config.submodules(self.rust_info()) {
             return;
         }
-        let output = self
-            .run(
-                helpers::git(Some(&self.src))
-                    .capture()
-                    .args(["config", "--file"])
-                    .arg(self.config.src.join(".gitmodules"))
-                    .args(["--get-regexp", "path"]),
-            )
+        let output = helpers::git(Some(&self.src))
+            .capture()
+            .args(["config", "--file"])
+            .arg(self.config.src.join(".gitmodules"))
+            .args(["--get-regexp", "path"])
+            .run(self)
             .stdout();
         for line in output.lines() {
             // Look for `submodule.$name.path = $path`
@@ -860,16 +861,14 @@ impl Build {
         if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
             s.to_path_buf()
         } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
-            let llvm_bindir =
-                self.run(BootstrapCommand::new(s).capture_stdout().arg("--bindir")).stdout();
+            let llvm_bindir = command(s).capture_stdout().arg("--bindir").run(self).stdout();
             let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target));
             if filecheck.exists() {
                 filecheck
             } else {
                 // On Fedora the system LLVM installs FileCheck in the
                 // llvm subdirectory of the libdir.
-                let llvm_libdir =
-                    self.run(BootstrapCommand::new(s).capture_stdout().arg("--libdir")).stdout();
+                let llvm_libdir = command(s).capture_stdout().arg("--libdir").run(self).stdout();
                 let lib_filecheck =
                     Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target));
                 if lib_filecheck.exists() {
@@ -935,8 +934,7 @@ impl Build {
 
     /// Execute a command and return its output.
     /// This method should be used for all command executions in bootstrap.
-    fn run<C: AsMut<BootstrapCommand>>(&self, mut command: C) -> CommandOutput {
-        let command = command.as_mut();
+    fn run(&self, command: &mut BootstrapCommand) -> CommandOutput {
         if self.config.dry_run() && !command.run_always {
             return CommandOutput::default();
         }
@@ -1496,18 +1494,17 @@ impl Build {
             // Figure out how many merge commits happened since we branched off master.
             // That's our beta number!
             // (Note that we use a `..` range, not the `...` symmetric difference.)
-            self.run(
-                helpers::git(Some(&self.src))
-                    .capture()
-                    .arg("rev-list")
-                    .arg("--count")
-                    .arg("--merges")
-                    .arg(format!(
-                        "refs/remotes/origin/{}..HEAD",
-                        self.config.stage0_metadata.config.nightly_branch
-                    )),
-            )
-            .stdout()
+            helpers::git(Some(&self.src))
+                .capture()
+                .arg("rev-list")
+                .arg("--count")
+                .arg("--merges")
+                .arg(format!(
+                    "refs/remotes/origin/{}..HEAD",
+                    self.config.stage0_metadata.config.nightly_branch
+                ))
+                .run(self)
+                .stdout()
         });
         let n = count.trim().parse().unwrap();
         self.prerelease_version.set(Some(n));
diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs
index b80df545202..d6fa7fc0bbe 100644
--- a/src/bootstrap/src/utils/cc_detect.rs
+++ b/src/bootstrap/src/utils/cc_detect.rs
@@ -26,7 +26,7 @@ use std::path::{Path, PathBuf};
 use std::{env, iter};
 
 use crate::core::config::TargetSelection;
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::{command, BootstrapCommand};
 use crate::{Build, CLang, GitRepo};
 
 // The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
@@ -183,14 +183,14 @@ fn default_compiler(
             }
 
             let cmd = BootstrapCommand::from(c.to_command());
-            let output = build.run(cmd.capture_stdout().arg("--version")).stdout();
+            let output = cmd.capture_stdout().arg("--version").run(build).stdout();
             let i = output.find(" 4.")?;
             match output[i + 3..].chars().next().unwrap() {
                 '0'..='6' => {}
                 _ => return None,
             }
             let alternative = format!("e{gnu_compiler}");
-            if build.run(BootstrapCommand::new(&alternative).capture()).is_success() {
+            if command(&alternative).capture().run(build).is_success() {
                 Some(PathBuf::from(alternative))
             } else {
                 None
diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs
index 086d10c6351..ba963f52dc2 100644
--- a/src/bootstrap/src/utils/exec.rs
+++ b/src/bootstrap/src/utils/exec.rs
@@ -1,3 +1,4 @@
+use crate::Build;
 use std::ffi::OsStr;
 use std::path::Path;
 use std::process::{Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio};
@@ -108,14 +109,17 @@ impl BootstrapCommand {
         self
     }
 
+    #[must_use]
     pub fn delay_failure(self) -> Self {
         Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self }
     }
 
+    #[must_use]
     pub fn fail_fast(self) -> Self {
         Self { failure_behavior: BehaviorOnFailure::Exit, ..self }
     }
 
+    #[must_use]
     pub fn allow_failure(self) -> Self {
         Self { failure_behavior: BehaviorOnFailure::Ignore, ..self }
     }
@@ -126,21 +130,20 @@ impl BootstrapCommand {
     }
 
     /// Capture all output of the command, do not print it.
+    #[must_use]
     pub fn capture(self) -> Self {
         Self { stdout: OutputMode::Capture, stderr: OutputMode::Capture, ..self }
     }
 
     /// Capture stdout of the command, do not print it.
+    #[must_use]
     pub fn capture_stdout(self) -> Self {
         Self { stdout: OutputMode::Capture, ..self }
     }
-}
 
-/// This implementation exists to make it possible to pass both [BootstrapCommand] and
-/// `&mut BootstrapCommand` to `Build.run()`.
-impl AsMut<BootstrapCommand> for BootstrapCommand {
-    fn as_mut(&mut self) -> &mut BootstrapCommand {
-        self
+    /// Run the command, returning its output.
+    pub fn run(&mut self, builder: &Build) -> CommandOutput {
+        builder.run(self)
     }
 }
 
@@ -164,6 +167,13 @@ enum CommandStatus {
     DidNotStart,
 }
 
+/// Create a new BootstrapCommand. This is a helper function to make command creation
+/// shorter than `BootstrapCommand::new`.
+#[must_use]
+pub fn command<S: AsRef<OsStr>>(program: S) -> BootstrapCommand {
+    BootstrapCommand::new(program)
+}
+
 /// Represents the output of an executed process.
 #[allow(unused)]
 pub struct CommandOutput {
@@ -173,10 +183,12 @@ pub struct CommandOutput {
 }
 
 impl CommandOutput {
+    #[must_use]
     pub fn did_not_start() -> Self {
         Self { status: CommandStatus::DidNotStart, stdout: vec![], stderr: vec![] }
     }
 
+    #[must_use]
     pub fn is_success(&self) -> bool {
         match self.status {
             CommandStatus::Finished(status) => status.success(),
@@ -184,10 +196,12 @@ impl CommandOutput {
         }
     }
 
+    #[must_use]
     pub fn is_failure(&self) -> bool {
         !self.is_success()
     }
 
+    #[must_use]
     pub fn status(&self) -> Option<ExitStatus> {
         match self.status {
             CommandStatus::Finished(status) => Some(status),
@@ -195,14 +209,17 @@ impl CommandOutput {
         }
     }
 
+    #[must_use]
     pub fn stdout(&self) -> String {
         String::from_utf8(self.stdout.clone()).expect("Cannot parse process stdout as UTF-8")
     }
 
+    #[must_use]
     pub fn stdout_if_ok(&self) -> Option<String> {
         if self.is_success() { Some(self.stdout()) } else { None }
     }
 
+    #[must_use]
     pub fn stderr(&self) -> String {
         String::from_utf8(self.stderr.clone()).expect("Cannot parse process stderr as UTF-8")
     }
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 2575b7939b4..5dd3ba96786 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -47,7 +47,7 @@ macro_rules! t {
         }
     };
 }
-use crate::utils::exec::BootstrapCommand;
+use crate::utils::exec::{command, BootstrapCommand};
 pub use t;
 
 pub fn exe(name: &str, target: TargetSelection) -> String {
@@ -369,9 +369,9 @@ fn lld_flag_no_threads(builder: &Builder<'_>, lld_mode: LldMode, is_windows: boo
     let (windows_flag, other_flag) = LLD_NO_THREADS.get_or_init(|| {
         let newer_version = match lld_mode {
             LldMode::External => {
-                let mut cmd = BootstrapCommand::new("lld").capture_stdout();
+                let mut cmd = command("lld").capture_stdout();
                 cmd.arg("-flavor").arg("ld").arg("--version");
-                let out = builder.run(cmd).stdout();
+                let out = cmd.run(builder).stdout();
                 match (out.find(char::is_numeric), out.find('.')) {
                     (Some(b), Some(e)) => out.as_str()[b..e].parse::<i32>().ok().unwrap_or(14) > 10,
                     _ => true,
@@ -499,10 +499,10 @@ pub fn check_cfg_arg(name: &str, values: Option<&[&str]>) -> String {
 /// Whenever a git invocation is needed, this function should be preferred over
 /// manually building a git `BootstrapCommand`. This approach allows us to manage
 /// bootstrap-specific needs/hacks from a single source, rather than applying them on next to every
-/// `BootstrapCommand::new("git")`, which is painful to ensure that the required change is applied
+/// git command creation, which is painful to ensure that the required change is applied
 /// on each one of them correctly.
 pub fn git(source_dir: Option<&Path>) -> BootstrapCommand {
-    let mut git = BootstrapCommand::new("git");
+    let mut git = command("git");
 
     if let Some(source_dir) = source_dir {
         git.current_dir(source_dir);
diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs
index 1a35dc1fd38..4f0104e4bba 100644
--- a/src/bootstrap/src/utils/tarball.rs
+++ b/src/bootstrap/src/utils/tarball.rs
@@ -379,7 +379,7 @@ impl<'a> Tarball<'a> {
             cmd.args(["--override-file-mtime", timestamp.trim()]);
         }
 
-        self.builder.run(cmd);
+        cmd.run(self.builder);
 
         // Ensure there are no symbolic links in the tarball. In particular,
         // rustup-toolchain-install-master and most versions of Windows can't handle symbolic links.
diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs
index 3e0bdc1c6f6..24fa88af82e 100644
--- a/src/tools/run-make-support/src/diff/mod.rs
+++ b/src/tools/run-make-support/src/diff/mod.rs
@@ -87,9 +87,7 @@ impl Diff {
         self
     }
 
-    #[track_caller]
-    pub fn run(&mut self) {
-        self.drop_bomb.defuse();
+    fn run_common(&self) -> (&str, &str, String, String) {
         let expected = self.expected.as_ref().expect("expected text not set");
         let mut actual = self.actual.as_ref().expect("actual text not set").to_string();
         let expected_name = self.expected_name.as_ref().unwrap();
@@ -104,6 +102,14 @@ impl Diff {
             .header(expected_name, actual_name)
             .to_string();
 
+        (expected_name, actual_name, output, actual)
+    }
+
+    #[track_caller]
+    pub fn run(&mut self) {
+        self.drop_bomb.defuse();
+        let (expected_name, actual_name, output, actual) = self.run_common();
+
         if !output.is_empty() {
             // If we can bless (meaning we have a file to write into and the `RUSTC_BLESS_TEST`
             // environment variable set), then we write into the file and return.
@@ -120,4 +126,26 @@ impl Diff {
             )
         }
     }
+
+    #[track_caller]
+    pub fn run_fail(&mut self) {
+        self.drop_bomb.defuse();
+        let (expected_name, actual_name, output, actual) = self.run_common();
+
+        if output.is_empty() {
+            // If we can bless (meaning we have a file to write into and the `RUSTC_BLESS_TEST`
+            // environment variable set), then we write into the file and return.
+            if let Some(ref expected_file) = self.expected_file {
+                if std::env::var("RUSTC_BLESS_TEST").is_ok() {
+                    println!("Blessing `{}`", expected_file.display());
+                    fs_wrapper::write(expected_file, actual);
+                    return;
+                }
+            }
+            panic!(
+                "test failed: `{}` is not different from `{}`\n\n{}",
+                expected_name, actual_name, output
+            )
+        }
+    }
 }
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index ff98bd538db..31cb32d349a 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -23,8 +23,6 @@ run-make/dep-info-spaces/Makefile
 run-make/dep-info/Makefile
 run-make/dump-ice-to-disk/Makefile
 run-make/dump-mono-stats/Makefile
-run-make/emit-path-unhashed/Makefile
-run-make/emit-shared-files/Makefile
 run-make/emit-to-stdout/Makefile
 run-make/env-dep-info/Makefile
 run-make/export-executable-symbols/Makefile
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index e4d54d2a2b5..f65001e8de8 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -18,9 +18,8 @@
 // ignore-tidy-dbg
 
 use crate::walk::{filter_dirs, walk};
-use regex::RegexSet;
 use rustc_hash::FxHashMap;
-use std::{ffi::OsStr, path::Path};
+use std::{ffi::OsStr, path::Path, sync::LazyLock};
 
 #[cfg(test)]
 mod tests;
@@ -110,15 +109,32 @@ const ROOT_PROBLEMATIC_CONSTS: &[u32] = &[
     173390526, 721077,
 ];
 
+#[cfg(not(test))]
+const LETTER_DIGIT: &[(char, char)] = &[('A', '4'), ('B', '8'), ('E', '3')];
+
+#[cfg(test)]
+const LETTER_DIGIT: &[(char, char)] = &[('A', '4'), ('B', '8'), ('E', '3'), ('0', 'F')]; // use "futile" F intentionally
+
 fn generate_problematic_strings(
     consts: &[u32],
     letter_digit: &FxHashMap<char, char>,
 ) -> Vec<String> {
     generate_problems(consts, letter_digit)
-        .flat_map(|v| vec![v.to_string(), format!("{:x}", v), format!("{:X}", v)])
+        .flat_map(|v| vec![v.to_string(), format!("{:X}", v)])
         .collect()
 }
 
+static PROBLEMATIC_CONSTS_STRINGS: LazyLock<Vec<String>> = LazyLock::new(|| {
+    generate_problematic_strings(
+        ROOT_PROBLEMATIC_CONSTS,
+        &FxHashMap::from_iter(LETTER_DIGIT.iter().copied()),
+    )
+});
+
+fn contains_problematic_const(trimmed: &str) -> bool {
+    PROBLEMATIC_CONSTS_STRINGS.iter().any(|s| trimmed.to_uppercase().contains(s))
+}
+
 const INTERNAL_COMPILER_DOCS_LINE: &str = "#### This error code is internal to the compiler and will not be emitted with normal Rust code.";
 
 /// Parser states for `line_is_url`.
@@ -315,11 +331,6 @@ pub fn check(path: &Path, bad: &mut bool) {
         // We only check CSS files in rustdoc.
         path.extension().map_or(false, |e| e == "css") && !is_in(path, "src", "librustdoc")
     }
-    let problematic_consts_strings = generate_problematic_strings(
-        ROOT_PROBLEMATIC_CONSTS,
-        &[('A', '4'), ('B', '8'), ('E', '3')].iter().cloned().collect(),
-    );
-    let problematic_regex = RegexSet::new(problematic_consts_strings.as_slice()).unwrap();
 
     walk(path, skip, &mut |entry, contents| {
         let file = entry.path();
@@ -389,7 +400,6 @@ pub fn check(path: &Path, bad: &mut bool) {
         let is_test = file.components().any(|c| c.as_os_str() == "tests");
         // scanning the whole file for multiple needles at once is more efficient than
         // executing lines times needles separate searches.
-        let any_problematic_line = problematic_regex.is_match(contents);
         for (i, line) in contents.split('\n').enumerate() {
             if line.is_empty() {
                 if i == 0 {
@@ -459,12 +469,8 @@ pub fn check(path: &Path, bad: &mut bool) {
                 if trimmed.contains("//") && trimmed.contains(" XXX") {
                     err("Instead of XXX use FIXME")
                 }
-                if any_problematic_line {
-                    for s in problematic_consts_strings.iter() {
-                        if trimmed.contains(s) {
-                            err("Don't use magic numbers that spell things (consider 0x12345678)");
-                        }
-                    }
+                if contains_problematic_const(trimmed) {
+                    err("Don't use magic numbers that spell things (consider 0x12345678)");
                 }
             }
             // for now we just check libcore
diff --git a/src/tools/tidy/src/style/tests.rs b/src/tools/tidy/src/style/tests.rs
index 292e23916d2..47fec890432 100644
--- a/src/tools/tidy/src/style/tests.rs
+++ b/src/tools/tidy/src/style/tests.rs
@@ -1,17 +1,10 @@
 use super::*;
 
 #[test]
-fn test_generate_problematic_strings() {
-    let problematic_regex = RegexSet::new(
-        generate_problematic_strings(
-            ROOT_PROBLEMATIC_CONSTS,
-            &[('A', '4'), ('B', '8'), ('E', '3'), ('0', 'F')].iter().cloned().collect(), // use "futile" F intentionally
-        )
-        .as_slice(),
-    )
-    .unwrap();
-    assert!(problematic_regex.is_match("786357")); // check with no "decimal" hex digits - converted to integer
-    assert!(problematic_regex.is_match("589701")); // check with "decimal" replacements - converted to integer
-    assert!(problematic_regex.is_match("8FF85")); // check for hex display
-    assert!(!problematic_regex.is_match("1193046")); // check for non-matching value
+fn test_contains_problematic_const() {
+    assert!(contains_problematic_const("786357")); // check with no "decimal" hex digits - converted to integer
+    assert!(contains_problematic_const("589701")); // check with "decimal" replacements - converted to integer
+    assert!(contains_problematic_const("8FF85")); // check for hex display
+    assert!(contains_problematic_const("8fF85")); // check for case-alternating hex display
+    assert!(!contains_problematic_const("1193046")); // check for non-matching value
 }
diff --git a/tests/crashes/125099.rs b/tests/crashes/125099.rs
deleted file mode 100644
index bfc8c8fdcf6..00000000000
--- a/tests/crashes/125099.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-//@ known-bug: rust-lang/rust#125099
-
-pub trait ContFn<T>: Fn(T) -> Self::Future {
-    type Future;
-}
-impl<T, F> ContFn<T> for F
-where
-    F: Fn(T),
-{
-    type Future = ();
-}
-
-pub trait SeqHandler {
-    type Requires;
-    fn process<F: ContFn<Self::Requires>>() -> impl Sized;
-}
-
-pub struct ConvertToU64;
-impl SeqHandler for ConvertToU64 {
-    type Requires = u64;
-    fn process<F: ContFn<Self::Requires>>() -> impl Sized {}
-}
-
-fn main() {}
diff --git a/tests/run-make/emit-path-unhashed/Makefile b/tests/run-make/emit-path-unhashed/Makefile
deleted file mode 100644
index 611f8578140..00000000000
--- a/tests/run-make/emit-path-unhashed/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-include ../tools.mk
-
-OUT=$(TMPDIR)/emit
-
-# --emit KIND=PATH should not affect crate hash vs --emit KIND
-all: $(OUT)/a/libfoo.rlib $(OUT)/b/libfoo.rlib $(OUT)/c/libfoo.rlib \
-		$(TMPDIR)/libfoo.rlib
-	$(RUSTC) -Zls=root $(TMPDIR)/libfoo.rlib > $(TMPDIR)/base.txt
-	$(RUSTC) -Zls=root $(OUT)/a/libfoo.rlib > $(TMPDIR)/a.txt
-	$(RUSTC) -Zls=root $(OUT)/b/libfoo.rlib > $(TMPDIR)/b.txt
-	$(RUSTC) -Zls=root $(OUT)/c/libfoo.rlib > $(TMPDIR)/c.txt
-
-	diff $(TMPDIR)/base.txt $(TMPDIR)/a.txt
-	diff $(TMPDIR)/base.txt $(TMPDIR)/b.txt
-
-	# Different KIND parameters do affect hash.
-	# diff exits 1 on difference, 2 on trouble
-	diff $(TMPDIR)/base.txt $(TMPDIR)/c.txt ; test "$$?" -eq 1
-
-# Default output name
-$(TMPDIR)/libfoo.rlib: foo.rs
-	$(RUSTC) --emit link foo.rs
-
-# Output named with -o
-$(OUT)/a/libfoo.rlib: foo.rs
-	mkdir -p $(OUT)/a
-	$(RUSTC) --emit link -o $@ foo.rs
-
-# Output named with KIND=PATH
-$(OUT)/b/libfoo.rlib: foo.rs
-	mkdir -p $(OUT)/b
-	$(RUSTC) --emit link=$@ foo.rs
-
-# Output multiple kinds
-$(OUT)/c/libfoo.rlib: foo.rs
-	mkdir -p $(OUT)/c
-	$(RUSTC) --emit link=$@,metadata foo.rs
diff --git a/tests/run-make/emit-path-unhashed/rmake.rs b/tests/run-make/emit-path-unhashed/rmake.rs
new file mode 100644
index 00000000000..ce56c197588
--- /dev/null
+++ b/tests/run-make/emit-path-unhashed/rmake.rs
@@ -0,0 +1,34 @@
+// Specifying how rustc outputs a file can be done in different ways, such as
+// the output flag or the KIND=NAME syntax. However, some of these methods used
+// to result in different hashes on output files even though they yielded the
+// exact same result otherwise. This was fixed in #86045, and this test checks
+// that the hash is only modified when the output is made different, such as by
+// adding a new output type (in this test, metadata).
+// See https://github.com/rust-lang/rust/issues/86044
+
+use run_make_support::{diff, fs_wrapper, rustc};
+
+fn main() {
+    fs_wrapper::create_dir("emit");
+    fs_wrapper::create_dir("emit/a");
+    fs_wrapper::create_dir("emit/b");
+    fs_wrapper::create_dir("emit/c");
+    // The default output name.
+    rustc().emit("link").input("foo.rs").run();
+    // The output is named with the output flag.
+    rustc().emit("link").output("emit/a/libfoo.rlib").input("foo.rs").run();
+    // The output is named with link=NAME.
+    rustc().emit("link=emit/b/libfoo.rlib").input("foo.rs").run();
+    // The output is named with link=NAME, with an additional kind tacked on.
+    rustc().emit("link=emit/c/libfoo.rlib,metadata").input("foo.rs").run();
+
+    let base = rustc().arg("-Zls=root").input("libfoo.rlib").run().stdout_utf8();
+    let a = rustc().arg("-Zls=root").input("emit/a/libfoo.rlib").run().stdout_utf8();
+    let b = rustc().arg("-Zls=root").input("emit/b/libfoo.rlib").run().stdout_utf8();
+    let c = rustc().arg("-Zls=root").input("emit/c/libfoo.rlib").run().stdout_utf8();
+    // Both the output flag and link=NAME methods do not modify the hash of the output file.
+    diff().expected_text("base", &base).actual_text("a", a).run();
+    diff().expected_text("base", &base).actual_text("b", b).run();
+    // However, having multiple types of outputs does modify the hash.
+    diff().expected_text("base", &base).actual_text("c", c).run_fail();
+}
diff --git a/tests/run-make/emit-shared-files/Makefile b/tests/run-make/emit-shared-files/Makefile
deleted file mode 100644
index 27c72b00368..00000000000
--- a/tests/run-make/emit-shared-files/Makefile
+++ /dev/null
@@ -1,46 +0,0 @@
-include ../tools.mk
-
-INVOCATION_ONLY = $(TMPDIR)/invocation-only
-TOOLCHAIN_ONLY = $(TMPDIR)/toolchain-only
-ALL_SHARED = $(TMPDIR)/all-shared
-
-all: invocation-only toolchain-only all-shared
-
-invocation-only:
-	$(RUSTDOC) -Z unstable-options --emit=invocation-specific --output $(INVOCATION_ONLY) --resource-suffix=-xxx --theme y.css --extend-css z.css x.rs
-	[ -e $(INVOCATION_ONLY)/search-index-xxx.js ]
-	[ -e $(INVOCATION_ONLY)/settings.html ]
-	[ -e $(INVOCATION_ONLY)/x/all.html ]
-	[ -e $(INVOCATION_ONLY)/x/index.html ]
-	[ -e $(INVOCATION_ONLY)/theme-xxx.css ] # generated from z.css
-	! [ -e $(INVOCATION_ONLY)/storage-xxx.js ]
-	! [ -e $(INVOCATION_ONLY)/SourceSerif4-It.ttf.woff2 ]
-
-	# FIXME: this probably shouldn't have a suffix
-	[ -e $(INVOCATION_ONLY)/y-xxx.css ]
-	# FIXME: this is technically incorrect (see `write_shared`)
-	! [ -e $(INVOCATION_ONLY)/main-xxx.js ]
-
-toolchain-only:
-	$(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx --extend-css z.css x.rs
-	[ -e $(TOOLCHAIN_ONLY)/static.files/storage-*.js ]
-	[ -e $(TOOLCHAIN_ONLY)/static.files/SourceSerif4-It-*.ttf.woff2 ]
-	! [ -e $(TOOLCHAIN_ONLY)/search-index-xxx.js ]
-	! [ -e $(TOOLCHAIN_ONLY)/x/index.html ]
-	! [ -e $(TOOLCHAIN_ONLY)/theme.css ]
-
-	[ -e $(TOOLCHAIN_ONLY)/static.files/main-*.js ]
-	! [ -e $(TOOLCHAIN_ONLY)/y-xxx.css ]
-
-all-shared:
-	$(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx --extend-css z.css x.rs
-	[ -e $(ALL_SHARED)/static.files/storage-*.js ]
-	[ -e $(ALL_SHARED)/static.files/SourceSerif4-It-*.ttf.woff2 ]
-	! [ -e $(ALL_SHARED)/search-index-xxx.js ]
-	! [ -e $(ALL_SHARED)/settings.html ]
-	! [ -e $(ALL_SHARED)/x ]
-	! [ -e $(ALL_SHARED)/src ]
-	! [ -e $(ALL_SHARED)/theme.css ]
-
-	[ -e $(ALL_SHARED)/static.files/main-*.js ]
-	! [ -e $(ALL_SHARED)/y-xxx.css ]
diff --git a/tests/run-make/emit-shared-files/rmake.rs b/tests/run-make/emit-shared-files/rmake.rs
new file mode 100644
index 00000000000..33c12310246
--- /dev/null
+++ b/tests/run-make/emit-shared-files/rmake.rs
@@ -0,0 +1,102 @@
+// This test checks the functionality of one of rustdoc's unstable options,
+// the ability to specify emit restrictions with `--emit`.
+// `invocation-only` should only emit crate-specific files.
+// `toolchain-only` should only emit toolchain-specific files.
+// `all-shared` should only emit files that can be shared between crates.
+// See https://github.com/rust-lang/rust/pull/83478
+
+use run_make_support::{has_extension, has_prefix, rustdoc, shallow_find_files};
+use std::path::Path;
+
+fn main() {
+    rustdoc()
+        .arg("-Zunstable-options")
+        .arg("--emit=invocation-specific")
+        .output("invocation-only")
+        .arg("--resource-suffix=-xxx")
+        .args(&["--theme", "y.css"])
+        .args(&["--extend-css", "z.css"])
+        .input("x.rs")
+        .run();
+    assert!(Path::new("invocation-only/search-index-xxx.js").exists());
+    assert!(Path::new("invocation-only/settings.html").exists());
+    assert!(Path::new("invocation-only/x/all.html").exists());
+    assert!(Path::new("invocation-only/x/index.html").exists());
+    assert!(Path::new("invocation-only/theme-xxx.css").exists()); // generated from z.css
+    assert!(!Path::new("invocation-only/storage-xxx.js").exists());
+    assert!(!Path::new("invocation-only/SourceSerif4-It.ttf.woff2").exists());
+    // FIXME: this probably shouldn't have a suffix
+    assert!(Path::new("invocation-only/y-xxx.css").exists());
+    // FIXME: this is technically incorrect (see `write_shared`)
+    assert!(!Path::new("invocation-only/main-xxx.js").exists());
+
+    rustdoc()
+        .arg("-Zunstable-options")
+        .arg("--emit=toolchain-shared-resources")
+        .output("toolchain-only")
+        .arg("--resource-suffix=-xxx")
+        .args(&["--extend-css", "z.css"])
+        .input("x.rs")
+        .run();
+    assert_eq!(
+        shallow_find_files("toolchain-only/static.files", |path| {
+            has_prefix(path, "storage-") && has_extension(path, "js")
+        })
+        .len(),
+        1
+    );
+    assert_eq!(
+        shallow_find_files("toolchain-only/static.files", |path| {
+            has_prefix(path, "SourceSerif4-It-") && has_extension(path, "woff2")
+        })
+        .len(),
+        1
+    );
+    assert_eq!(
+        shallow_find_files("toolchain-only/static.files", |path| {
+            has_prefix(path, "main-") && has_extension(path, "js")
+        })
+        .len(),
+        1
+    );
+    assert!(!Path::new("toolchain-only/search-index-xxx.js").exists());
+    assert!(!Path::new("toolchain-only/x/index.html").exists());
+    assert!(!Path::new("toolchain-only/theme.css").exists());
+    assert!(!Path::new("toolchain-only/y-xxx.css").exists());
+
+    rustdoc()
+        .arg("-Zunstable-options")
+        .arg("--emit=toolchain-shared-resources,unversioned-shared-resources")
+        .output("all-shared")
+        .arg("--resource-suffix=-xxx")
+        .args(&["--extend-css", "z.css"])
+        .input("x.rs")
+        .run();
+    assert_eq!(
+        shallow_find_files("all-shared/static.files", |path| {
+            has_prefix(path, "storage-") && has_extension(path, "js")
+        })
+        .len(),
+        1
+    );
+    assert_eq!(
+        shallow_find_files("all-shared/static.files", |path| {
+            has_prefix(path, "SourceSerif4-It-") && has_extension(path, "woff2")
+        })
+        .len(),
+        1
+    );
+    assert!(!Path::new("all-shared/search-index-xxx.js").exists());
+    assert!(!Path::new("all-shared/settings.html").exists());
+    assert!(!Path::new("all-shared/x").exists());
+    assert!(!Path::new("all-shared/src").exists());
+    assert!(!Path::new("all-shared/theme.css").exists());
+    assert_eq!(
+        shallow_find_files("all-shared/static.files", |path| {
+            has_prefix(path, "main-") && has_extension(path, "js")
+        })
+        .len(),
+        1
+    );
+    assert!(!Path::new("all-shared/y-xxx.css").exists());
+}
diff --git a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr
new file mode 100644
index 00000000000..d71c1768a6a
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr
@@ -0,0 +1,173 @@
+error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
+  --> $DIR/false-positive-predicate-entailment-error.rs:36:5
+   |
+LL | /     fn autobatch<F>(self) -> impl Trait
+LL | |
+LL | |
+LL | |
+...  |
+LL | |     where
+LL | |         F: Callback<Self::CallbackArg>,
+   | |_______________________________________^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>`
+   |
+note: required for `F` to implement `Callback<i32>`
+  --> $DIR/false-positive-predicate-entailment-error.rs:14:21
+   |
+LL | impl<A, F: MyFn<A>> Callback<A> for F {
+   |            -------  ^^^^^^^^^^^     ^
+   |            |
+   |            unsatisfied trait bound introduced here
+help: consider further restricting this bound
+   |
+LL |         F: Callback<Self::CallbackArg> + MyFn<i32>,
+   |                                        +++++++++++
+
+error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
+  --> $DIR/false-positive-predicate-entailment-error.rs:36:30
+   |
+LL |     fn autobatch<F>(self) -> impl Trait
+   |                              ^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>`
+   |
+note: required for `F` to implement `Callback<i32>`
+  --> $DIR/false-positive-predicate-entailment-error.rs:14:21
+   |
+LL | impl<A, F: MyFn<A>> Callback<A> for F {
+   |            -------  ^^^^^^^^^^^     ^
+   |            |
+   |            unsatisfied trait bound introduced here
+note: required by a bound in `<Sender as ChannelSender>::autobatch`
+  --> $DIR/false-positive-predicate-entailment-error.rs:43:12
+   |
+LL |     fn autobatch<F>(self) -> impl Trait
+   |        --------- required by a bound in this associated function
+...
+LL |         F: Callback<Self::CallbackArg>,
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `<Sender as ChannelSender>::autobatch`
+help: consider further restricting this bound
+   |
+LL |         F: Callback<Self::CallbackArg> + MyFn<i32>,
+   |                                        +++++++++++
+
+error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
+  --> $DIR/false-positive-predicate-entailment-error.rs:36:5
+   |
+LL | /     fn autobatch<F>(self) -> impl Trait
+LL | |
+LL | |
+LL | |
+...  |
+LL | |     where
+LL | |         F: Callback<Self::CallbackArg>,
+   | |_______________________________________^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>`
+   |
+note: required for `F` to implement `Callback<i32>`
+  --> $DIR/false-positive-predicate-entailment-error.rs:14:21
+   |
+LL | impl<A, F: MyFn<A>> Callback<A> for F {
+   |            -------  ^^^^^^^^^^^     ^
+   |            |
+   |            unsatisfied trait bound introduced here
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider further restricting this bound
+   |
+LL |         F: Callback<Self::CallbackArg> + MyFn<i32>,
+   |                                        +++++++++++
+
+error[E0277]: the trait bound `F: Callback<i32>` is not satisfied
+  --> $DIR/false-positive-predicate-entailment-error.rs:43:12
+   |
+LL |         F: Callback<Self::CallbackArg>,
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>`
+   |
+note: required for `F` to implement `Callback<i32>`
+  --> $DIR/false-positive-predicate-entailment-error.rs:14:21
+   |
+LL | impl<A, F: MyFn<A>> Callback<A> for F {
+   |            -------  ^^^^^^^^^^^     ^
+   |            |
+   |            unsatisfied trait bound introduced here
+note: the requirement `F: Callback<i32>` appears on the `impl`'s method `autobatch` but not on the corresponding trait's method
+  --> $DIR/false-positive-predicate-entailment-error.rs:25:8
+   |
+LL | trait ChannelSender {
+   |       ------------- in this trait
+...
+LL |     fn autobatch<F>(self) -> impl Trait
+   |        ^^^^^^^^^ this trait's method doesn't have the requirement `F: Callback<i32>`
+help: consider further restricting this bound
+   |
+LL |         F: Callback<Self::CallbackArg> + MyFn<i32>,
+   |                                        +++++++++++
+
+error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
+  --> $DIR/false-positive-predicate-entailment-error.rs:36:30
+   |
+LL |     fn autobatch<F>(self) -> impl Trait
+   |                              ^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>`
+   |
+note: required for `F` to implement `Callback<i32>`
+  --> $DIR/false-positive-predicate-entailment-error.rs:14:21
+   |
+LL | impl<A, F: MyFn<A>> Callback<A> for F {
+   |            -------  ^^^^^^^^^^^     ^
+   |            |
+   |            unsatisfied trait bound introduced here
+
+error[E0277]: the trait bound `F: Callback<i32>` is not satisfied
+  --> $DIR/false-positive-predicate-entailment-error.rs:27:12
+   |
+LL |         F: Callback<Self::CallbackArg>;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>`
+   |
+note: required for `F` to implement `Callback<i32>`
+  --> $DIR/false-positive-predicate-entailment-error.rs:14:21
+   |
+LL | impl<A, F: MyFn<A>> Callback<A> for F {
+   |            -------  ^^^^^^^^^^^     ^
+   |            |
+   |            unsatisfied trait bound introduced here
+
+error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
+  --> $DIR/false-positive-predicate-entailment-error.rs:36:5
+   |
+LL | /     fn autobatch<F>(self) -> impl Trait
+LL | |
+LL | |
+LL | |
+...  |
+LL | |     where
+LL | |         F: Callback<Self::CallbackArg>,
+   | |_______________________________________^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>`
+   |
+note: required for `F` to implement `Callback<i32>`
+  --> $DIR/false-positive-predicate-entailment-error.rs:14:21
+   |
+LL | impl<A, F: MyFn<A>> Callback<A> for F {
+   |            -------  ^^^^^^^^^^^     ^
+   |            |
+   |            unsatisfied trait bound introduced here
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider further restricting this bound
+   |
+LL |         F: Callback<Self::CallbackArg> + MyFn<i32>,
+   |                                        +++++++++++
+
+error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied
+  --> $DIR/false-positive-predicate-entailment-error.rs:43:12
+   |
+LL |         F: Callback<Self::CallbackArg>,
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`
+   |
+note: required by a bound in `Callback`
+  --> $DIR/false-positive-predicate-entailment-error.rs:10:20
+   |
+LL | trait Callback<A>: MyFn<A, Output = Self::Ret> {
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Callback`
+help: consider further restricting this bound
+   |
+LL |         F: Callback<Self::CallbackArg> + MyFn<i32>,
+   |                                        +++++++++++
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs
new file mode 100644
index 00000000000..59fdeab9e0a
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs
@@ -0,0 +1,51 @@
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+//@[next] check-pass
+
+trait MyFn<T> {
+    type Output;
+}
+
+trait Callback<A>: MyFn<A, Output = Self::Ret> {
+    type Ret;
+}
+
+impl<A, F: MyFn<A>> Callback<A> for F {
+    type Ret = F::Output;
+}
+
+struct Thing;
+trait Trait {}
+impl Trait for Thing {}
+
+trait ChannelSender {
+    type CallbackArg;
+
+    fn autobatch<F>(self) -> impl Trait
+    where
+        F: Callback<Self::CallbackArg>;
+        //[current]~^ ERROR the trait bound `F: Callback<i32>` is not satisfied
+}
+
+struct Sender;
+
+impl ChannelSender for Sender {
+    type CallbackArg = i32;
+
+    fn autobatch<F>(self) -> impl Trait
+    //[current]~^ ERROR the trait bound `F: MyFn<i32>` is not satisfied
+    //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied
+    //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied
+    //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied
+    //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied
+    where
+        F: Callback<Self::CallbackArg>,
+        //[current]~^ ERROR the trait bound `F: Callback<i32>` is not satisfied
+        //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied
+    {
+        Thing
+    }
+}
+
+fn main() {}