diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index 20a3482aaa27f..ce1a200886478 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -146,13 +146,12 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
     }
 
     fn const_str(&self, s: &str) -> (RValue<'gcc>, RValue<'gcc>) {
-        let str_global = *self
-            .const_str_cache
-            .borrow_mut()
-            .raw_entry_mut()
-            .from_key(s)
-            .or_insert_with(|| (s.to_owned(), self.global_string(s)))
-            .1;
+        let mut const_str_cache = self.const_str_cache.borrow_mut();
+        let str_global = const_str_cache.get(s).copied().unwrap_or_else(|| {
+            let g = self.global_string(s);
+            const_str_cache.insert(s.to_owned(), g);
+            g
+        });
         let len = s.len();
         let cs = self.const_ptrcast(
             str_global.get_address(None),
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index 9d91aab72ab3c..f090597f95335 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -16,7 +16,7 @@
 #![allow(internal_features)]
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
-#![feature(rustc_private, decl_macro, never_type, trusted_len, hash_raw_entry, let_chains)]
+#![feature(rustc_private, decl_macro, never_type, trusted_len, let_chains)]
 #![allow(broken_intra_doc_links)]
 #![recursion_limit = "256"]
 #![warn(rust_2018_idioms)]
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 0621b893e7550..6eaecfa87a9c2 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -209,28 +209,24 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     }
 
     fn const_str(&self, s: &str) -> (&'ll Value, &'ll Value) {
-        let str_global = *self
-            .const_str_cache
-            .borrow_mut()
-            .raw_entry_mut()
-            .from_key(s)
-            .or_insert_with(|| {
-                let sc = self.const_bytes(s.as_bytes());
-                let sym = self.generate_local_symbol_name("str");
-                let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| {
-                    bug!("symbol `{}` is already defined", sym);
-                });
-                llvm::set_initializer(g, sc);
-                unsafe {
-                    llvm::LLVMSetGlobalConstant(g, True);
-                    llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
-                }
-                llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
-                // Cast to default address space if globals are in a different addrspace
-                let g = self.const_pointercast(g, self.type_ptr());
-                (s.to_owned(), g)
-            })
-            .1;
+        let mut const_str_cache = self.const_str_cache.borrow_mut();
+        let str_global = const_str_cache.get(s).copied().unwrap_or_else(|| {
+            let sc = self.const_bytes(s.as_bytes());
+            let sym = self.generate_local_symbol_name("str");
+            let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| {
+                bug!("symbol `{}` is already defined", sym);
+            });
+            llvm::set_initializer(g, sc);
+            unsafe {
+                llvm::LLVMSetGlobalConstant(g, True);
+                llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
+            }
+            llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
+            // Cast to default address space if globals are in a different addrspace
+            let g = self.const_pointercast(g, self.type_ptr());
+            const_str_cache.insert(s.to_owned(), g);
+            g
+        });
         let len = s.len();
         (str_global, self.const_usize(len as u64))
     }
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index c88372db4913b..8f72307eeba3d 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -12,7 +12,6 @@
 #![feature(exact_size_is_empty)]
 #![feature(extern_types)]
 #![feature(file_buffered)]
-#![feature(hash_raw_entry)]
 #![feature(if_let_guard)]
 #![feature(impl_trait_in_assoc_type)]
 #![feature(iter_intersperse)]
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 5054ae561c043..d1732e5057f29 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -3438,6 +3438,32 @@ fn add_lld_args(
     // this, `wasm-component-ld`, which is overridden if this option is passed.
     if !sess.target.is_like_wasm {
         cmd.cc_arg("-fuse-ld=lld");
+
+        // GNU ld and LLD have opposite defaults on some section garbage-collection features. For
+        // example, the somewhat popular `linkme` crate and its dependents rely in practice on this
+        // difference: when using lld, they need `-z nostart-stop-gc` to prevent encapsulation
+        // symbols and sections from being garbage-collected.
+        //
+        // More information about all this can be found in:
+        // - https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order
+        // - https://lld.llvm.org/ELF/start-stop-gc
+        //
+        // So when using lld, we restore, for now, the traditional behavior to help migration, but
+        // will remove it in the future.
+        // Since this only disables an optimization, it shouldn't create issues, but is in theory
+        // slightly suboptimal. However, it:
+        // - doesn't have any visible impact on our benchmarks
+        // - reduces the need to disable lld for the crates that depend on this
+        //
+        // Note that lld can detect some cases where this difference is relied on, and emits a
+        // dedicated error to add this link arg. We could make use of this error to emit an FCW. As
+        // of writing this, we don't do it, because lld is already enabled by default on nightly
+        // without this mitigation: no working project would see the FCW, so we do this to help
+        // stabilization.
+        //
+        // FIXME: emit an FCW if linking fails due its absence, and then remove this link-arg in the
+        // future.
+        cmd.link_arg("-znostart-stop-gc");
     }
 
     if !flavor.is_gnu() {
diff --git a/compiler/rustc_data_structures/src/captures.rs b/compiler/rustc_data_structures/src/captures.rs
deleted file mode 100644
index 677ccb31454ea..0000000000000
--- a/compiler/rustc_data_structures/src/captures.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-/// "Signaling" trait used in impl trait to tag lifetimes that you may
-/// need to capture but don't really need for other reasons.
-/// Basically a workaround; see [this comment] for details.
-///
-/// [this comment]: https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999
-pub trait Captures<'a> {}
-
-impl<'a, T: ?Sized> Captures<'a> for T {}
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index 66d3834d85784..a3b62b469196a 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -48,7 +48,6 @@ pub use rustc_index::static_assert_size;
 pub mod aligned;
 pub mod base_n;
 pub mod binary_search_util;
-pub mod captures;
 pub mod fingerprint;
 pub mod flat_map_in_place;
 pub mod flock;
diff --git a/config.example.toml b/config.example.toml
index 2e26c024865db..d27633423a935 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -425,6 +425,10 @@
 # a specific version.
 #ccache = false
 
+# List of paths to exclude from the build and test processes. 
+# For example, exclude = ["tests/ui", "src/tools/tidy"].
+#exclude = []
+
 # =============================================================================
 # General install configuration options
 # =============================================================================
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 4a071b4e1faec..e62aeb2ede0ad 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -641,11 +641,6 @@ impl Error for JoinPathsError {
 ///     None => println!("Impossible to get your home dir!"),
 /// }
 /// ```
-#[deprecated(
-    since = "1.29.0",
-    note = "This function's behavior may be unexpected on Windows. \
-            Consider using a crate from crates.io instead."
-)]
 #[must_use]
 #[stable(feature = "env", since = "1.0.0")]
 pub fn home_dir() -> Option<PathBuf> {
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 57e235c3efe1d..4314c8a0b1825 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -2857,9 +2857,11 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 ///
 /// See [`fs::remove_file`] and [`fs::remove_dir`].
 ///
-/// `remove_dir_all` will fail if `remove_dir` or `remove_file` fail on any constituent paths, including the root `path`.
-/// As a result, the directory you are deleting must exist, meaning that this function is not idempotent.
-/// Additionally, `remove_dir_all` will also fail if the `path` is not a directory.
+/// [`remove_dir_all`] will fail if [`remove_dir`] or [`remove_file`] fail on *any* constituent
+/// paths, *including* the root `path`. Consequently,
+///
+/// - The directory you are deleting *must* exist, meaning that this function is *not idempotent*.
+/// - [`remove_dir_all`] will fail if the `path` is *not* a directory.
 ///
 /// Consider ignoring the error if validating the removal is not required for your use case.
 ///
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 980ea1478e084..7f610bc88bfd7 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -2534,7 +2534,7 @@ pub trait BufRead: Read {
     fn read_line(&mut self, buf: &mut String) -> Result<usize> {
         // Note that we are not calling the `.read_until` method here, but
         // rather our hardcoded implementation. For more details as to why, see
-        // the comments in `read_to_end`.
+        // the comments in `default_read_to_string`.
         unsafe { append_to_string(buf, |b| read_until(self, b'\n', b)) }
     }
 
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index d0e0ed50ad89f..0791604dc034d 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -942,6 +942,7 @@ define_config! {
         jobs: Option<u32> = "jobs",
         compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
         ccache: Option<StringOrBool> = "ccache",
+        exclude: Option<Vec<PathBuf>> = "exclude",
     }
 }
 
@@ -1372,22 +1373,6 @@ impl Config {
             "flags.exclude" = ?flags.exclude
         );
 
-        config.skip = flags
-            .skip
-            .into_iter()
-            .chain(flags.exclude)
-            .map(|p| {
-                // Never return top-level path here as it would break `--skip`
-                // logic on rustc's internal test framework which is utilized
-                // by compiletest.
-                if cfg!(windows) {
-                    PathBuf::from(p.to_str().unwrap().replace('/', "\\"))
-                } else {
-                    p
-                }
-            })
-            .collect();
-
         #[cfg(feature = "tracing")]
         span!(
             target: "CONFIG_HANDLING",
@@ -1632,8 +1617,29 @@ impl Config {
             jobs,
             compiletest_diff_tool,
             mut ccache,
+            exclude,
         } = toml.build.unwrap_or_default();
 
+        let mut paths: Vec<PathBuf> = flags.skip.into_iter().chain(flags.exclude).collect();
+
+        if let Some(exclude) = exclude {
+            paths.extend(exclude);
+        }
+
+        config.skip = paths
+            .into_iter()
+            .map(|p| {
+                // Never return top-level path here as it would break `--skip`
+                // logic on rustc's internal test framework which is utilized
+                // by compiletest.
+                if cfg!(windows) {
+                    PathBuf::from(p.to_str().unwrap().replace('/', "\\"))
+                } else {
+                    p
+                }
+            })
+            .collect();
+
         config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0))));
 
         if let Some(file_build) = build {
diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs
index eff5e0337428c..27968bea31869 100644
--- a/src/bootstrap/src/core/config/tests.rs
+++ b/src/bootstrap/src/core/config/tests.rs
@@ -515,3 +515,17 @@ fn test_explicit_stage() {
     assert!(!config.explicit_stage_from_config);
     assert!(!config.is_explicit_stage());
 }
+
+#[test]
+fn test_exclude() {
+    let config = parse("build.exclude=[\"test/codegen\"]");
+
+    let first_excluded = config
+        .skip
+        .first()
+        .expect("Expected at least one excluded path")
+        .to_str()
+        .expect("Failed to convert excluded path to string");
+
+    assert_eq!(first_excluded, "test/codegen");
+}
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 5f49c50c5ad3a..b9f57898f4d98 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -365,4 +365,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "`rust.channel` now supports \"auto-detect\" to load the channel from `src/ci/channel`",
     },
+    ChangeInfo {
+        change_id: 137147,
+        severity: ChangeSeverity::Info,
+        summary: "New option `build.exclude` that adds support for excluding test.",
+    },
 ];
diff --git a/src/etc/test-float-parse/src/gen/exhaustive.rs b/src/etc/test-float-parse/src/gen/exhaustive.rs
index 5d4b6df8e59b9..01458fb0b6084 100644
--- a/src/etc/test-float-parse/src/gen/exhaustive.rs
+++ b/src/etc/test-float-parse/src/gen/exhaustive.rs
@@ -13,13 +13,12 @@ impl<F: Float> Generator<F> for Exhaustive<F>
 where
     RangeInclusive<F::Int>: Iterator<Item = F::Int>,
 {
-    const NAME: &'static str = "exhaustive";
     const SHORT_NAME: &'static str = "exhaustive";
 
     type WriteCtx = F;
 
     fn total_tests() -> u64 {
-        F::Int::MAX.try_into().unwrap_or(u64::MAX)
+        1u64.checked_shl(F::Int::BITS).expect("More than u64::MAX tests")
     }
 
     fn new() -> Self {
diff --git a/src/etc/test-float-parse/src/gen/fuzz.rs b/src/etc/test-float-parse/src/gen/fuzz.rs
index 0c63e8aae26ea..7fc999d167113 100644
--- a/src/etc/test-float-parse/src/gen/fuzz.rs
+++ b/src/etc/test-float-parse/src/gen/fuzz.rs
@@ -49,7 +49,6 @@ impl<F: Float> Generator<F> for Fuzz<F>
 where
     Standard: Distribution<<F as Float>::Int>,
 {
-    const NAME: &'static str = "fuzz";
     const SHORT_NAME: &'static str = "fuzz";
 
     type WriteCtx = F;
diff --git a/src/etc/test-float-parse/src/gen/sparse.rs b/src/etc/test-float-parse/src/gen/sparse.rs
index 389b71056a3e5..72b65d4ce7f10 100644
--- a/src/etc/test-float-parse/src/gen/sparse.rs
+++ b/src/etc/test-float-parse/src/gen/sparse.rs
@@ -35,7 +35,6 @@ impl<F: Float> Generator<F> for FewOnesInt<F>
 where
     <F::Int as TryFrom<u128>>::Error: std::fmt::Debug,
 {
-    const NAME: &'static str = "few ones int";
     const SHORT_NAME: &'static str = "few ones int";
 
     type WriteCtx = F::Int;
diff --git a/src/etc/test-float-parse/src/lib.rs b/src/etc/test-float-parse/src/lib.rs
index 3c71b0dc32e74..5a0c160312295 100644
--- a/src/etc/test-float-parse/src/lib.rs
+++ b/src/etc/test-float-parse/src/lib.rs
@@ -2,19 +2,19 @@ mod traits;
 mod ui;
 mod validate;
 
-use std::any::{TypeId, type_name};
+use std::any::type_name;
 use std::cmp::min;
 use std::ops::RangeInclusive;
 use std::process::ExitCode;
+use std::sync::OnceLock;
 use std::sync::atomic::{AtomicU64, Ordering};
-use std::sync::{OnceLock, mpsc};
 use std::{fmt, time};
 
-use indicatif::{MultiProgress, ProgressBar};
 use rand::distributions::{Distribution, Standard};
 use rayon::prelude::*;
 use time::{Duration, Instant};
 use traits::{Float, Generator, Int};
+use validate::CheckError;
 
 /// Test generators.
 mod gen {
@@ -43,7 +43,7 @@ const HUGE_TEST_CUTOFF: u64 = 5_000_000;
 /// Seed for tests that use a deterministic RNG.
 const SEED: [u8; 32] = *b"3.141592653589793238462643383279";
 
-/// Global configuration
+/// Global configuration.
 #[derive(Debug)]
 pub struct Config {
     pub timeout: Duration,
@@ -106,7 +106,7 @@ pub fn run(cfg: Config, include: &[String], exclude: &[String]) -> ExitCode {
 
     println!("launching");
     let elapsed = launch_tests(&mut tests, &cfg);
-    ui::finish(&tests, elapsed, &cfg)
+    ui::finish_all(&tests, elapsed, &cfg)
 }
 
 /// Enumerate tests to run but don't actually run them.
@@ -160,18 +160,18 @@ where
 #[derive(Debug)]
 pub struct TestInfo {
     pub name: String,
-    /// Tests are identified by the type ID of `(F, G)` (tuple of the float and generator type).
-    /// This gives an easy way to associate messages with tests.
-    id: TypeId,
     float_name: &'static str,
+    float_bits: u32,
     gen_name: &'static str,
     /// Name for display in the progress bar.
     short_name: String,
+    /// Pad the short name to a common width for progress bar use.
+    short_name_padded: String,
     total_tests: u64,
     /// Function to launch this test.
-    launch: fn(&mpsc::Sender<Msg>, &TestInfo, &Config),
+    launch: fn(&TestInfo, &Config),
     /// Progress bar to be updated.
-    pb: Option<ProgressBar>,
+    progress: Option<ui::Progress>,
     /// Once completed, this will be set.
     completed: OnceLock<Completed>,
 }
@@ -187,14 +187,18 @@ impl TestInfo {
         let f_name = type_name::<F>();
         let gen_name = G::NAME;
         let gen_short_name = G::SHORT_NAME;
+        let name = format!("{f_name} {gen_name}");
+        let short_name = format!("{f_name} {gen_short_name}");
+        let short_name_padded = format!("{short_name:18}");
 
         let info = TestInfo {
-            id: TypeId::of::<(F, G)>(),
             float_name: f_name,
+            float_bits: F::BITS,
             gen_name,
-            pb: None,
-            name: format!("{f_name} {gen_name}"),
-            short_name: format!("{f_name} {gen_short_name}"),
+            progress: None,
+            name,
+            short_name_padded,
+            short_name,
             launch: test_runner::<F, G>,
             total_tests: G::total_tests(),
             completed: OnceLock::new(),
@@ -202,106 +206,18 @@ impl TestInfo {
         v.push(info);
     }
 
-    /// Pad the short name to a common width for progress bar use.
-    fn short_name_padded(&self) -> String {
-        format!("{:18}", self.short_name)
-    }
-
-    /// Create a progress bar for this test within a multiprogress bar.
-    fn register_pb(&mut self, mp: &MultiProgress, drop_bars: &mut Vec<ProgressBar>) {
-        self.pb = Some(ui::create_pb(mp, self.total_tests, &self.short_name_padded(), drop_bars));
-    }
-
-    /// When the test is finished, update progress bar messages and finalize.
-    fn finalize_pb(&self, c: &Completed) {
-        let pb = self.pb.as_ref().unwrap();
-        ui::finalize_pb(pb, &self.short_name_padded(), c);
-    }
-
     /// True if this should be run after all others.
     fn is_huge_test(&self) -> bool {
         self.total_tests >= HUGE_TEST_CUTOFF
     }
-}
-
-/// A message sent from test runner threads to the UI/log thread.
-#[derive(Clone, Debug)]
-struct Msg {
-    id: TypeId,
-    update: Update,
-}
 
-impl Msg {
-    /// Wrap an `Update` into a message for the specified type. We use the `TypeId` of `(F, G)` to
-    /// identify which test a message in the channel came from.
-    fn new<F: Float, G: Generator<F>>(u: Update) -> Self {
-        Self { id: TypeId::of::<(F, G)>(), update: u }
-    }
-
-    /// Get the matching test from a list. Panics if not found.
-    fn find_test<'a>(&self, tests: &'a [TestInfo]) -> &'a TestInfo {
-        tests.iter().find(|t| t.id == self.id).unwrap()
-    }
-
-    /// Update UI as needed for a single message received from the test runners.
-    fn handle(self, tests: &[TestInfo], mp: &MultiProgress) {
-        let test = self.find_test(tests);
-        let pb = test.pb.as_ref().unwrap();
-
-        match self.update {
-            Update::Started => {
-                mp.println(format!("Testing '{}'", test.name)).unwrap();
-            }
-            Update::Progress { executed, failures } => {
-                pb.set_message(format! {"{failures}"});
-                pb.set_position(executed);
-            }
-            Update::Failure { fail, input, float_res } => {
-                mp.println(format!(
-                    "Failure in '{}': {fail}. parsing '{input}'. Parsed as: {float_res}",
-                    test.name
-                ))
-                .unwrap();
-            }
-            Update::Completed(c) => {
-                test.finalize_pb(&c);
-
-                let prefix = match c.result {
-                    Ok(FinishedAll) => "Completed tests for",
-                    Err(EarlyExit::Timeout) => "Timed out",
-                    Err(EarlyExit::MaxFailures) => "Max failures reached for",
-                };
-
-                mp.println(format!(
-                    "{prefix} generator '{}' in {:?}. {} tests run, {} failures",
-                    test.name, c.elapsed, c.executed, c.failures
-                ))
-                .unwrap();
-                test.completed.set(c).unwrap();
-            }
-        };
+    /// When the test is finished, update progress bar messages and finalize.
+    fn complete(&self, c: Completed) {
+        self.progress.as_ref().unwrap().complete(&c, 0);
+        self.completed.set(c).unwrap();
     }
 }
 
-/// Status sent with a message.
-#[derive(Clone, Debug)]
-enum Update {
-    /// Starting a new test runner.
-    Started,
-    /// Completed a out of b tests.
-    Progress { executed: u64, failures: u64 },
-    /// Received a failed test.
-    Failure {
-        fail: CheckFailure,
-        /// String for which parsing was attempted.
-        input: Box<str>,
-        /// The parsed & decomposed `FloatRes`, already stringified so we don't need generics here.
-        float_res: Box<str>,
-    },
-    /// Exited with an unexpected condition.
-    Completed(Completed),
-}
-
 /// Result of an input did not parsing successfully.
 #[derive(Clone, Debug)]
 enum CheckFailure {
@@ -398,55 +314,21 @@ enum EarlyExit {
 /// This launches a main thread that receives messages and handlees UI updates, and uses the
 /// rest of the thread pool to execute the tests.
 fn launch_tests(tests: &mut [TestInfo], cfg: &Config) -> Duration {
-    // Run shorter tests first
-    tests.sort_unstable_by_key(|test| test.total_tests);
+    // Run shorter tests and smaller float types first.
+    tests.sort_unstable_by_key(|test| (test.total_tests, test.float_bits));
 
     for test in tests.iter() {
         println!("Launching test '{}'", test.name);
     }
 
-    // Configure progress bars
     let mut all_progress_bars = Vec::new();
-    let mp = MultiProgress::new();
-    mp.set_move_cursor(true);
-    for test in tests.iter_mut() {
-        test.register_pb(&mp, &mut all_progress_bars);
-    }
-
-    ui::set_panic_hook(all_progress_bars);
-
-    let (tx, rx) = mpsc::channel::<Msg>();
     let start = Instant::now();
 
-    rayon::scope(|scope| {
-        // Thread that updates the UI
-        scope.spawn(|_scope| {
-            let rx = rx; // move rx
-
-            loop {
-                if tests.iter().all(|t| t.completed.get().is_some()) {
-                    break;
-                }
-
-                let msg = rx.recv().unwrap();
-                msg.handle(tests, &mp);
-            }
-
-            // All tests completed; finish things up
-            drop(mp);
-            assert_eq!(rx.try_recv().unwrap_err(), mpsc::TryRecvError::Empty);
-        });
-
-        // Don't let the thread pool be starved by huge tests. Run faster tests first in parallel,
-        // then parallelize only within the rest of the tests.
-        let (huge_tests, normal_tests): (Vec<_>, Vec<_>) =
-            tests.iter().partition(|t| t.is_huge_test());
-
-        // Run the actual tests
-        normal_tests.par_iter().for_each(|test| ((test.launch)(&tx, test, cfg)));
-
-        huge_tests.par_iter().for_each(|test| ((test.launch)(&tx, test, cfg)));
-    });
+    for test in tests.iter_mut() {
+        test.progress = Some(ui::Progress::new(test, &mut all_progress_bars));
+        ui::set_panic_hook(&all_progress_bars);
+        ((test.launch)(test, cfg));
+    }
 
     start.elapsed()
 }
@@ -454,15 +336,12 @@ fn launch_tests(tests: &mut [TestInfo], cfg: &Config) -> Duration {
 /// Test runer for a single generator.
 ///
 /// This calls the generator's iterator multiple times (in parallel) and validates each output.
-fn test_runner<F: Float, G: Generator<F>>(tx: &mpsc::Sender<Msg>, _info: &TestInfo, cfg: &Config) {
-    tx.send(Msg::new::<F, G>(Update::Started)).unwrap();
-
-    let total = G::total_tests();
+fn test_runner<F: Float, G: Generator<F>>(test: &TestInfo, cfg: &Config) {
     let gen = G::new();
     let executed = AtomicU64::new(0);
     let failures = AtomicU64::new(0);
 
-    let checks_per_update = min(total, 1000);
+    let checks_per_update = min(test.total_tests, 1000);
     let started = Instant::now();
 
     // Function to execute for a single test iteration.
@@ -474,7 +353,12 @@ fn test_runner<F: Float, G: Generator<F>>(tx: &mpsc::Sender<Msg>, _info: &TestIn
         match validate::validate::<F>(buf) {
             Ok(()) => (),
             Err(e) => {
-                tx.send(Msg::new::<F, G>(e)).unwrap();
+                let CheckError { fail, input, float_res } = e;
+                test.progress.as_ref().unwrap().println(&format!(
+                    "Failure in '{}': {fail}. parsing '{input}'. Parsed as: {float_res}",
+                    test.name
+                ));
+
                 let f = failures.fetch_add(1, Ordering::Relaxed);
                 // End early if the limit is exceeded.
                 if f >= cfg.max_failures {
@@ -486,9 +370,7 @@ fn test_runner<F: Float, G: Generator<F>>(tx: &mpsc::Sender<Msg>, _info: &TestIn
         // Send periodic updates
         if executed % checks_per_update == 0 {
             let failures = failures.load(Ordering::Relaxed);
-
-            tx.send(Msg::new::<F, G>(Update::Progress { executed, failures })).unwrap();
-
+            test.progress.as_ref().unwrap().update(executed, failures);
             if started.elapsed() > cfg.timeout {
                 return Err(EarlyExit::Timeout);
             }
@@ -499,15 +381,19 @@ fn test_runner<F: Float, G: Generator<F>>(tx: &mpsc::Sender<Msg>, _info: &TestIn
 
     // Run the test iterations in parallel. Each thread gets a string buffer to write
     // its check values to.
-    let res = gen.par_bridge().try_for_each_init(|| String::with_capacity(100), check_one);
+    let res = gen.par_bridge().try_for_each_init(String::new, check_one);
 
     let elapsed = started.elapsed();
     let executed = executed.into_inner();
     let failures = failures.into_inner();
 
     // Warn about bad estimates if relevant.
-    let warning = if executed != total && res.is_ok() {
-        let msg = format!("executed tests != estimated ({executed} != {total}) for {}", G::NAME);
+    let warning = if executed != test.total_tests && res.is_ok() {
+        let msg = format!(
+            "executed tests != estimated ({executed} != {}) for {}",
+            test.total_tests,
+            G::NAME
+        );
 
         Some(msg.into())
     } else {
@@ -515,12 +401,5 @@ fn test_runner<F: Float, G: Generator<F>>(tx: &mpsc::Sender<Msg>, _info: &TestIn
     };
 
     let result = res.map(|()| FinishedAll);
-    tx.send(Msg::new::<F, G>(Update::Completed(Completed {
-        executed,
-        failures,
-        result,
-        warning,
-        elapsed,
-    })))
-    .unwrap();
+    test.complete(Completed { executed, failures, result, warning, elapsed });
 }
diff --git a/src/etc/test-float-parse/src/traits.rs b/src/etc/test-float-parse/src/traits.rs
index f5333d63b3693..c8407ba7cc50a 100644
--- a/src/etc/test-float-parse/src/traits.rs
+++ b/src/etc/test-float-parse/src/traits.rs
@@ -177,7 +177,7 @@ impl_float!(f32, u32, 32; f64, u64, 64);
 /// allocations (which otherwise turn out to be a pretty expensive part of these tests).
 pub trait Generator<F: Float>: Iterator<Item = Self::WriteCtx> + Send + 'static {
     /// Full display and filtering name
-    const NAME: &'static str;
+    const NAME: &'static str = Self::SHORT_NAME;
 
     /// Name for display with the progress bar
     const SHORT_NAME: &'static str;
diff --git a/src/etc/test-float-parse/src/ui.rs b/src/etc/test-float-parse/src/ui.rs
index f333bd4a55dc9..1ee57723e6a62 100644
--- a/src/etc/test-float-parse/src/ui.rs
+++ b/src/etc/test-float-parse/src/ui.rs
@@ -1,67 +1,92 @@
 //! Progress bars and such.
 
+use std::any::type_name;
+use std::fmt;
 use std::io::{self, Write};
 use std::process::ExitCode;
 use std::time::Duration;
 
-use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
+use indicatif::{ProgressBar, ProgressStyle};
 
 use crate::{Completed, Config, EarlyExit, FinishedAll, TestInfo};
 
 /// Templates for progress bars.
-const PB_TEMPLATE: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME ({pos}/{len}, {msg} f, {per_sec}, eta {eta})";
-const PB_TEMPLATE_FINAL: &str =
-    "[{elapsed:3} {percent:3}%] NAME ({pos}/{len}, {msg:.COLOR}, {per_sec}, {elapsed_precise})";
-
-/// Create a new progress bar within a multiprogress bar.
-pub fn create_pb(
-    mp: &MultiProgress,
-    total_tests: u64,
-    short_name_padded: &str,
-    all_bars: &mut Vec<ProgressBar>,
-) -> ProgressBar {
-    let pb = mp.add(ProgressBar::new(total_tests));
-    let pb_style = ProgressStyle::with_template(&PB_TEMPLATE.replace("NAME", short_name_padded))
-        .unwrap()
-        .progress_chars("##-");
-
-    pb.set_style(pb_style.clone());
-    pb.set_message("0");
-    all_bars.push(pb.clone());
-    pb
+const PB_TEMPLATE: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME \
+        {human_pos:>8}/{human_len:8} {msg} f {per_sec:14} eta {eta:8}";
+const PB_TEMPLATE_FINAL: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME \
+        {human_pos:>8}/{human_len:8} {msg:.COLOR} {per_sec:18} {elapsed_precise}";
+
+/// Thin abstraction over our usage of a `ProgressBar`.
+#[derive(Debug)]
+pub struct Progress {
+    pb: ProgressBar,
+    make_final_style: NoDebug<Box<dyn Fn(&'static str) -> ProgressStyle + Sync>>,
 }
 
-/// Removes the status bar and replace it with a message.
-pub fn finalize_pb(pb: &ProgressBar, short_name_padded: &str, c: &Completed) {
-    let f = c.failures;
+impl Progress {
+    /// Create a new progress bar within a multiprogress bar.
+    pub fn new(test: &TestInfo, all_bars: &mut Vec<ProgressBar>) -> Self {
+        let initial_template = PB_TEMPLATE.replace("NAME", &test.short_name_padded);
+        let final_template = PB_TEMPLATE_FINAL.replace("NAME", &test.short_name_padded);
+        let initial_style =
+            ProgressStyle::with_template(&initial_template).unwrap().progress_chars("##-");
+        let make_final_style = move |color| {
+            ProgressStyle::with_template(&final_template.replace("COLOR", color))
+                .unwrap()
+                .progress_chars("##-")
+        };
 
-    // Use a tuple so we can use colors
-    let (color, msg, finish_pb): (&str, String, fn(&ProgressBar, String)) = match &c.result {
-        Ok(FinishedAll) if f > 0 => {
-            ("red", format!("{f} f (finished with errors)",), ProgressBar::finish_with_message)
-        }
-        Ok(FinishedAll) => {
-            ("green", format!("{f} f (finished successfully)",), ProgressBar::finish_with_message)
-        }
-        Err(EarlyExit::Timeout) => {
-            ("red", format!("{f} f (timed out)"), ProgressBar::abandon_with_message)
+        let pb = ProgressBar::new(test.total_tests);
+        pb.set_style(initial_style);
+        pb.set_length(test.total_tests);
+        pb.set_message("0");
+        all_bars.push(pb.clone());
+
+        Progress { pb, make_final_style: NoDebug(Box::new(make_final_style)) }
+    }
+
+    /// Completed a out of b tests.
+    pub fn update(&self, completed: u64, failures: u64) {
+        // Infrequently update the progress bar.
+        if completed % 5_000 == 0 || failures > 0 {
+            self.pb.set_position(completed);
         }
-        Err(EarlyExit::MaxFailures) => {
-            ("red", format!("{f} f (failure limit)"), ProgressBar::abandon_with_message)
+
+        if failures > 0 {
+            self.pb.set_message(format! {"{failures}"});
         }
-    };
+    }
+
+    /// Finalize the progress bar.
+    pub fn complete(&self, c: &Completed, real_total: u64) {
+        let f = c.failures;
+        let (color, msg, finish_fn): (&str, String, fn(&ProgressBar)) = match &c.result {
+            Ok(FinishedAll) if f > 0 => {
+                ("red", format!("{f} f (completed with errors)",), ProgressBar::finish)
+            }
+            Ok(FinishedAll) => {
+                ("green", format!("{f} f (completed successfully)",), ProgressBar::finish)
+            }
+            Err(EarlyExit::Timeout) => ("red", format!("{f} f (timed out)"), ProgressBar::abandon),
+            Err(EarlyExit::MaxFailures) => {
+                ("red", format!("{f} f (failure limit)"), ProgressBar::abandon)
+            }
+        };
 
-    let pb_style = ProgressStyle::with_template(
-        &PB_TEMPLATE_FINAL.replace("NAME", short_name_padded).replace("COLOR", color),
-    )
-    .unwrap();
+        self.pb.set_position(real_total);
+        self.pb.set_style(self.make_final_style.0(color));
+        self.pb.set_message(msg);
+        finish_fn(&self.pb);
+    }
 
-    pb.set_style(pb_style);
-    finish_pb(pb, msg);
+    /// Print a message to stdout above the current progress bar.
+    pub fn println(&self, msg: &str) {
+        self.pb.suspend(|| println!("{msg}"));
+    }
 }
 
 /// Print final messages after all tests are complete.
-pub fn finish(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> ExitCode {
+pub fn finish_all(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> ExitCode {
     println!("\n\nResults:");
 
     let mut failed_generators = 0;
@@ -118,8 +143,9 @@ pub fn finish(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> Exit
 
 /// indicatif likes to eat panic messages. This workaround isn't ideal, but it improves things.
 /// <https://github.com/console-rs/indicatif/issues/121>.
-pub fn set_panic_hook(drop_bars: Vec<ProgressBar>) {
+pub fn set_panic_hook(drop_bars: &[ProgressBar]) {
     let hook = std::panic::take_hook();
+    let drop_bars = drop_bars.to_owned();
     std::panic::set_hook(Box::new(move |info| {
         for bar in &drop_bars {
             bar.abandon();
@@ -130,3 +156,13 @@ pub fn set_panic_hook(drop_bars: Vec<ProgressBar>) {
         hook(info);
     }));
 }
+
+/// Allow non-Debug items in a `derive(Debug)` struct`.
+#[derive(Clone)]
+struct NoDebug<T>(T);
+
+impl<T> fmt::Debug for NoDebug<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(type_name::<Self>())
+    }
+}
diff --git a/src/etc/test-float-parse/src/validate.rs b/src/etc/test-float-parse/src/validate.rs
index 1eb3699cfb9f7..6ca94a9378f25 100644
--- a/src/etc/test-float-parse/src/validate.rs
+++ b/src/etc/test-float-parse/src/validate.rs
@@ -9,7 +9,7 @@ use std::sync::LazyLock;
 use num::bigint::ToBigInt;
 use num::{BigInt, BigRational, FromPrimitive, Signed, ToPrimitive};
 
-use crate::{CheckFailure, Float, Int, Update};
+use crate::{CheckFailure, Float, Int};
 
 /// Powers of two that we store for constants. Account for binary128 which has a 15-bit exponent.
 const POWERS_OF_TWO_RANGE: RangeInclusive<i32> = (-(2 << 15))..=(2 << 15);
@@ -89,7 +89,7 @@ impl Constants {
 }
 
 /// Validate that a string parses correctly
-pub fn validate<F: Float>(input: &str) -> Result<(), Update> {
+pub fn validate<F: Float>(input: &str) -> Result<(), CheckError> {
     let parsed: F = input
         .parse()
         .unwrap_or_else(|e| panic!("parsing failed for {}: {e}. Input: {input}", type_name::<F>()));
@@ -118,10 +118,19 @@ pub enum FloatRes<F: Float> {
     },
 }
 
+#[derive(Clone, Debug)]
+pub struct CheckError {
+    pub fail: CheckFailure,
+    /// String for which parsing was attempted.
+    pub input: Box<str>,
+    /// The parsed & decomposed `FloatRes`, already stringified so we don't need generics here.
+    pub float_res: Box<str>,
+}
+
 impl<F: Float> FloatRes<F> {
     /// Given a known exact rational, check that this representation is accurate within the
     /// limits of the float representation. If not, construct a failure `Update` to send.
-    fn check(self, expected: Rational, input: &str) -> Result<(), Update> {
+    fn check(self, expected: Rational, input: &str) -> Result<(), CheckError> {
         let consts = F::constants();
         // let bool_helper = |cond: bool, err| cond.then_some(()).ok_or(err);
 
@@ -173,7 +182,7 @@ impl<F: Float> FloatRes<F> {
             (Rational::Finite(r), FloatRes::Real { sig, exp }) => Self::validate_real(r, sig, exp),
         };
 
-        res.map_err(|fail| Update::Failure {
+        res.map_err(|fail| CheckError {
             fail,
             input: input.into(),
             float_res: format!("{self:?}").into(),
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 91cc408878826..909b81a723b48 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "rustdoc"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 build = "build.rs"
 
 [lib]
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index bec7fbe8f52bd..ab169f3c2a4d5 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -48,12 +48,12 @@ impl Cfg {
         exclude: &FxHashSet<Cfg>,
     ) -> Result<Option<Cfg>, InvalidCfgError> {
         match nested_cfg {
-            MetaItemInner::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude),
+            MetaItemInner::MetaItem(cfg) => Cfg::parse_without(cfg, exclude),
             MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => match *b {
                 true => Ok(Some(Cfg::True)),
                 false => Ok(Some(Cfg::False)),
             },
-            MetaItemInner::Lit(ref lit) => {
+            MetaItemInner::Lit(lit) => {
                 Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
             }
         }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index ceffe5e5ce04e..bb12e4a706e7d 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -741,7 +741,7 @@ pub(crate) fn clean_generics<'tcx>(
     for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
         let mut p = clean_generic_param(cx, Some(gens), p);
         match &mut p.kind {
-            GenericParamDefKind::Lifetime { ref mut outlives } => {
+            GenericParamDefKind::Lifetime { outlives } => {
                 if let Some(region_pred) = region_predicates.get_mut(&Lifetime(p.name)) {
                     // We merge bounds in the `where` clause.
                     for outlive in outlives.drain(..) {
@@ -2688,7 +2688,7 @@ fn filter_doc_attr_ident(ident: Symbol, is_inline: bool) -> bool {
 /// Before calling this function, make sure `normal` is a `#[doc]` attribute.
 fn filter_doc_attr(args: &mut hir::AttrArgs, is_inline: bool) {
     match args {
-        hir::AttrArgs::Delimited(ref mut args) => {
+        hir::AttrArgs::Delimited(args) => {
             let tokens = filter_tokens_from_list(&args.tokens, |token| {
                 !matches!(
                     token,
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 178b6a60b41f7..5906a720e0fd3 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -502,7 +502,7 @@ impl Item {
         let Some(links) = cx.cache().intra_doc_links.get(&self.item_id) else { return vec![] };
         links
             .iter()
-            .filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| {
+            .filter_map(|ItemLink { link: s, link_text, page_id: id, fragment }| {
                 debug!(?id);
                 if let Ok((mut href, ..)) = href(*id, cx) {
                     debug!(?href);
@@ -1150,7 +1150,7 @@ pub(crate) struct Attributes {
 }
 
 impl Attributes {
-    pub(crate) fn lists(&self, name: Symbol) -> impl Iterator<Item = ast::MetaItemInner> + '_ {
+    pub(crate) fn lists(&self, name: Symbol) -> impl Iterator<Item = ast::MetaItemInner> {
         hir_attr_lists(&self.other_attrs[..], name)
     }
 
@@ -1864,7 +1864,7 @@ impl PrimitiveType {
             .copied()
     }
 
-    pub(crate) fn all_impls(tcx: TyCtxt<'_>) -> impl Iterator<Item = DefId> + '_ {
+    pub(crate) fn all_impls(tcx: TyCtxt<'_>) -> impl Iterator<Item = DefId> {
         Self::simplified_types()
             .values()
             .flatten()
@@ -2259,7 +2259,7 @@ impl GenericArgs {
             GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(),
         }
     }
-    pub(crate) fn constraints<'a>(&'a self) -> Box<dyn Iterator<Item = AssocItemConstraint> + 'a> {
+    pub(crate) fn constraints(&self) -> Box<dyn Iterator<Item = AssocItemConstraint> + '_> {
         match self {
             GenericArgs::AngleBracketed { constraints, .. } => {
                 Box::new(constraints.iter().cloned())
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 34656b26ce28c..a284de5229a21 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -60,7 +60,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
     let primitives = local_crate.primitives(cx.tcx);
     let keywords = local_crate.keywords(cx.tcx);
     {
-        let ItemKind::ModuleItem(ref mut m) = &mut module.inner.kind else { unreachable!() };
+        let ItemKind::ModuleItem(m) = &mut module.inner.kind else { unreachable!() };
         m.items.extend(primitives.iter().map(|&(def_id, prim)| {
             Item::from_def_id_and_parts(
                 def_id,
@@ -302,7 +302,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
     use rustc_hir::*;
     debug!("trying to get a name from pattern: {p:?}");
 
-    Symbol::intern(&match p.kind {
+    Symbol::intern(&match &p.kind {
         // FIXME(never_patterns): does this make sense?
         PatKind::Wild
         | PatKind::Err(_)
@@ -313,8 +313,9 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
         }
         PatKind::Binding(_, _, ident, _) => return ident.name,
         PatKind::Box(p) | PatKind::Ref(p, _) | PatKind::Guard(p, _) => return name_from_pat(p),
-        PatKind::TupleStruct(ref p, ..)
-        | PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref p), .. }) => qpath_to_string(p),
+        PatKind::TupleStruct(p, ..) | PatKind::Expr(PatExpr { kind: PatExprKind::Path(p), .. }) => {
+            qpath_to_string(p)
+        }
         PatKind::Or(pats) => {
             fmt::from_fn(|f| pats.iter().map(|p| name_from_pat(p)).joined(" | ", f)).to_string()
         }
@@ -329,7 +330,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
             return Symbol::intern("()");
         }
         PatKind::Slice(begin, mid, end) => {
-            fn print_pat<'a>(pat: &'a Pat<'a>, wild: bool) -> impl Display + 'a {
+            fn print_pat(pat: &Pat<'_>, wild: bool) -> impl Display {
                 fmt::from_fn(move |f| {
                     if wild {
                         f.write_str("..")?;
@@ -493,7 +494,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type {
 pub(crate) fn synthesize_auto_trait_and_blanket_impls(
     cx: &mut DocContext<'_>,
     item_def_id: DefId,
-) -> impl Iterator<Item = Item> {
+) -> impl Iterator<Item = Item> + use<> {
     let auto_impls = cx
         .sess()
         .prof
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 91b4b3ba1ebac..3b481acd410ae 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -15,7 +15,6 @@ use std::iter::{self, once};
 use itertools::Either;
 use rustc_abi::ExternAbi;
 use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince};
-use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -41,10 +40,10 @@ pub(crate) fn write_str(s: &mut String, f: fmt::Arguments<'_>) {
     s.write_fmt(f).unwrap();
 }
 
-pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
-    bounds: &'a [clean::GenericBound],
-    cx: &'a Context<'tcx>,
-) -> impl Display + 'a + Captures<'tcx> {
+pub(crate) fn print_generic_bounds(
+    bounds: &[clean::GenericBound],
+    cx: &Context<'_>,
+) -> impl Display {
     fmt::from_fn(move |f| {
         let mut bounds_dup = FxHashSet::default();
 
@@ -57,10 +56,7 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
 }
 
 impl clean::GenericParamDef {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| match &self.kind {
             clean::GenericParamDefKind::Lifetime { outlives } => {
                 write!(f, "{}", self.name)?;
@@ -80,7 +76,7 @@ impl clean::GenericParamDef {
                     print_generic_bounds(bounds, cx).fmt(f)?;
                 }
 
-                if let Some(ref ty) = default {
+                if let Some(ty) = default {
                     f.write_str(" = ")?;
                     ty.print(cx).fmt(f)?;
                 }
@@ -107,10 +103,7 @@ impl clean::GenericParamDef {
 }
 
 impl clean::Generics {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| {
             let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable();
             if real_params.peek().is_none() {
@@ -134,10 +127,7 @@ pub(crate) enum Ending {
     NoNewline,
 }
 
-fn print_where_predicate<'a, 'tcx: 'a>(
-    predicate: &'a clean::WherePredicate,
-    cx: &'a Context<'tcx>,
-) -> impl Display + 'a + Captures<'tcx> {
+fn print_where_predicate(predicate: &clean::WherePredicate, cx: &Context<'_>) -> impl Display {
     fmt::from_fn(move |f| {
         match predicate {
             clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
@@ -173,12 +163,12 @@ fn print_where_predicate<'a, 'tcx: 'a>(
 /// * The Generics from which to emit a where-clause.
 /// * The number of spaces to indent each line with.
 /// * Whether the where-clause needs to add a comma and newline after the last bound.
-pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
-    gens: &'a clean::Generics,
-    cx: &'a Context<'tcx>,
+pub(crate) fn print_where_clause(
+    gens: &clean::Generics,
+    cx: &Context<'_>,
     indent: usize,
     ending: Ending,
-) -> impl Display + 'a + Captures<'tcx> {
+) -> impl Display {
     fmt::from_fn(move |f| {
         if gens.where_predicates.is_empty() {
             return Ok(());
@@ -250,13 +240,13 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
 }
 
 impl clean::Lifetime {
-    pub(crate) fn print(&self) -> impl Display + '_ {
+    pub(crate) fn print(&self) -> impl Display {
         self.0.as_str()
     }
 }
 
 impl clean::ConstantKind {
-    pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display + '_ {
+    pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display {
         let expr = self.expr(tcx);
         fmt::from_fn(move |f| {
             if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) }
@@ -265,7 +255,7 @@ impl clean::ConstantKind {
 }
 
 impl clean::PolyTrait {
-    fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> {
+    fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| {
             print_higher_ranked_params_with_space(&self.generic_params, cx, "for").fmt(f)?;
             self.trait_.print(cx).fmt(f)
@@ -274,10 +264,7 @@ impl clean::PolyTrait {
 }
 
 impl clean::GenericBound {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| match self {
             clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
             clean::GenericBound::TraitBound(ty, modifiers) => {
@@ -304,7 +291,7 @@ impl clean::GenericBound {
 }
 
 impl clean::GenericArgs {
-    fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> {
+    fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| {
             match self {
                 clean::GenericArgs::AngleBracketed { args, constraints } => {
@@ -809,11 +796,11 @@ fn primitive_link_fragment(
     Ok(())
 }
 
-fn tybounds<'a, 'tcx: 'a>(
-    bounds: &'a [clean::PolyTrait],
-    lt: &'a Option<clean::Lifetime>,
-    cx: &'a Context<'tcx>,
-) -> impl Display + 'a + Captures<'tcx> {
+fn tybounds(
+    bounds: &[clean::PolyTrait],
+    lt: &Option<clean::Lifetime>,
+    cx: &Context<'_>,
+) -> impl Display {
     fmt::from_fn(move |f| {
         bounds.iter().map(|bound| bound.print(cx)).joined(" + ", f)?;
         if let Some(lt) = lt {
@@ -825,11 +812,11 @@ fn tybounds<'a, 'tcx: 'a>(
     })
 }
 
-fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>(
-    params: &'a [clean::GenericParamDef],
-    cx: &'a Context<'tcx>,
+fn print_higher_ranked_params_with_space(
+    params: &[clean::GenericParamDef],
+    cx: &Context<'_>,
     keyword: &'static str,
-) -> impl Display + 'a + Captures<'tcx> {
+) -> impl Display {
     fmt::from_fn(move |f| {
         if !params.is_empty() {
             f.write_str(keyword)?;
@@ -841,11 +828,7 @@ fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>(
     })
 }
 
-pub(crate) fn anchor<'a: 'cx, 'cx>(
-    did: DefId,
-    text: Symbol,
-    cx: &'cx Context<'a>,
-) -> impl Display + Captures<'a> + 'cx {
+pub(crate) fn anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display {
     fmt::from_fn(move |f| {
         let parts = href(did, cx);
         if let Ok((url, short_ty, fqp)) = parts {
@@ -1121,29 +1104,19 @@ fn fmt_type(
 }
 
 impl clean::Type {
-    pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'b + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| fmt_type(self, f, false, cx))
     }
 }
 
 impl clean::Path {
-    pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'b + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx))
     }
 }
 
 impl clean::Impl {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        use_absolute: bool,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, use_absolute: bool, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| {
             f.write_str("impl")?;
             self.generics.print(cx).fmt(f)?;
@@ -1182,12 +1155,12 @@ impl clean::Impl {
             print_where_clause(&self.generics, cx, 0, Ending::Newline).fmt(f)
         })
     }
-    fn print_type<'a, 'tcx: 'a>(
+    fn print_type(
         &self,
         type_: &clean::Type,
         f: &mut fmt::Formatter<'_>,
         use_absolute: bool,
-        cx: &'a Context<'tcx>,
+        cx: &Context<'_>,
     ) -> Result<(), fmt::Error> {
         if let clean::Type::Tuple(types) = type_
             && let [clean::Type::Generic(name)] = &types[..]
@@ -1258,10 +1231,7 @@ impl clean::Impl {
 }
 
 impl clean::Arguments {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| {
             self.values
                 .iter()
@@ -1301,10 +1271,7 @@ impl Display for Indent {
 }
 
 impl clean::FnDecl {
-    pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'b + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| {
             let ellipsis = if self.c_variadic { ", ..." } else { "" };
             if f.alternate() {
@@ -1333,12 +1300,12 @@ impl clean::FnDecl {
     ///   are preserved.
     /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
     ///   necessary.
-    pub(crate) fn full_print<'a, 'tcx: 'a>(
-        &'a self,
+    pub(crate) fn full_print(
+        &self,
         header_len: usize,
         indent: usize,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+        cx: &Context<'_>,
+    ) -> impl Display {
         fmt::from_fn(move |f| {
             // First, generate the text form of the declaration, with no line wrapping, and count the bytes.
             let mut counter = WriteCounter(0);
@@ -1420,10 +1387,7 @@ impl clean::FnDecl {
         self.print_output(cx).fmt(f)
     }
 
-    fn print_output<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    fn print_output(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| match &self.output {
             clean::Tuple(tys) if tys.is_empty() => Ok(()),
             ty if f.alternate() => {
@@ -1434,10 +1398,7 @@ impl clean::FnDecl {
     }
 }
 
-pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
-    item: &clean::Item,
-    cx: &'a Context<'tcx>,
-) -> impl Display + 'a + Captures<'tcx> {
+pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) -> impl Display {
     use std::fmt::Write as _;
     let vis: Cow<'static, str> = match item.visibility(cx.tcx()) {
         None => "".into(),
@@ -1546,10 +1507,7 @@ pub(crate) fn print_constness_with_space(
 }
 
 impl clean::Import {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| match self.kind {
             clean::ImportKind::Simple(name) => {
                 if name == self.source.path.last() {
@@ -1570,10 +1528,7 @@ impl clean::Import {
 }
 
 impl clean::ImportSource {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| match self.did {
             Some(did) => resolved_path(f, did, &self.path, true, false, cx),
             _ => {
@@ -1593,10 +1548,7 @@ impl clean::ImportSource {
 }
 
 impl clean::AssocItemConstraint {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| {
             f.write_str(self.assoc.name.as_str())?;
             self.assoc.args.print(cx).fmt(f)?;
@@ -1627,15 +1579,12 @@ pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display {
     })
 }
 
-pub(crate) fn print_default_space<'a>(v: bool) -> &'a str {
+pub(crate) fn print_default_space(v: bool) -> &'static str {
     if v { "default " } else { "" }
 }
 
 impl clean::GenericArg {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| match self {
             clean::GenericArg::Lifetime(lt) => lt.print().fmt(f),
             clean::GenericArg::Type(ty) => ty.print(cx).fmt(f),
@@ -1646,10 +1595,7 @@ impl clean::GenericArg {
 }
 
 impl clean::Term {
-    pub(crate) fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cx: &'a Context<'tcx>,
-    ) -> impl Display + 'a + Captures<'tcx> {
+    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
         fmt::from_fn(move |f| match self {
             clean::Term::Type(ty) => ty.print(cx).fmt(f),
             clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f),
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 204631063a23a..620135d37abb3 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -47,7 +47,6 @@ use rinja::Template;
 use rustc_attr_parsing::{
     ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince,
 };
-use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::Mutability;
 use rustc_hir::def_id::{DefId, DefIdSet};
@@ -82,7 +81,7 @@ use crate::html::{highlight, sources};
 use crate::scrape_examples::{CallData, CallLocation};
 use crate::{DOC_RUST_LANG_ORG_VERSION, try_none};
 
-pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
+pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) }
     })
@@ -310,7 +309,7 @@ impl ItemEntry {
 }
 
 impl ItemEntry {
-    pub(crate) fn print(&self) -> impl fmt::Display + '_ {
+    pub(crate) fn print(&self) -> impl fmt::Display {
         fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name)))
     }
 }
@@ -505,12 +504,12 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
     )
 }
 
-fn document<'a, 'cx: 'a>(
-    cx: &'a Context<'cx>,
-    item: &'a clean::Item,
-    parent: Option<&'a clean::Item>,
+fn document(
+    cx: &Context<'_>,
+    item: &clean::Item,
+    parent: Option<&clean::Item>,
     heading_offset: HeadingOffset,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+) -> impl fmt::Display {
     if let Some(ref name) = item.name {
         info!("Documenting {name}");
     }
@@ -526,12 +525,12 @@ fn document<'a, 'cx: 'a>(
 }
 
 /// Render md_text as markdown.
-fn render_markdown<'a, 'cx: 'a>(
-    cx: &'a Context<'cx>,
-    md_text: &'a str,
+fn render_markdown(
+    cx: &Context<'_>,
+    md_text: &str,
     links: Vec<RenderedLink>,
     heading_offset: HeadingOffset,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         write!(
             f,
@@ -552,13 +551,13 @@ fn render_markdown<'a, 'cx: 'a>(
 
 /// Writes a documentation block containing only the first paragraph of the documentation. If the
 /// docs are longer, a "Read more" link is appended to the end.
-fn document_short<'a, 'cx: 'a>(
-    item: &'a clean::Item,
-    cx: &'a Context<'cx>,
-    link: AssocItemLink<'a>,
-    parent: &'a clean::Item,
+fn document_short(
+    item: &clean::Item,
+    cx: &Context<'_>,
+    link: AssocItemLink<'_>,
+    parent: &clean::Item,
     show_def_docs: bool,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         document_item_info(cx, item, Some(parent)).render_into(f).unwrap();
         if !show_def_docs {
@@ -595,28 +594,28 @@ fn document_short<'a, 'cx: 'a>(
     })
 }
 
-fn document_full_collapsible<'a, 'cx: 'a>(
-    item: &'a clean::Item,
-    cx: &'a Context<'cx>,
+fn document_full_collapsible(
+    item: &clean::Item,
+    cx: &Context<'_>,
     heading_offset: HeadingOffset,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+) -> impl fmt::Display {
     document_full_inner(item, cx, true, heading_offset)
 }
 
-fn document_full<'a, 'cx: 'a>(
-    item: &'a clean::Item,
-    cx: &'a Context<'cx>,
+fn document_full(
+    item: &clean::Item,
+    cx: &Context<'_>,
     heading_offset: HeadingOffset,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+) -> impl fmt::Display {
     document_full_inner(item, cx, false, heading_offset)
 }
 
-fn document_full_inner<'a, 'cx: 'a>(
-    item: &'a clean::Item,
-    cx: &'a Context<'cx>,
+fn document_full_inner(
+    item: &clean::Item,
+    cx: &Context<'_>,
     is_collapsible: bool,
     heading_offset: HeadingOffset,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         if let Some(s) = item.opt_doc_value() {
             debug!("Doc block: =====\n{s}\n=====");
@@ -799,11 +798,11 @@ pub(crate) fn render_impls(
 }
 
 /// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
-fn assoc_href_attr<'a, 'tcx>(
+fn assoc_href_attr(
     it: &clean::Item,
-    link: AssocItemLink<'a>,
-    cx: &Context<'tcx>,
-) -> Option<impl fmt::Display + 'a + Captures<'tcx>> {
+    link: AssocItemLink<'_>,
+    cx: &Context<'_>,
+) -> Option<impl fmt::Display> {
     let name = it.name.unwrap();
     let item_type = it.type_();
 
@@ -814,7 +813,7 @@ fn assoc_href_attr<'a, 'tcx>(
     }
 
     let href = match link {
-        AssocItemLink::Anchor(Some(ref id)) => Href::AnchorId(id),
+        AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id),
         AssocItemLink::Anchor(None) => Href::Anchor(item_type),
         AssocItemLink::GotoSource(did, provided_methods) => {
             // We're creating a link from the implementation of an associated item to its
@@ -1168,7 +1167,7 @@ fn render_assoc_item(
             if parent == ItemType::Trait { 4 } else { 0 },
             cx,
         ),
-        clean::RequiredAssocTypeItem(ref generics, ref bounds) => assoc_type(
+        clean::RequiredAssocTypeItem(generics, bounds) => assoc_type(
             w,
             item,
             generics,
@@ -1178,7 +1177,7 @@ fn render_assoc_item(
             if parent == ItemType::Trait { 4 } else { 0 },
             cx,
         ),
-        clean::AssocTypeItem(ref ty, ref bounds) => assoc_type(
+        clean::AssocTypeItem(ty, bounds) => assoc_type(
             w,
             item,
             &ty.generics,
@@ -1194,11 +1193,7 @@ fn render_assoc_item(
 
 // When an attribute is rendered inside a `<pre>` tag, it is formatted using
 // a whitespace prefix and newline.
-fn render_attributes_in_pre<'a, 'tcx: 'a>(
-    it: &'a clean::Item,
-    prefix: &'a str,
-    cx: &'a Context<'tcx>,
-) -> impl fmt::Display + Captures<'a> + Captures<'tcx> {
+fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         for a in it.attributes(cx.tcx(), cx.cache(), false) {
             writeln!(f, "{prefix}{a}")?;
@@ -1292,12 +1287,12 @@ pub(crate) fn render_all_impls(
     }
 }
 
-fn render_assoc_items<'a, 'cx: 'a>(
-    cx: &'a Context<'cx>,
-    containing_item: &'a clean::Item,
+fn render_assoc_items(
+    cx: &Context<'_>,
+    containing_item: &clean::Item,
     it: DefId,
-    what: AssocItemRender<'a>,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+    what: AssocItemRender<'_>,
+) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         let mut derefs = DefIdSet::default();
         derefs.insert(it);
@@ -1468,10 +1463,10 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
     }
 }
 
-pub(crate) fn notable_traits_button<'a, 'tcx>(
-    ty: &'a clean::Type,
-    cx: &'a Context<'tcx>,
-) -> Option<impl fmt::Display + 'a + Captures<'tcx>> {
+pub(crate) fn notable_traits_button(
+    ty: &clean::Type,
+    cx: &Context<'_>,
+) -> Option<impl fmt::Display> {
     let mut has_notable_trait = false;
 
     if ty.is_unit() {
@@ -1773,7 +1768,7 @@ fn render_impl(
                     w.push_str("</h4></section>");
                 }
             }
-            clean::RequiredAssocConstItem(ref generics, ref ty) => {
+            clean::RequiredAssocConstItem(generics, ty) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
                 write_str(
@@ -1827,7 +1822,7 @@ fn render_impl(
                 );
                 w.push_str("</h4></section>");
             }
-            clean::RequiredAssocTypeItem(ref generics, ref bounds) => {
+            clean::RequiredAssocTypeItem(generics, bounds) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
                 write_str(
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index f320114703996..f04fbd992accc 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -4,7 +4,6 @@ use std::fmt::Display;
 
 use rinja::Template;
 use rustc_abi::VariantIdx;
-use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
@@ -92,44 +91,32 @@ macro_rules! item_template {
 macro_rules! item_template_methods {
     () => {};
     (document $($rest:tt)*) => {
-        fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let (item, cx) = self.item_and_cx();
-                let v = document(cx, item, None, HeadingOffset::H2);
-                write!(f, "{v}")
-            })
+        fn document(&self) -> impl fmt::Display {
+            let (item, cx) = self.item_and_cx();
+            document(cx, item, None, HeadingOffset::H2)
         }
         item_template_methods!($($rest)*);
     };
     (document_type_layout $($rest:tt)*) => {
-        fn document_type_layout<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let (item, cx) = self.item_and_cx();
-                let def_id = item.item_id.expect_def_id();
-                let v = document_type_layout(cx, def_id);
-                write!(f, "{v}")
-            })
+        fn document_type_layout(&self) -> impl fmt::Display {
+            let (item, cx) = self.item_and_cx();
+            let def_id = item.item_id.expect_def_id();
+            document_type_layout(cx, def_id)
         }
         item_template_methods!($($rest)*);
     };
     (render_attributes_in_pre $($rest:tt)*) => {
-        fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let (item, cx) = self.item_and_cx();
-                let v = render_attributes_in_pre(item, "", cx);
-                write!(f, "{v}")
-            })
+        fn render_attributes_in_pre(&self) -> impl fmt::Display {
+            let (item, cx) = self.item_and_cx();
+            render_attributes_in_pre(item, "", cx)
         }
         item_template_methods!($($rest)*);
     };
     (render_assoc_items $($rest:tt)*) => {
-        fn render_assoc_items<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let (item, cx) = self.item_and_cx();
-                let def_id = item.item_id.expect_def_id();
-                let v = render_assoc_items(cx, item, def_id, AssocItemRender::All);
-                write!(f, "{v}")
-            })
+        fn render_assoc_items(&self) -> impl fmt::Display {
+            let (item, cx) = self.item_and_cx();
+            let def_id = item.item_id.expect_def_id();
+            render_assoc_items(cx, item, def_id, AssocItemRender::All)
         }
         item_template_methods!($($rest)*);
     };
@@ -252,24 +239,24 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut String)
     item_vars.render_into(buf).unwrap();
 
     match &item.kind {
-        clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
-        clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f, _) => {
+        clean::ModuleItem(m) => item_module(buf, cx, item, &m.items),
+        clean::FunctionItem(f) | clean::ForeignFunctionItem(f, _) => {
             item_function(buf, cx, item, f)
         }
-        clean::TraitItem(ref t) => item_trait(buf, cx, item, t),
-        clean::StructItem(ref s) => item_struct(buf, cx, item, s),
-        clean::UnionItem(ref s) => item_union(buf, cx, item, s),
-        clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
-        clean::TypeAliasItem(ref t) => item_type_alias(buf, cx, item, t),
-        clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
-        clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
+        clean::TraitItem(t) => item_trait(buf, cx, item, t),
+        clean::StructItem(s) => item_struct(buf, cx, item, s),
+        clean::UnionItem(s) => item_union(buf, cx, item, s),
+        clean::EnumItem(e) => item_enum(buf, cx, item, e),
+        clean::TypeAliasItem(t) => item_type_alias(buf, cx, item, t),
+        clean::MacroItem(m) => item_macro(buf, cx, item, m),
+        clean::ProcMacroItem(m) => item_proc_macro(buf, cx, item, m),
         clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
-        clean::StaticItem(ref i) => item_static(buf, cx, item, i, None),
-        clean::ForeignStaticItem(ref i, safety) => item_static(buf, cx, item, i, Some(*safety)),
+        clean::StaticItem(i) => item_static(buf, cx, item, i, None),
+        clean::ForeignStaticItem(i, safety) => item_static(buf, cx, item, i, Some(*safety)),
         clean::ConstantItem(ci) => item_constant(buf, cx, item, &ci.generics, &ci.type_, &ci.kind),
         clean::ForeignTypeItem => item_foreign_type(buf, cx, item),
         clean::KeywordItem => item_keyword(buf, cx, item),
-        clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta),
+        clean::TraitAliasItem(ta) => item_trait_alias(buf, cx, item, ta),
         _ => {
             // We don't generate pages for any other type.
             unreachable!();
@@ -527,14 +514,14 @@ fn item_module(w: &mut String, cx: &Context<'_>, item: &clean::Item, items: &[cl
 
 /// Render the stability, deprecation and portability tags that are displayed in the item's summary
 /// at the module level.
-fn extra_info_tags<'a, 'tcx: 'a>(
-    tcx: TyCtxt<'tcx>,
-    item: &'a clean::Item,
-    parent: &'a clean::Item,
+fn extra_info_tags(
+    tcx: TyCtxt<'_>,
+    item: &clean::Item,
+    parent: &clean::Item,
     import_def_id: Option<DefId>,
-) -> impl Display + 'a + Captures<'tcx> {
+) -> impl Display {
     fmt::from_fn(move |f| {
-        fn tag_html<'a>(class: &'a str, title: &'a str, contents: &'a str) -> impl Display + 'a {
+        fn tag_html(class: &str, title: &str, contents: &str) -> impl Display {
             fmt::from_fn(move |f| {
                 write!(
                     f,
@@ -973,7 +960,7 @@ fn item_trait(w: &mut String, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                 extern_crates.insert(did.krate);
             }
             match implementor.inner_impl().for_.without_borrowed_ref() {
-                clean::Type::Path { ref path } if !path.is_assoc_ty() => {
+                clean::Type::Path { path } if !path.is_assoc_ty() => {
                     let did = path.def_id();
                     let &mut (prev_did, ref mut has_duplicates) =
                         implementor_dups.entry(path.last()).or_insert((did, false));
@@ -1419,35 +1406,20 @@ fn item_union(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
     );
 
     impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
-        fn render_union<'b>(&'b self) -> impl Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let v = render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx);
-                write!(f, "{v}")
-            })
+        fn render_union(&self) -> impl Display {
+            render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx)
         }
 
-        fn document_field<'b>(
-            &'b self,
-            field: &'a clean::Item,
-        ) -> impl Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let v = document(self.cx, field, Some(self.it), HeadingOffset::H3);
-                write!(f, "{v}")
-            })
+        fn document_field(&self, field: &'a clean::Item) -> impl Display {
+            document(self.cx, field, Some(self.it), HeadingOffset::H3)
         }
 
         fn stability_field(&self, field: &clean::Item) -> Option<String> {
             field.stability_class(self.cx.tcx())
         }
 
-        fn print_ty<'b>(
-            &'b self,
-            ty: &'a clean::Type,
-        ) -> impl Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let v = ty.print(self.cx);
-                write!(f, "{v}")
-            })
+        fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
+            ty.print(self.cx)
         }
 
         fn fields_iter(
@@ -1467,10 +1439,7 @@ fn item_union(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
     ItemUnion { cx, it, s }.render_into(w).unwrap();
 }
 
-fn print_tuple_struct_fields<'a, 'cx: 'a>(
-    cx: &'a Context<'cx>,
-    s: &'a [clean::Item],
-) -> impl Display + 'a + Captures<'cx> {
+fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Display {
     fmt::from_fn(|f| {
         if !s.is_empty()
             && s.iter().all(|field| {
@@ -2111,18 +2080,14 @@ pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
     s
 }
 
-pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display + '_ {
+pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display {
     fmt::from_fn(move |f| match ty {
         ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
         _ => write!(f, "{ty}.{name}.html"),
     })
 }
 
-fn bounds<'a, 'tcx>(
-    bounds: &'a [clean::GenericBound],
-    trait_alias: bool,
-    cx: &'a Context<'tcx>,
-) -> impl Display + 'a + Captures<'tcx> {
+fn bounds(bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> impl Display {
     (!bounds.is_empty())
         .then_some(fmt::from_fn(move |f| {
             let has_lots_of_bounds = bounds.len() > 2;
@@ -2208,12 +2173,12 @@ fn render_implementor(
     );
 }
 
-fn render_union<'a, 'cx: 'a>(
-    it: &'a clean::Item,
-    g: Option<&'a clean::Generics>,
-    fields: &'a [clean::Item],
-    cx: &'a Context<'cx>,
-) -> impl Display + 'a + Captures<'cx> {
+fn render_union(
+    it: &clean::Item,
+    g: Option<&clean::Generics>,
+    fields: &[clean::Item],
+    cx: &Context<'_>,
+) -> impl Display {
     fmt::from_fn(move |mut f| {
         write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
 
@@ -2410,7 +2375,7 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str {
     if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
 }
 
-fn document_non_exhaustive(item: &clean::Item) -> impl Display + '_ {
+fn document_non_exhaustive(item: &clean::Item) -> impl Display {
     fmt::from_fn(|f| {
         if item.is_non_exhaustive() {
             write!(
diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs
index 0f01db5f6bcc7..a1ee5c8c548b7 100644
--- a/src/librustdoc/html/render/type_layout.rs
+++ b/src/librustdoc/html/render/type_layout.rs
@@ -2,7 +2,6 @@ use std::fmt;
 
 use rinja::Template;
 use rustc_abi::{Primitive, TagEncoding, Variants};
-use rustc_data_structures::captures::Captures;
 use rustc_hir::def_id::DefId;
 use rustc_middle::span_bug;
 use rustc_middle::ty::layout::LayoutError;
@@ -26,10 +25,7 @@ struct TypeLayoutSize {
     size: u64,
 }
 
-pub(crate) fn document_type_layout<'a, 'cx: 'a>(
-    cx: &'a Context<'cx>,
-    ty_def_id: DefId,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+pub(crate) fn document_type_layout(cx: &Context<'_>, ty_def_id: DefId) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         if !cx.shared.show_type_layout {
             return Ok(());
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 78c86a27632b1..cbbd4b01d83ed 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -333,7 +333,7 @@ pub(crate) fn print_src(
     source_context: &SourceContext<'_>,
 ) {
     let mut lines = s.lines().count();
-    let line_info = if let SourceContext::Embedded(ref info) = source_context {
+    let line_info = if let SourceContext::Embedded(info) = source_context {
         highlight::LineInfo::new_scraped(lines as u32, info.offset as u32)
     } else {
         highlight::LineInfo::new(lines as u32)
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 97e6d3146427c..440d6331457b0 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -58,7 +58,7 @@ fn filter_assoc_items_by_name_and_namespace(
     assoc_items_of: DefId,
     ident: Ident,
     ns: Namespace,
-) -> impl Iterator<Item = &ty::AssocItem> + '_ {
+) -> impl Iterator<Item = &ty::AssocItem> {
     tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name).filter(move |item| {
         item.kind.namespace() == ns && tcx.hygienic_eq(ident, item.ident(tcx), assoc_items_of)
     })
diff --git a/src/tools/rustdoc/Cargo.toml b/src/tools/rustdoc/Cargo.toml
index c4101f72cc2da..d1682758d3626 100644
--- a/src/tools/rustdoc/Cargo.toml
+++ b/src/tools/rustdoc/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "rustdoc-tool"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 # 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"
diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
index 57fc601a2e0c0..91c83fa2f5ba9 100644
--- a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
+++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
@@ -19,7 +19,7 @@
 #![allow(incomplete_features)]
 #![feature(unsized_locals, unsized_fn_params)]
 
-// CHECK-LABEL: emptyfn:
+// CHECK-LABEL: emptyfn{{:|\[}}
 #[no_mangle]
 pub fn emptyfn() {
     // all: __stack_chk_fail
@@ -29,7 +29,7 @@ pub fn emptyfn() {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: array_char
+// CHECK-LABEL: array_char{{:|\[}}
 #[no_mangle]
 pub fn array_char(f: fn(*const char)) {
     let a = ['c'; 1];
@@ -47,7 +47,7 @@ pub fn array_char(f: fn(*const char)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: array_u8_1
+// CHECK-LABEL: array_u8_1{{:|\[}}
 #[no_mangle]
 pub fn array_u8_1(f: fn(*const u8)) {
     let a = [0u8; 1];
@@ -63,7 +63,7 @@ pub fn array_u8_1(f: fn(*const u8)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: array_u8_small:
+// CHECK-LABEL: array_u8_small{{:|\[}}
 #[no_mangle]
 pub fn array_u8_small(f: fn(*const u8)) {
     let a = [0u8; 2];
@@ -80,7 +80,7 @@ pub fn array_u8_small(f: fn(*const u8)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: array_u8_large:
+// CHECK-LABEL: array_u8_large{{:|\[}}
 #[no_mangle]
 pub fn array_u8_large(f: fn(*const u8)) {
     let a = [0u8; 9];
@@ -99,7 +99,7 @@ pub fn array_u8_large(f: fn(*const u8)) {
 #[derive(Copy, Clone)]
 pub struct ByteSizedNewtype(u8);
 
-// CHECK-LABEL: array_bytesizednewtype_9:
+// CHECK-LABEL: array_bytesizednewtype_9{{:|\[}}
 #[no_mangle]
 pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
     let a = [ByteSizedNewtype(0); 9];
@@ -115,7 +115,7 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: local_var_addr_used_indirectly
+// CHECK-LABEL: local_var_addr_used_indirectly{{:|\[}}
 #[no_mangle]
 pub fn local_var_addr_used_indirectly(f: fn(bool)) {
     let a = 5;
@@ -142,7 +142,7 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: local_string_addr_taken
+// CHECK-LABEL: local_string_addr_taken{{:|\[}}
 #[no_mangle]
 pub fn local_string_addr_taken(f: fn(&String)) {
     let x = String::new();
@@ -168,7 +168,7 @@ impl SelfByRef for i32 {
     }
 }
 
-// CHECK-LABEL: local_var_addr_taken_used_locally_only
+// CHECK-LABEL: local_var_addr_taken_used_locally_only{{:|\[}}
 #[no_mangle]
 pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32)) {
     let x = factory();
@@ -195,7 +195,7 @@ pub struct Gigastruct {
     members: u64,
 }
 
-// CHECK-LABEL: local_large_var_moved
+// CHECK-LABEL: local_large_var_moved{{:|\[}}
 #[no_mangle]
 pub fn local_large_var_moved(f: fn(Gigastruct)) {
     let x = Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 };
@@ -224,7 +224,7 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: local_large_var_cloned
+// CHECK-LABEL: local_large_var_cloned{{:|\[}}
 #[no_mangle]
 pub fn local_large_var_cloned(f: fn(Gigastruct)) {
     f(Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 });
@@ -281,7 +281,7 @@ extern "C" {
     fn alloca(size: usize) -> *mut ();
 }
 
-// CHECK-LABEL: alloca_small_compile_time_constant_arg
+// CHECK-LABEL: alloca_small_compile_time_constant_arg{{:|\[}}
 #[no_mangle]
 pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) {
     f(unsafe { alloca(8) });
@@ -293,7 +293,7 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: alloca_large_compile_time_constant_arg
+// CHECK-LABEL: alloca_large_compile_time_constant_arg{{:|\[}}
 #[no_mangle]
 pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) {
     f(unsafe { alloca(9) });
@@ -305,7 +305,7 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: alloca_dynamic_arg
+// CHECK-LABEL: alloca_dynamic_arg{{:|\[}}
 #[no_mangle]
 pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) {
     f(unsafe { alloca(n) });
@@ -324,7 +324,7 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) {
 // this is support for the "unsized locals" unstable feature:
 // https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html.
 
-// CHECK-LABEL: unsized_fn_param
+// CHECK-LABEL: unsized_fn_param{{:|\[}}
 #[no_mangle]
 pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
     let n = if l { 1 } else { 2 };
@@ -344,7 +344,7 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: unsized_local
+// CHECK-LABEL: unsized_local{{:|\[}}
 #[no_mangle]
 pub fn unsized_local(s: &[u8], l: bool, f: fn(&mut [u8])) {
     let n = if l { 1 } else { 2 };
diff --git a/x b/x
index e656d37c1e455..551cfe6efbf33 100755
--- a/x
+++ b/x
@@ -25,7 +25,13 @@ xpy=$(dirname "$(realpath "$0")")/x.py
 
 # On Windows, `py -3` sometimes works. We need to try it first because `python3`
 # sometimes tries to launch the app store on Windows.
-for SEARCH_PYTHON in py python3 python python2; do
+# On MacOS, `py` tries to install "Developer command line tools". Try `python3` first.
+# NOTE: running `bash -c ./x` from Windows doesn't set OSTYPE.
+case ${OSTYPE:-} in
+    cygwin*|msys*) SEARCH="py python3 python python2";;
+    *) SEARCH="python3 python py python2";;
+esac
+for SEARCH_PYTHON in $SEARCH; do
     if python=$(command -v $SEARCH_PYTHON) && [ -x "$python" ]; then
         if [ $SEARCH_PYTHON = py ]; then
             extra_arg="-3"