about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2024-10-29 08:13:34 +0200
committerLaurențiu Nicola <lnicola@dend.ro>2024-10-29 08:13:34 +0200
commit772d1383f7c5ad2af087c5e409f55bf60f702254 (patch)
tree96e75912f27b58dca0562f9e4f5f050c25024580 /src/tools
parent4b27980870eefce0928d03530fe4fb326230435d (diff)
parenta9d17627d241645a54c1134a20f1596127fedb60 (diff)
downloadrust-772d1383f7c5ad2af087c5e409f55bf60f702254.tar.gz
rust-772d1383f7c5ad2af087c5e409f55bf60f702254.zip
Merge from rust-lang/rust
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/build-manifest/src/main.rs1
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/clippy_lints/src/cognitive_complexity.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/ctfe.rs40
-rw-r--r--src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/deprecated_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_enum.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_div_ceil.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs16
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs4
-rw-r--r--src/tools/clippy/tests/ui-internal/author.rs (renamed from src/tools/clippy/tests/ui/author.rs)2
-rw-r--r--src/tools/clippy/tests/ui-internal/author.stdout (renamed from src/tools/clippy/tests/ui/author.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/blocks.rs (renamed from src/tools/clippy/tests/ui/author/blocks.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/blocks.stdout (renamed from src/tools/clippy/tests/ui/author/blocks.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/call.rs (renamed from src/tools/clippy/tests/ui/author/call.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/call.stdout (renamed from src/tools/clippy/tests/ui/author/call.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/if.rs (renamed from src/tools/clippy/tests/ui/author/if.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/if.stdout (renamed from src/tools/clippy/tests/ui/author/if.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/issue_3849.rs (renamed from src/tools/clippy/tests/ui/author/issue_3849.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/issue_3849.stdout (renamed from src/tools/clippy/tests/ui/author/issue_3849.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/loop.rs (renamed from src/tools/clippy/tests/ui/author/loop.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/loop.stdout (renamed from src/tools/clippy/tests/ui/author/loop.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/macro_in_closure.rs (renamed from src/tools/clippy/tests/ui/author/macro_in_closure.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/macro_in_closure.stdout (renamed from src/tools/clippy/tests/ui/author/macro_in_closure.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/macro_in_loop.rs (renamed from src/tools/clippy/tests/ui/author/macro_in_loop.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/macro_in_loop.stdout (renamed from src/tools/clippy/tests/ui/author/macro_in_loop.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/matches.rs (renamed from src/tools/clippy/tests/ui/author/matches.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/matches.stdout (renamed from src/tools/clippy/tests/ui/author/matches.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/repeat.rs (renamed from src/tools/clippy/tests/ui/author/repeat.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/repeat.stdout (renamed from src/tools/clippy/tests/ui/author/repeat.stdout)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/struct.rs (renamed from src/tools/clippy/tests/ui/author/struct.rs)0
-rw-r--r--src/tools/clippy/tests/ui-internal/author/struct.stdout (renamed from src/tools/clippy/tests/ui/author/struct.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/cast.rs1
-rw-r--r--src/tools/clippy/tests/ui/cast.stderr184
-rw-r--r--src/tools/clippy/tests/ui/no_lints.rs3
-rw-r--r--src/tools/clippy/tests/ui/rename.fixed4
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr18
-rw-r--r--src/tools/compiletest/src/common.rs21
-rw-r--r--src/tools/compiletest/src/header.rs79
-rw-r--r--src/tools/compiletest/src/lib.rs8
-rw-r--r--src/tools/compiletest/src/runtest.rs117
-rw-r--r--src/tools/compiletest/src/runtest/debugger.rs15
-rw-r--r--src/tools/compiletest/src/runtest/debuginfo.rs27
-rw-r--r--src/tools/compiletest/src/runtest/run_make.rs7
-rw-r--r--src/tools/miri/.github/workflows/ci.yml44
-rwxr-xr-xsrc/tools/miri/ci/ci.sh2
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs6
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs30
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs11
-rw-r--r--src/tools/miri/src/lib.rs2
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs9
-rw-r--r--src/tools/miri/src/shims/io_error.rs10
-rw-r--r--src/tools/miri/src/shims/time.rs12
-rw-r--r--src/tools/miri/src/shims/unix/android/foreign_items.rs4
-rw-r--r--src/tools/miri/src/shims/unix/android/mod.rs1
-rw-r--r--src/tools/miri/src/shims/unix/android/thread.rs57
-rw-r--r--src/tools/miri/src/shims/unix/fd.rs37
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs25
-rw-r--r--src/tools/miri/src/shims/unix/freebsd/foreign_items.rs1
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs221
-rw-r--r--src/tools/miri/src/shims/unix/linux/epoll.rs42
-rw-r--r--src/tools/miri/src/shims/unix/linux/foreign_items.rs1
-rw-r--r--src/tools/miri/src/shims/unix/linux/mem.rs4
-rw-r--r--src/tools/miri/src/shims/unix/linux/sync.rs17
-rw-r--r--src/tools/miri/src/shims/unix/macos/foreign_items.rs1
-rw-r--r--src/tools/miri/src/shims/unix/mem.rs16
-rw-r--r--src/tools/miri/src/shims/unix/solarish/foreign_items.rs1
-rw-r--r--src/tools/miri/src/shims/unix/thread.rs12
-rw-r--r--src/tools/miri/src/shims/windows/sync.rs2
-rw-r--r--src/tools/miri/test_dependencies/Cargo.lock6
-rw-r--r--src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs10
-rw-r--r--src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr21
-rw-r--r--src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs2
-rw-r--r--src/tools/miri/tests/fail/provenance/ptr_invalid.rs1
-rw-r--r--src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs16
-rw-r--r--src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr30
-rw-r--r--src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs74
-rw-r--r--src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs18
-rw-r--r--src/tools/miri/tests/pass/dyn-upcast.rs3
-rw-r--r--src/tools/miri/tests/pass/float.rs160
-rw-r--r--src/tools/run-make-support/Cargo.toml3
-rw-r--r--src/tools/run-make-support/src/command.rs18
-rw-r--r--src/tools/run-make-support/src/external_deps/llvm.rs66
-rw-r--r--src/tools/run-make-support/src/lib.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs4
-rw-r--r--src/tools/rustbook/Cargo.lock3
-rw-r--r--src/tools/rustbook/src/main.rs9
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt1
-rw-r--r--src/tools/tidy/src/issues.txt20
100 files changed, 1114 insertions, 587 deletions
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index 62e1695cbe3..925cbfe09a4 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -161,6 +161,7 @@ static TARGETS: &[&str] = &[
     "wasm32-wasip1",
     "wasm32-wasip1-threads",
     "wasm32-wasip2",
+    "wasm32v1-none",
     "x86_64-apple-darwin",
     "x86_64-apple-ios",
     "x86_64-apple-ios-macabi",
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject cf53cc54bb593b5ec3dc2be4b1702f50c36d24d
+Subproject e75214ea4936d2f2c909a71a1237042cc0e14b0
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index 495d8ce3fa7..477435236a5 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     pub COGNITIVE_COMPLEXITY,
     nursery,
     "functions that should be split up into multiple functions"
+    @eval_always = true
 }
 
 pub struct CognitiveComplexity {
diff --git a/src/tools/clippy/clippy_lints/src/ctfe.rs b/src/tools/clippy/clippy_lints/src/ctfe.rs
new file mode 100644
index 00000000000..2fe37a64db6
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/ctfe.rs
@@ -0,0 +1,40 @@
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{Body, FnDecl};
+use rustc_lint::Level::Deny;
+use rustc_lint::{LateContext, LateLintPass, Lint};
+use rustc_session::declare_lint_pass;
+use rustc_span::Span;
+
+/// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes).
+/// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran.
+pub static CLIPPY_CTFE: &Lint = &Lint {
+    name: &"clippy::CLIPPY_CTFE",
+    default_level: Deny,
+    desc: "Ensure CTFE is being made",
+    edition_lint_opts: None,
+    report_in_external_macro: true,
+    future_incompatible: None,
+    is_externally_loaded: true,
+    crate_level_only: false,
+    eval_always: true,
+    ..Lint::default_fields_for_macro()
+};
+
+// No static CLIPPY_CTFE_INFO because we want this lint to be invisible
+
+declare_lint_pass! { ClippyCtfe => [CLIPPY_CTFE] }
+
+impl<'tcx> LateLintPass<'tcx> for ClippyCtfe {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'_>,
+        _: FnKind<'tcx>,
+        _: &'tcx FnDecl<'tcx>,
+        _: &'tcx Body<'tcx>,
+        _: Span,
+        defid: LocalDefId,
+    ) {
+        cx.tcx.ensure().mir_drops_elaborated_and_const_checked(defid); // Lint
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs
index b1e39c70baa..a785a9d377c 100644
--- a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs
+++ b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs
@@ -9,6 +9,7 @@ macro_rules! declare_clippy_lint {
         $desc:literal,
         $version_expr:expr,
         $version_lit:literal
+        $(, $eval_always: literal)?
     ) => {
         rustc_session::declare_tool_lint! {
             $(#[doc = $lit])*
@@ -17,6 +18,7 @@ macro_rules! declare_clippy_lint {
             $category,
             $desc,
             report_in_external_macro:true
+            $(, @eval_always = $eval_always)?
         }
 
         pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo {
@@ -33,11 +35,12 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         restriction,
         $desc:literal
+        $(@eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Allow, crate::LintCategory::Restriction, $desc,
-            Some($version), $version
+            Some($version), $version $(, $eval_always)?
         }
     };
     (
@@ -46,12 +49,12 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         style,
         $desc:literal
+        $(@eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Warn, crate::LintCategory::Style, $desc,
-            Some($version), $version
-
+            Some($version), $version $(, $eval_always)?
         }
     };
     (
@@ -60,11 +63,12 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         correctness,
         $desc:literal
+        $(@eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Deny, crate::LintCategory::Correctness, $desc,
-            Some($version), $version
+            Some($version), $version $(, $eval_always)?
 
         }
     };
@@ -74,11 +78,12 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         perf,
         $desc:literal
+        $(@eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Warn, crate::LintCategory::Perf, $desc,
-            Some($version), $version
+            Some($version), $version $(, $eval_always)?
         }
     };
     (
@@ -87,11 +92,12 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         complexity,
         $desc:literal
+        $(@eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Warn, crate::LintCategory::Complexity, $desc,
-            Some($version), $version
+            Some($version), $version $(, $eval_always)?
         }
     };
     (
@@ -100,11 +106,12 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         suspicious,
         $desc:literal
+        $(@eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc,
-            Some($version), $version
+            Some($version), $version $(, $eval_always)?
         }
     };
     (
@@ -113,11 +120,12 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         nursery,
         $desc:literal
+        $(@eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Allow, crate::LintCategory::Nursery, $desc,
-            Some($version), $version
+            Some($version), $version $(, $eval_always)?
         }
     };
     (
@@ -126,11 +134,12 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         pedantic,
         $desc:literal
+        $(@eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc,
-            Some($version), $version
+            Some($version), $version $(, $eval_always)?
         }
     };
     (
@@ -139,11 +148,12 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         cargo,
         $desc:literal
+        $(@eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Allow, crate::LintCategory::Cargo, $desc,
-            Some($version), $version
+            Some($version), $version $(, $eval_always)?
         }
     };
 
diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
index 0066ed64325..77dbe9b78a1 100644
--- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
@@ -166,7 +166,7 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[
     #[clippy::version = ""]
     ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"),
     #[clippy::version = ""]
-    ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
+    ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"),
     #[clippy::version = ""]
     ("clippy::undropped_manually_drops", "undropped_manually_drops"),
     #[clippy::version = ""]
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 82a66cc9202..e8e21edd494 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -324,7 +324,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
     // If the current self type doesn't implement Copy (due to generic constraints), search to see if
     // there's a Copy impl for any instance of the adt.
     if !is_copy(cx, ty) {
-        if ty_subs.non_erasable_generics(cx.tcx, ty_adt.did()).next().is_some() {
+        if ty_subs.non_erasable_generics().next().is_some() {
             let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
                 impls.iter().any(|&id| {
                     matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs
index 70eb81fa09c..b0389fd9a2f 100644
--- a/src/tools/clippy/clippy_lints/src/empty_enum.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs
@@ -64,7 +64,7 @@ impl LateLintPass<'_> for EmptyEnum {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
         if let ItemKind::Enum(..) = item.kind
             // Only suggest the `never_type` if the feature is enabled
-            && cx.tcx.features().never_type
+            && cx.tcx.features().never_type()
             && let Some(adt) = cx.tcx.type_of(item.owner_id).instantiate_identity().ty_adt_def()
             && adt.variants().is_empty()
         {
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index 00f83237224..65fdc93e0ed 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::snippet;
 use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
-    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier, TyKind,
+    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifiers, TyKind,
     WherePredicate,
 };
 use rustc_hir_analysis::lower_ty;
@@ -234,7 +234,7 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds
         .iter()
         .filter_map(|bound| {
             if let GenericBound::Trait(poly_trait) = bound
-                && let TraitBoundModifier::None = poly_trait.modifiers
+                && let TraitBoundModifiers::NONE = poly_trait.modifiers
                 && let [.., path] = poly_trait.trait_ref.path.segments
                 && poly_trait.bound_generic_params.is_empty()
                 && let Some(trait_def_id) = path.res.opt_def_id()
@@ -300,7 +300,7 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) {
     // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
     for (index, bound) in bounds.iter().enumerate() {
         if let GenericBound::Trait(poly_trait) = bound
-            && let TraitBoundModifier::None = poly_trait.modifiers
+            && let TraitBoundModifiers::NONE = poly_trait.modifiers
             && let [.., path] = poly_trait.trait_ref.path.segments
             && let implied_args = path.args.map_or([].as_slice(), |a| a.args)
             && let implied_constraints = path.args.map_or([].as_slice(), |a| a.constraints)
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 6e29dde2211..14110539709 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -65,6 +65,7 @@ extern crate clippy_utils;
 #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 mod utils;
 
+pub mod ctfe; // Very important lint, do not remove (rust#125116)
 pub mod declared_lints;
 pub mod deprecated_lints;
 
@@ -605,6 +606,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         });
     }
 
+    store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe));
+
     store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf)));
     store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
     store.register_late_pass(|_| Box::new(utils::author::Author));
diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs
index 00e02e2a336..4c171e6d890 100644
--- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs
@@ -111,11 +111,7 @@ fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let expr_ty = cx.typeck_results().expr_ty(expr);
     match expr_ty.peel_refs().kind() {
         ty::Uint(_) => true,
-        ty::Int(_) => cx
-            .tcx
-            .features()
-            .declared_features
-            .contains(&Symbol::intern("int_roundings")),
+        ty::Int(_) => cx.tcx.features().enabled(Symbol::intern("int_roundings")),
 
         _ => false,
     }
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
index f9ffbc5dc0b..20984bc40ca 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
@@ -114,7 +114,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
     let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
     for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
         if matches!(arm2.pat.kind, PatKind::Wild) {
-            if !cx.tcx.features().non_exhaustive_omitted_patterns_lint
+            if !cx.tcx.features().non_exhaustive_omitted_patterns_lint()
                 || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id)
             {
                 let arm_span = adjusted_arm_span(cx, arm1.span);
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
index 564c598a334..42d9efe4ff6 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
@@ -251,7 +251,7 @@ fn emit_redundant_guards<'tcx>(
 fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     for_each_expr_without_closures(expr, |expr| {
         if match expr.kind {
-            ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat,
+            ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat(),
             ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => {
                 // Allow ctors
                 matches!(cx.qpath_res(&qpath, c.hir_id), Res::Def(DefKind::Ctor(..), ..))
diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
index 68c9af07465..9a1c397b5b2 100644
--- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::{DefId, DefIdMap};
-use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifier, WherePredicate};
+use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, BoundPolarity, WherePredicate};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{ClauseKind, PredicatePolarity};
 use rustc_session::declare_lint_pass;
@@ -118,13 +118,13 @@ impl LateLintPass<'_> for NeedlessMaybeSized {
         let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics)
             .filter(|bound| {
                 bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait)
-                    && bound.trait_bound.modifiers == TraitBoundModifier::Maybe
+                    && matches!(bound.trait_bound.modifiers.polarity, BoundPolarity::Maybe(_))
             })
             .map(|bound| (bound.param, bound))
             .collect();
 
         for bound in type_param_bounds(generics) {
-            if bound.trait_bound.modifiers == TraitBoundModifier::None
+            if bound.trait_bound.modifiers == TraitBoundModifiers::NONE
                 && let Some(sized_bound) = maybe_sized_params.get(&bound.param)
                 && let Some(path) = path_to_sized_bound(cx, bound.trait_bound)
             {
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 7f528b9d17b..3da4bf67558 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -11,7 +11,7 @@ use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{
     GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
-    TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
+    TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicate, BoundPolarity,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -233,7 +233,7 @@ impl TraitBounds {
     fn cannot_combine_maybe_bound(&self, cx: &LateContext<'_>, bound: &GenericBound<'_>) -> bool {
         if !self.msrv.meets(msrvs::MAYBE_BOUND_IN_WHERE)
             && let GenericBound::Trait(tr) = bound
-            && let TraitBoundModifier::Maybe = tr.modifiers
+            && let BoundPolarity::Maybe(_) = tr.modifiers.polarity
         {
             cx.tcx.lang_items().get(LangItem::Sized) == tr.trait_ref.path.res.opt_def_id()
         } else {
@@ -374,12 +374,12 @@ fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Gen
 struct ComparableTraitRef<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     trait_ref: &'tcx TraitRef<'tcx>,
-    modifier: TraitBoundModifier,
+    modifiers: TraitBoundModifiers,
 }
 
 impl PartialEq for ComparableTraitRef<'_, '_> {
     fn eq(&self, other: &Self) -> bool {
-        self.modifier == other.modifier
+        SpanlessEq::new(self.cx).eq_modifiers(self.modifiers, other.modifiers)
             && SpanlessEq::new(self.cx)
                 .paths_by_resolution()
                 .eq_path(self.trait_ref.path, other.trait_ref.path)
@@ -390,8 +390,8 @@ impl Hash for ComparableTraitRef<'_, '_> {
     fn hash<H: Hasher>(&self, state: &mut H) {
         let mut s = SpanlessHash::new(self.cx).paths_by_resolution();
         s.hash_path(self.trait_ref.path);
+        s.hash_modifiers(self.modifiers);
         state.write_u64(s.finish());
-        self.modifier.hash(state);
     }
 }
 
@@ -400,7 +400,7 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
         let trait_path = t.trait_ref.path;
         let trait_span = {
             let path_span = trait_path.span;
-            if let TraitBoundModifier::Maybe = t.modifiers {
+            if let BoundPolarity::Maybe(_) = t.modifiers.polarity {
                 path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?`
             } else {
                 path_span
@@ -427,7 +427,7 @@ fn rollup_traits<'cx, 'tcx>(
                 ComparableTraitRef {
                     cx,
                     trait_ref: &t.trait_ref,
-                    modifier: t.modifiers,
+                    modifiers: t.modifiers,
                 },
                 t.span,
             ))
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs b/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs
index 032cd3ed739..22b2c895f7c 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs
@@ -39,7 +39,7 @@ impl From<&Attribute> for IdentIter {
 struct IdentCollector(Vec<Ident>);
 
 impl Visitor<'_> for IdentCollector {
-    fn visit_ident(&mut self, ident: Ident) {
-        self.0.push(ident);
+    fn visit_ident(&mut self, ident: &Ident) {
+        self.0.push(*ident);
     }
 }
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 27c57808ece..181d414cbbd 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -9,7 +9,8 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
     ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
     ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
-    LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind,
+    LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty,
+    TyKind,
 };
 use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::LateContext;
@@ -126,6 +127,11 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
     pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
         self.inter_expr().eq_path_segments(left, right)
     }
+
+    pub fn eq_modifiers(&mut self, left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool {
+        std::mem::discriminant(&left.constness) == std::mem::discriminant(&right.constness)
+            && std::mem::discriminant(&left.polarity) == std::mem::discriminant(&right.polarity)
+    }
 }
 
 pub struct HirEqInterExpr<'a, 'b, 'tcx> {
@@ -1143,6 +1149,12 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
         }
     }
 
+    pub fn hash_modifiers(&mut self, modifiers: TraitBoundModifiers) {
+        let TraitBoundModifiers { constness, polarity } = modifiers;
+        std::mem::discriminant(&polarity).hash(&mut self.s);
+        std::mem::discriminant(&constness).hash(&mut self.s);
+    }
+
     pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
         std::mem::discriminant(&b.kind).hash(&mut self.s);
 
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 5f12b6bf99e..46739862de6 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -334,7 +334,7 @@ fn check_terminator<'tcx>(
         | TerminatorKind::TailCall { func, args, fn_span: _ } => {
             let fn_ty = func.ty(body, tcx);
             if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
-                if !is_const_fn(tcx, fn_def_id, msrv) {
+                if !is_stable_const_fn(tcx, fn_def_id, msrv) {
                     return Err((
                         span,
                         format!(
@@ -377,12 +377,12 @@ fn check_terminator<'tcx>(
     }
 }
 
-fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
+fn is_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
     tcx.is_const_fn(def_id)
-        && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
+        && tcx.lookup_const_stability(def_id).is_none_or(|const_stab| {
             if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
                 // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
-                // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
+                // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`.
                 // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
 
                 let const_stab_rust_version = match since {
@@ -393,8 +393,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
 
                 msrv.meets(const_stab_rust_version)
             } else {
-                // Unstable const fn with the feature enabled.
-                msrv.current().is_none()
+                // Unstable const fn, check if the feature is enabled. We need both the regular stability
+                // feature and (if set) the const stability feature to const-call this function.
+                let stab = tcx.lookup_stability(def_id);
+                let is_enabled = stab.is_some_and(|s| s.is_stable() || tcx.features().enabled(s.feature))
+                    && const_stab.feature.is_none_or(|f| tcx.features().enabled(f));
+                is_enabled && msrv.current().is_none()
             }
         })
 }
diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
index 91ec120adbf..3021f21df12 100644
--- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
@@ -207,16 +207,7 @@ fn path_segment_certainty(
             if cx.tcx.res_generics_def_id(path_segment.res).is_some() {
                 let generics = cx.tcx.generics_of(def_id);
 
-                let own_count = generics.own_params.len()
-                    - usize::from(generics.host_effect_index.is_some_and(|index| {
-                        // Check that the host index actually belongs to this resolution.
-                        // E.g. for `Add::add`, host_effect_index is `Some(2)`, but it's part of the parent `Add`
-                        // trait's generics.
-                        // Add params:      [Self#0, Rhs#1, host#2]   parent_count=0, count=3
-                        // Add::add params: []                        parent_count=3, count=3
-                        // (3..3).contains(&host_effect_index) => false
-                        (generics.parent_count..generics.count()).contains(&index)
-                    }));
+                let own_count = generics.own_params.len();
                 let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && own_count == 0 {
                     Certainty::Certain(None)
                 } else {
@@ -310,8 +301,7 @@ fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bo
 
     // Check that all type parameters appear in the functions input types.
     (0..(generics.parent_count + generics.own_params.len()) as u32).all(|index| {
-        Some(index as usize) == generics.host_effect_index
-            || fn_sig
+        fn_sig
                 .inputs()
                 .iter()
                 .any(|input_ty| contains_param(*input_ty.skip_binder(), index))
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index 02931306f16..8db6502dbfb 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -346,13 +346,13 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
                     .cx
                     .qpath_res(p, hir_id)
                     .opt_def_id()
-                    .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
+                    .map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {},
                 ExprKind::MethodCall(..)
                     if self
                         .cx
                         .typeck_results()
                         .type_dependent_def_id(e.hir_id)
-                        .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
+                        .map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {},
                 ExprKind::Binary(_, lhs, rhs)
                     if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
                         && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
diff --git a/src/tools/clippy/tests/ui/author.rs b/src/tools/clippy/tests/ui-internal/author.rs
index 0a1be356896..eb1f3e3f870 100644
--- a/src/tools/clippy/tests/ui/author.rs
+++ b/src/tools/clippy/tests/ui-internal/author.rs
@@ -1,3 +1,5 @@
+#![warn(clippy::author)]
+
 fn main() {
     #[clippy::author]
     let x: char = 0x45 as char;
diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui-internal/author.stdout
index eed704e82fe..eed704e82fe 100644
--- a/src/tools/clippy/tests/ui/author.stdout
+++ b/src/tools/clippy/tests/ui-internal/author.stdout
diff --git a/src/tools/clippy/tests/ui/author/blocks.rs b/src/tools/clippy/tests/ui-internal/author/blocks.rs
index 164f7d0d9d6..164f7d0d9d6 100644
--- a/src/tools/clippy/tests/ui/author/blocks.rs
+++ b/src/tools/clippy/tests/ui-internal/author/blocks.rs
diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui-internal/author/blocks.stdout
index 6bf48d5ba4e..6bf48d5ba4e 100644
--- a/src/tools/clippy/tests/ui/author/blocks.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/blocks.stdout
diff --git a/src/tools/clippy/tests/ui/author/call.rs b/src/tools/clippy/tests/ui-internal/author/call.rs
index e99c3c41dc4..e99c3c41dc4 100644
--- a/src/tools/clippy/tests/ui/author/call.rs
+++ b/src/tools/clippy/tests/ui-internal/author/call.rs
diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui-internal/author/call.stdout
index 59d4da490fe..59d4da490fe 100644
--- a/src/tools/clippy/tests/ui/author/call.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/call.stdout
diff --git a/src/tools/clippy/tests/ui/author/if.rs b/src/tools/clippy/tests/ui-internal/author/if.rs
index 946088ab346..946088ab346 100644
--- a/src/tools/clippy/tests/ui/author/if.rs
+++ b/src/tools/clippy/tests/ui-internal/author/if.rs
diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui-internal/author/if.stdout
index a85dcddd331..a85dcddd331 100644
--- a/src/tools/clippy/tests/ui/author/if.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/if.stdout
diff --git a/src/tools/clippy/tests/ui/author/issue_3849.rs b/src/tools/clippy/tests/ui-internal/author/issue_3849.rs
index 5f65746d71f..5f65746d71f 100644
--- a/src/tools/clippy/tests/ui/author/issue_3849.rs
+++ b/src/tools/clippy/tests/ui-internal/author/issue_3849.rs
diff --git a/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui-internal/author/issue_3849.stdout
index a5a8c0304ee..a5a8c0304ee 100644
--- a/src/tools/clippy/tests/ui/author/issue_3849.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/issue_3849.stdout
diff --git a/src/tools/clippy/tests/ui/author/loop.rs b/src/tools/clippy/tests/ui-internal/author/loop.rs
index ff5b6100117..ff5b6100117 100644
--- a/src/tools/clippy/tests/ui/author/loop.rs
+++ b/src/tools/clippy/tests/ui-internal/author/loop.rs
diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui-internal/author/loop.stdout
index 609d2491061..609d2491061 100644
--- a/src/tools/clippy/tests/ui/author/loop.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/loop.stdout
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.rs b/src/tools/clippy/tests/ui-internal/author/macro_in_closure.rs
index 444e6a12165..444e6a12165 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_closure.rs
+++ b/src/tools/clippy/tests/ui-internal/author/macro_in_closure.rs
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui-internal/author/macro_in_closure.stdout
index 66caf382d89..66caf382d89 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/macro_in_closure.stdout
diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.rs b/src/tools/clippy/tests/ui-internal/author/macro_in_loop.rs
index 8a520501f8d..8a520501f8d 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_loop.rs
+++ b/src/tools/clippy/tests/ui-internal/author/macro_in_loop.rs
diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui-internal/author/macro_in_loop.stdout
index 3f9be297c33..3f9be297c33 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/macro_in_loop.stdout
diff --git a/src/tools/clippy/tests/ui/author/matches.rs b/src/tools/clippy/tests/ui-internal/author/matches.rs
index 674e07ec2d3..674e07ec2d3 100644
--- a/src/tools/clippy/tests/ui/author/matches.rs
+++ b/src/tools/clippy/tests/ui-internal/author/matches.rs
diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui-internal/author/matches.stdout
index 91b3b6f6877..91b3b6f6877 100644
--- a/src/tools/clippy/tests/ui/author/matches.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/matches.stdout
diff --git a/src/tools/clippy/tests/ui/author/repeat.rs b/src/tools/clippy/tests/ui-internal/author/repeat.rs
index d8e9d589e68..d8e9d589e68 100644
--- a/src/tools/clippy/tests/ui/author/repeat.rs
+++ b/src/tools/clippy/tests/ui-internal/author/repeat.rs
diff --git a/src/tools/clippy/tests/ui/author/repeat.stdout b/src/tools/clippy/tests/ui-internal/author/repeat.stdout
index d9e3f864f12..d9e3f864f12 100644
--- a/src/tools/clippy/tests/ui/author/repeat.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/repeat.stdout
diff --git a/src/tools/clippy/tests/ui/author/struct.rs b/src/tools/clippy/tests/ui-internal/author/struct.rs
index a99bdfc1313..a99bdfc1313 100644
--- a/src/tools/clippy/tests/ui/author/struct.rs
+++ b/src/tools/clippy/tests/ui-internal/author/struct.rs
diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui-internal/author/struct.stdout
index 0b332d5e7d0..0b332d5e7d0 100644
--- a/src/tools/clippy/tests/ui/author/struct.stdout
+++ b/src/tools/clippy/tests/ui-internal/author/struct.stdout
diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs
index 146b04ab813..0de53a75c62 100644
--- a/src/tools/clippy/tests/ui/cast.rs
+++ b/src/tools/clippy/tests/ui/cast.rs
@@ -1,7 +1,6 @@
 //@no-rustfix
 
 #![feature(repr128)]
-#![feature(isqrt)]
 #![allow(incomplete_features)]
 #![warn(
     clippy::cast_precision_loss,
diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr
index 7824bdfac25..452482fc88e 100644
--- a/src/tools/clippy/tests/ui/cast.stderr
+++ b/src/tools/clippy/tests/ui/cast.stderr
@@ -1,5 +1,5 @@
 error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> tests/ui/cast.rs:26:5
+  --> tests/ui/cast.rs:25:5
    |
 LL |     x0 as f32;
    |     ^^^^^^^^^
@@ -8,37 +8,37 @@ LL |     x0 as f32;
    = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]`
 
 error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> tests/ui/cast.rs:30:5
+  --> tests/ui/cast.rs:29:5
    |
 LL |     x1 as f32;
    |     ^^^^^^^^^
 
 error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-  --> tests/ui/cast.rs:32:5
+  --> tests/ui/cast.rs:31:5
    |
 LL |     x1 as f64;
    |     ^^^^^^^^^
 
 error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> tests/ui/cast.rs:35:5
+  --> tests/ui/cast.rs:34:5
    |
 LL |     x2 as f32;
    |     ^^^^^^^^^
 
 error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> tests/ui/cast.rs:38:5
+  --> tests/ui/cast.rs:37:5
    |
 LL |     x3 as f32;
    |     ^^^^^^^^^
 
 error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-  --> tests/ui/cast.rs:40:5
+  --> tests/ui/cast.rs:39:5
    |
 LL |     x3 as f64;
    |     ^^^^^^^^^
 
 error: casting `f32` to `i32` may truncate the value
-  --> tests/ui/cast.rs:43:5
+  --> tests/ui/cast.rs:42:5
    |
 LL |     1f32 as i32;
    |     ^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     1f32 as i32;
    = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]`
 
 error: casting `f32` to `u32` may truncate the value
-  --> tests/ui/cast.rs:45:5
+  --> tests/ui/cast.rs:44:5
    |
 LL |     1f32 as u32;
    |     ^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     1f32 as u32;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 
 error: casting `f32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:45:5
+  --> tests/ui/cast.rs:44:5
    |
 LL |     1f32 as u32;
    |     ^^^^^^^^^^^
@@ -65,7 +65,7 @@ LL |     1f32 as u32;
    = help: to override `-D warnings` add `#[allow(clippy::cast_sign_loss)]`
 
 error: casting `f64` to `f32` may truncate the value
-  --> tests/ui/cast.rs:49:5
+  --> tests/ui/cast.rs:48:5
    |
 LL |     1f64 as f32;
    |     ^^^^^^^^^^^
@@ -73,7 +73,7 @@ LL |     1f64 as f32;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 
 error: casting `i32` to `i8` may truncate the value
-  --> tests/ui/cast.rs:51:5
+  --> tests/ui/cast.rs:50:5
    |
 LL |     1i32 as i8;
    |     ^^^^^^^^^^
@@ -85,7 +85,7 @@ LL |     i8::try_from(1i32);
    |     ~~~~~~~~~~~~~~~~~~
 
 error: casting `i32` to `u8` may truncate the value
-  --> tests/ui/cast.rs:53:5
+  --> tests/ui/cast.rs:52:5
    |
 LL |     1i32 as u8;
    |     ^^^^^^^^^^
@@ -97,7 +97,7 @@ LL |     u8::try_from(1i32);
    |     ~~~~~~~~~~~~~~~~~~
 
 error: casting `f64` to `isize` may truncate the value
-  --> tests/ui/cast.rs:55:5
+  --> tests/ui/cast.rs:54:5
    |
 LL |     1f64 as isize;
    |     ^^^^^^^^^^^^^
@@ -105,7 +105,7 @@ LL |     1f64 as isize;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 
 error: casting `f64` to `usize` may truncate the value
-  --> tests/ui/cast.rs:57:5
+  --> tests/ui/cast.rs:56:5
    |
 LL |     1f64 as usize;
    |     ^^^^^^^^^^^^^
@@ -113,13 +113,13 @@ LL |     1f64 as usize;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 
 error: casting `f64` to `usize` may lose the sign of the value
-  --> tests/ui/cast.rs:57:5
+  --> tests/ui/cast.rs:56:5
    |
 LL |     1f64 as usize;
    |     ^^^^^^^^^^^^^
 
 error: casting `u32` to `u16` may truncate the value
-  --> tests/ui/cast.rs:60:5
+  --> tests/ui/cast.rs:59:5
    |
 LL |     1f32 as u32 as u16;
    |     ^^^^^^^^^^^^^^^^^^
@@ -131,7 +131,7 @@ LL |     u16::try_from(1f32 as u32);
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `f32` to `u32` may truncate the value
-  --> tests/ui/cast.rs:60:5
+  --> tests/ui/cast.rs:59:5
    |
 LL |     1f32 as u32 as u16;
    |     ^^^^^^^^^^^
@@ -139,13 +139,13 @@ LL |     1f32 as u32 as u16;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 
 error: casting `f32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:60:5
+  --> tests/ui/cast.rs:59:5
    |
 LL |     1f32 as u32 as u16;
    |     ^^^^^^^^^^^
 
 error: casting `i32` to `i8` may truncate the value
-  --> tests/ui/cast.rs:65:22
+  --> tests/ui/cast.rs:64:22
    |
 LL |         let _x: i8 = 1i32 as _;
    |                      ^^^^^^^^^
@@ -157,7 +157,7 @@ LL |         let _x: i8 = 1i32.try_into();
    |                      ~~~~~~~~~~~~~~~
 
 error: casting `f32` to `i32` may truncate the value
-  --> tests/ui/cast.rs:67:9
+  --> tests/ui/cast.rs:66:9
    |
 LL |         1f32 as i32;
    |         ^^^^^^^^^^^
@@ -165,7 +165,7 @@ LL |         1f32 as i32;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 
 error: casting `f64` to `i32` may truncate the value
-  --> tests/ui/cast.rs:69:9
+  --> tests/ui/cast.rs:68:9
    |
 LL |         1f64 as i32;
    |         ^^^^^^^^^^^
@@ -173,7 +173,7 @@ LL |         1f64 as i32;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 
 error: casting `f32` to `u8` may truncate the value
-  --> tests/ui/cast.rs:71:9
+  --> tests/ui/cast.rs:70:9
    |
 LL |         1f32 as u8;
    |         ^^^^^^^^^^
@@ -181,13 +181,13 @@ LL |         1f32 as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 
 error: casting `f32` to `u8` may lose the sign of the value
-  --> tests/ui/cast.rs:71:9
+  --> tests/ui/cast.rs:70:9
    |
 LL |         1f32 as u8;
    |         ^^^^^^^^^^
 
 error: casting `u8` to `i8` may wrap around the value
-  --> tests/ui/cast.rs:76:5
+  --> tests/ui/cast.rs:75:5
    |
 LL |     1u8 as i8;
    |     ^^^^^^^^^
@@ -196,31 +196,31 @@ LL |     1u8 as i8;
    = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]`
 
 error: casting `u16` to `i16` may wrap around the value
-  --> tests/ui/cast.rs:79:5
+  --> tests/ui/cast.rs:78:5
    |
 LL |     1u16 as i16;
    |     ^^^^^^^^^^^
 
 error: casting `u32` to `i32` may wrap around the value
-  --> tests/ui/cast.rs:81:5
+  --> tests/ui/cast.rs:80:5
    |
 LL |     1u32 as i32;
    |     ^^^^^^^^^^^
 
 error: casting `u64` to `i64` may wrap around the value
-  --> tests/ui/cast.rs:83:5
+  --> tests/ui/cast.rs:82:5
    |
 LL |     1u64 as i64;
    |     ^^^^^^^^^^^
 
 error: casting `usize` to `isize` may wrap around the value
-  --> tests/ui/cast.rs:85:5
+  --> tests/ui/cast.rs:84:5
    |
 LL |     1usize as isize;
    |     ^^^^^^^^^^^^^^^
 
 error: casting `usize` to `i8` may truncate the value
-  --> tests/ui/cast.rs:88:5
+  --> tests/ui/cast.rs:87:5
    |
 LL |     1usize as i8;
    |     ^^^^^^^^^^^^
@@ -232,7 +232,7 @@ LL |     i8::try_from(1usize);
    |     ~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `i16` may truncate the value
-  --> tests/ui/cast.rs:91:5
+  --> tests/ui/cast.rs:90:5
    |
 LL |     1usize as i16;
    |     ^^^^^^^^^^^^^
@@ -244,7 +244,7 @@ LL |     i16::try_from(1usize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `i16` may wrap around the value on targets with 16-bit wide pointers
-  --> tests/ui/cast.rs:91:5
+  --> tests/ui/cast.rs:90:5
    |
 LL |     1usize as i16;
    |     ^^^^^^^^^^^^^
@@ -253,7 +253,7 @@ LL |     1usize as i16;
    = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types
 
 error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
-  --> tests/ui/cast.rs:96:5
+  --> tests/ui/cast.rs:95:5
    |
 LL |     1usize as i32;
    |     ^^^^^^^^^^^^^
@@ -265,19 +265,19 @@ LL |     i32::try_from(1usize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
-  --> tests/ui/cast.rs:96:5
+  --> tests/ui/cast.rs:95:5
    |
 LL |     1usize as i32;
    |     ^^^^^^^^^^^^^
 
 error: casting `usize` to `i64` may wrap around the value on targets with 64-bit wide pointers
-  --> tests/ui/cast.rs:100:5
+  --> tests/ui/cast.rs:99:5
    |
 LL |     1usize as i64;
    |     ^^^^^^^^^^^^^
 
 error: casting `u16` to `isize` may wrap around the value on targets with 16-bit wide pointers
-  --> tests/ui/cast.rs:105:5
+  --> tests/ui/cast.rs:104:5
    |
 LL |     1u16 as isize;
    |     ^^^^^^^^^^^^^
@@ -286,13 +286,13 @@ LL |     1u16 as isize;
    = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types
 
 error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
-  --> tests/ui/cast.rs:109:5
+  --> tests/ui/cast.rs:108:5
    |
 LL |     1u32 as isize;
    |     ^^^^^^^^^^^^^
 
 error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
-  --> tests/ui/cast.rs:112:5
+  --> tests/ui/cast.rs:111:5
    |
 LL |     1u64 as isize;
    |     ^^^^^^^^^^^^^
@@ -304,55 +304,55 @@ LL |     isize::try_from(1u64);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
-  --> tests/ui/cast.rs:112:5
+  --> tests/ui/cast.rs:111:5
    |
 LL |     1u64 as isize;
    |     ^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:117:5
+  --> tests/ui/cast.rs:116:5
    |
 LL |     -1i32 as u32;
    |     ^^^^^^^^^^^^
 
 error: casting `isize` to `usize` may lose the sign of the value
-  --> tests/ui/cast.rs:120:5
+  --> tests/ui/cast.rs:119:5
    |
 LL |     -1isize as usize;
    |     ^^^^^^^^^^^^^^^^
 
 error: casting `i8` to `u8` may lose the sign of the value
-  --> tests/ui/cast.rs:131:5
+  --> tests/ui/cast.rs:130:5
    |
 LL |     (i8::MIN).abs() as u8;
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i64` to `u64` may lose the sign of the value
-  --> tests/ui/cast.rs:135:5
+  --> tests/ui/cast.rs:134:5
    |
 LL |     (-1i64).abs() as u64;
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: casting `isize` to `usize` may lose the sign of the value
-  --> tests/ui/cast.rs:136:5
+  --> tests/ui/cast.rs:135:5
    |
 LL |     (-1isize).abs() as usize;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i64` to `u64` may lose the sign of the value
-  --> tests/ui/cast.rs:143:5
+  --> tests/ui/cast.rs:142:5
    |
 LL |     (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i64` to `u64` may lose the sign of the value
-  --> tests/ui/cast.rs:158:5
+  --> tests/ui/cast.rs:157:5
    |
 LL |     (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i64` to `i8` may truncate the value
-  --> tests/ui/cast.rs:209:5
+  --> tests/ui/cast.rs:208:5
    |
 LL |     (-99999999999i64).min(1) as i8;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -364,7 +364,7 @@ LL |     i8::try_from((-99999999999i64).min(1));
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `u8` may truncate the value
-  --> tests/ui/cast.rs:223:5
+  --> tests/ui/cast.rs:222:5
    |
 LL |     999999u64.clamp(0, 256) as u8;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -376,7 +376,7 @@ LL |     u8::try_from(999999u64.clamp(0, 256));
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E2` to `u8` may truncate the value
-  --> tests/ui/cast.rs:246:21
+  --> tests/ui/cast.rs:245:21
    |
 LL |             let _ = self as u8;
    |                     ^^^^^^^^^^
@@ -388,7 +388,7 @@ LL |             let _ = u8::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E2::B` to `u8` will truncate the value
-  --> tests/ui/cast.rs:248:21
+  --> tests/ui/cast.rs:247:21
    |
 LL |             let _ = Self::B as u8;
    |                     ^^^^^^^^^^^^^
@@ -397,7 +397,7 @@ LL |             let _ = Self::B as u8;
    = help: to override `-D warnings` add `#[allow(clippy::cast_enum_truncation)]`
 
 error: casting `main::E5` to `i8` may truncate the value
-  --> tests/ui/cast.rs:290:21
+  --> tests/ui/cast.rs:289:21
    |
 LL |             let _ = self as i8;
    |                     ^^^^^^^^^^
@@ -409,13 +409,13 @@ LL |             let _ = i8::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E5::A` to `i8` will truncate the value
-  --> tests/ui/cast.rs:292:21
+  --> tests/ui/cast.rs:291:21
    |
 LL |             let _ = Self::A as i8;
    |                     ^^^^^^^^^^^^^
 
 error: casting `main::E6` to `i16` may truncate the value
-  --> tests/ui/cast.rs:309:21
+  --> tests/ui/cast.rs:308:21
    |
 LL |             let _ = self as i16;
    |                     ^^^^^^^^^^^
@@ -427,7 +427,7 @@ LL |             let _ = i16::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
-  --> tests/ui/cast.rs:328:21
+  --> tests/ui/cast.rs:327:21
    |
 LL |             let _ = self as usize;
    |                     ^^^^^^^^^^^^^
@@ -439,7 +439,7 @@ LL |             let _ = usize::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E10` to `u16` may truncate the value
-  --> tests/ui/cast.rs:375:21
+  --> tests/ui/cast.rs:374:21
    |
 LL |             let _ = self as u16;
    |                     ^^^^^^^^^^^
@@ -451,7 +451,7 @@ LL |             let _ = u16::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `u32` to `u8` may truncate the value
-  --> tests/ui/cast.rs:386:13
+  --> tests/ui/cast.rs:385:13
    |
 LL |     let c = (q >> 16) as u8;
    |             ^^^^^^^^^^^^^^^
@@ -463,7 +463,7 @@ LL |     let c = u8::try_from(q >> 16);
    |             ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u32` to `u8` may truncate the value
-  --> tests/ui/cast.rs:390:13
+  --> tests/ui/cast.rs:389:13
    |
 LL |     let c = (q / 1000) as u8;
    |             ^^^^^^^^^^^^^^^^
@@ -475,85 +475,85 @@ LL |     let c = u8::try_from(q / 1000);
    |             ~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:402:9
+  --> tests/ui/cast.rs:401:9
    |
 LL |         (x * x) as u32;
    |         ^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:407:32
+  --> tests/ui/cast.rs:406:32
    |
 LL |     let _a = |x: i32| -> u32 { (x * x * x * x) as u32 };
    |                                ^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:409:5
+  --> tests/ui/cast.rs:408:5
    |
 LL |     (2_i32).checked_pow(3).unwrap() as u32;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:410:5
+  --> tests/ui/cast.rs:409:5
    |
 LL |     (-2_i32).pow(3) as u32;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:415:5
+  --> tests/ui/cast.rs:414:5
    |
 LL |     (-5_i32 % 2) as u32;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:417:5
+  --> tests/ui/cast.rs:416:5
    |
 LL |     (-5_i32 % -2) as u32;
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:420:5
+  --> tests/ui/cast.rs:419:5
    |
 LL |     (-2_i32 >> 1) as u32;
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:424:5
+  --> tests/ui/cast.rs:423:5
    |
 LL |     (x * x) as u32;
    |     ^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:425:5
+  --> tests/ui/cast.rs:424:5
    |
 LL |     (x * x * x) as u32;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: casting `i16` to `u16` may lose the sign of the value
-  --> tests/ui/cast.rs:429:5
+  --> tests/ui/cast.rs:428:5
    |
 LL |     (y * y * y * y * -2) as u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i16` to `u16` may lose the sign of the value
-  --> tests/ui/cast.rs:431:5
+  --> tests/ui/cast.rs:430:5
    |
 LL |     (y * y * y / y * 2) as u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i16` to `u16` may lose the sign of the value
-  --> tests/ui/cast.rs:432:5
+  --> tests/ui/cast.rs:431:5
    |
 LL |     (y * y / y * 2) as u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i16` to `u16` may lose the sign of the value
-  --> tests/ui/cast.rs:434:5
+  --> tests/ui/cast.rs:433:5
    |
 LL |     (y / y * y * -2) as u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: equal expressions as operands to `/`
-  --> tests/ui/cast.rs:434:6
+  --> tests/ui/cast.rs:433:6
    |
 LL |     (y / y * y * -2) as u16;
    |      ^^^^^
@@ -561,97 +561,97 @@ LL |     (y / y * y * -2) as u16;
    = note: `#[deny(clippy::eq_op)]` on by default
 
 error: casting `i16` to `u16` may lose the sign of the value
-  --> tests/ui/cast.rs:437:5
+  --> tests/ui/cast.rs:436:5
    |
 LL |     (y + y + y + -2) as u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i16` to `u16` may lose the sign of the value
-  --> tests/ui/cast.rs:439:5
+  --> tests/ui/cast.rs:438:5
    |
 LL |     (y + y + y + 2) as u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i16` to `u16` may lose the sign of the value
-  --> tests/ui/cast.rs:443:5
+  --> tests/ui/cast.rs:442:5
    |
 LL |     (z + -2) as u16;
    |     ^^^^^^^^^^^^^^^
 
 error: casting `i16` to `u16` may lose the sign of the value
-  --> tests/ui/cast.rs:445:5
+  --> tests/ui/cast.rs:444:5
    |
 LL |     (z + z + 2) as u16;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:448:9
+  --> tests/ui/cast.rs:447:9
    |
 LL |         (a * a * b * b * c * c) as u32;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:449:9
+  --> tests/ui/cast.rs:448:9
    |
 LL |         (a * b * c) as u32;
    |         ^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:451:9
+  --> tests/ui/cast.rs:450:9
    |
 LL |         (a * -b * c) as u32;
    |         ^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:453:9
+  --> tests/ui/cast.rs:452:9
    |
 LL |         (a * b * c * c) as u32;
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:454:9
+  --> tests/ui/cast.rs:453:9
    |
 LL |         (a * -2) as u32;
    |         ^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:456:9
+  --> tests/ui/cast.rs:455:9
    |
 LL |         (a * b * c * -2) as u32;
    |         ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:458:9
+  --> tests/ui/cast.rs:457:9
    |
 LL |         (a / b) as u32;
    |         ^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:459:9
+  --> tests/ui/cast.rs:458:9
    |
 LL |         (a / b * c) as u32;
    |         ^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:461:9
+  --> tests/ui/cast.rs:460:9
    |
 LL |         (a / b + b * c) as u32;
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:463:9
+  --> tests/ui/cast.rs:462:9
    |
 LL |         a.saturating_pow(3) as u32;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:465:9
+  --> tests/ui/cast.rs:464:9
    |
 LL |         (a.abs() * b.pow(2) / c.abs()) as u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> tests/ui/cast.rs:473:21
+  --> tests/ui/cast.rs:472:21
    |
 LL |             let _ = i32::MIN as u32; // cast_sign_loss
    |                     ^^^^^^^^^^^^^^^
@@ -662,7 +662,7 @@ LL |     m!();
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: casting `u32` to `u8` may truncate the value
-  --> tests/ui/cast.rs:474:21
+  --> tests/ui/cast.rs:473:21
    |
 LL |             let _ = u32::MAX as u8; // cast_possible_truncation
    |                     ^^^^^^^^^^^^^^
@@ -678,7 +678,7 @@ LL |             let _ = u8::try_from(u32::MAX); // cast_possible_truncation
    |                     ~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `f64` to `f32` may truncate the value
-  --> tests/ui/cast.rs:475:21
+  --> tests/ui/cast.rs:474:21
    |
 LL |             let _ = std::f64::consts::PI as f32; // cast_possible_truncation
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -690,7 +690,7 @@ LL |     m!();
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
-  --> tests/ui/cast.rs:484:5
+  --> tests/ui/cast.rs:483:5
    |
 LL |     bar.unwrap().unwrap() as usize
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -702,13 +702,13 @@ LL |     usize::try_from(bar.unwrap().unwrap())
    |
 
 error: casting `i64` to `usize` may lose the sign of the value
-  --> tests/ui/cast.rs:484:5
+  --> tests/ui/cast.rs:483:5
    |
 LL |     bar.unwrap().unwrap() as usize
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `u64` to `u8` may truncate the value
-  --> tests/ui/cast.rs:499:5
+  --> tests/ui/cast.rs:498:5
    |
 LL |     (256 & 999999u64) as u8;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -720,7 +720,7 @@ LL |     u8::try_from(256 & 999999u64);
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `u8` may truncate the value
-  --> tests/ui/cast.rs:501:5
+  --> tests/ui/cast.rs:500:5
    |
 LL |     (255 % 999999u64) as u8;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/no_lints.rs b/src/tools/clippy/tests/ui/no_lints.rs
new file mode 100644
index 00000000000..a8467bb6ef7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/no_lints.rs
@@ -0,0 +1,3 @@
+#![deny(clippy::all)]
+
+fn main() {}
\ No newline at end of file
diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed
index b810fd8224f..0d6e07aa546 100644
--- a/src/tools/clippy/tests/ui/rename.fixed
+++ b/src/tools/clippy/tests/ui/rename.fixed
@@ -54,7 +54,7 @@
 #![allow(enum_intrinsics_non_enums)]
 #![allow(non_fmt_panics)]
 #![allow(named_arguments_used_positionally)]
-#![allow(temporary_cstring_as_ptr)]
+#![allow(dangling_pointers_from_temporaries)]
 #![allow(undropped_manually_drops)]
 #![allow(unknown_lints)]
 #![allow(unused_labels)]
@@ -120,7 +120,7 @@
 #![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os`
 #![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params`
 #![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters`
-#![warn(temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr`
+#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr`
 #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops`
 #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints`
 #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label`
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
index 46d9f0fac59..b906079d7df 100644
--- a/src/tools/clippy/tests/ui/rename.stderr
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -1,11 +1,17 @@
+error: lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries`
+  --> tests/ui/rename.rs:57:10
+   |
+LL | #![allow(temporary_cstring_as_ptr)]
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries`
+   |
+   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]`
+
 error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
   --> tests/ui/rename.rs:63:9
    |
 LL | #![warn(clippy::almost_complete_letter_range)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
-   |
-   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]`
 
 error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
   --> tests/ui/rename.rs:64:9
@@ -361,11 +367,11 @@ error: lint `clippy::positional_named_format_parameters` has been renamed to `na
 LL | #![warn(clippy::positional_named_format_parameters)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
 
-error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
+error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries`
   --> tests/ui/rename.rs:123:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries`
 
 error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops`
   --> tests/ui/rename.rs:124:9
@@ -397,5 +403,5 @@ error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_e
 LL | #![warn(clippy::reverse_range_loop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges`
 
-error: aborting due to 66 previous errors
+error: aborting due to 67 previous errors
 
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index ff059940f7c..69ac4644941 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -376,6 +376,7 @@ pub struct Config {
     pub only_modified: bool,
 
     pub target_cfgs: OnceLock<TargetCfgs>,
+    pub builtin_cfg_names: OnceLock<HashSet<String>>,
 
     pub nocapture: bool,
 
@@ -387,6 +388,9 @@ pub struct Config {
     /// True if the profiler runtime is enabled for this target.
     /// Used by the "needs-profiler-runtime" directive in test files.
     pub profiler_runtime: bool,
+
+    /// Command for visual diff display, e.g. `diff-tool --color=always`.
+    pub diff_command: Option<String>,
 }
 
 impl Config {
@@ -440,6 +444,11 @@ impl Config {
         self.target_cfg().panic == PanicStrategy::Unwind
     }
 
+    /// Get the list of builtin, 'well known' cfg names
+    pub fn builtin_cfg_names(&self) -> &HashSet<String> {
+        self.builtin_cfg_names.get_or_init(|| builtin_cfg_names(self))
+    }
+
     pub fn has_threads(&self) -> bool {
         // Wasm targets don't have threads unless `-threads` is in the target
         // name, such as `wasm32-wasip1-threads`.
@@ -651,6 +660,18 @@ pub enum Endian {
     Big,
 }
 
+fn builtin_cfg_names(config: &Config) -> HashSet<String> {
+    rustc_output(
+        config,
+        &["--print=check-cfg", "-Zunstable-options", "--check-cfg=cfg()"],
+        Default::default(),
+    )
+    .lines()
+    .map(|l| if let Some((name, _)) = l.split_once('=') { name.to_string() } else { l.to_string() })
+    .chain(std::iter::once(String::from("test")))
+    .collect()
+}
+
 fn rustc_output(config: &Config, args: &[&str], envs: HashMap<String, String>) -> String {
     let mut command = Command::new(&config.rustc_path);
     add_dylib_path(&mut command, iter::once(&config.compile_lib_path));
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 099e620ffe0..d75cdefe635 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -57,7 +57,7 @@ impl EarlyProps {
             &mut poisoned,
             testfile,
             rdr,
-            &mut |DirectiveLine { directive: ln, .. }| {
+            &mut |DirectiveLine { raw_directive: ln, .. }| {
                 parse_and_update_aux(config, ln, &mut props.aux);
                 config.parse_and_update_revisions(testfile, ln, &mut props.revisions);
             },
@@ -344,8 +344,8 @@ impl TestProps {
                 &mut poisoned,
                 testfile,
                 file,
-                &mut |DirectiveLine { header_revision, directive: ln, .. }| {
-                    if header_revision.is_some() && header_revision != test_revision {
+                &mut |directive @ DirectiveLine { raw_directive: ln, .. }| {
+                    if !directive.applies_to_test_revision(test_revision) {
                         return;
                     }
 
@@ -678,28 +678,35 @@ impl TestProps {
     }
 }
 
-/// Extract an `(Option<line_revision>, directive)` directive from a line if comment is present.
-///
-/// See [`DirectiveLine`] for a diagram.
-pub fn line_directive<'line>(
+/// If the given line begins with the appropriate comment prefix for a directive,
+/// returns a struct containing various parts of the directive.
+fn line_directive<'line>(
+    line_number: usize,
     comment: &str,
     original_line: &'line str,
-) -> Option<(Option<&'line str>, &'line str)> {
+) -> Option<DirectiveLine<'line>> {
     // Ignore lines that don't start with the comment prefix.
     let after_comment = original_line.trim_start().strip_prefix(comment)?.trim_start();
 
+    let revision;
+    let raw_directive;
+
     if let Some(after_open_bracket) = after_comment.strip_prefix('[') {
         // A comment like `//@[foo]` only applies to revision `foo`.
-        let Some((line_revision, directive)) = after_open_bracket.split_once(']') else {
+        let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else {
             panic!(
                 "malformed condition directive: expected `{comment}[foo]`, found `{original_line}`"
             )
         };
 
-        Some((Some(line_revision), directive.trim_start()))
+        revision = Some(line_revision);
+        raw_directive = after_close_bracket.trim_start();
     } else {
-        Some((None, after_comment))
-    }
+        revision = None;
+        raw_directive = after_comment;
+    };
+
+    Some(DirectiveLine { line_number, revision, raw_directive })
 }
 
 // To prevent duplicating the list of commmands between `compiletest`,`htmldocck` and `jsondocck`,
@@ -730,28 +737,37 @@ const KNOWN_HTMLDOCCK_DIRECTIVE_NAMES: &[&str] = &[
 const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] =
     &["count", "!count", "has", "!has", "is", "!is", "ismany", "!ismany", "set", "!set"];
 
-/// The broken-down contents of a line containing a test header directive,
+/// The (partly) broken-down contents of a line containing a test directive,
 /// which [`iter_header`] passes to its callback function.
 ///
 /// For example:
 ///
 /// ```text
 /// //@ compile-flags: -O
-///     ^^^^^^^^^^^^^^^^^ directive
+///     ^^^^^^^^^^^^^^^^^ raw_directive
 ///
 /// //@ [foo] compile-flags: -O
-///      ^^^                    header_revision
-///           ^^^^^^^^^^^^^^^^^ directive
+///      ^^^                    revision
+///           ^^^^^^^^^^^^^^^^^ raw_directive
 /// ```
 struct DirectiveLine<'ln> {
     line_number: usize,
-    /// Some header directives start with a revision name in square brackets
+    /// Some test directives start with a revision name in square brackets
     /// (e.g. `[foo]`), and only apply to that revision of the test.
     /// If present, this field contains the revision name (e.g. `foo`).
-    header_revision: Option<&'ln str>,
-    /// The main part of the header directive, after removing the comment prefix
+    revision: Option<&'ln str>,
+    /// The main part of the directive, after removing the comment prefix
     /// and the optional revision specifier.
-    directive: &'ln str,
+    ///
+    /// This is "raw" because the directive's name and colon-separated value
+    /// (if present) have not yet been extracted or checked.
+    raw_directive: &'ln str,
+}
+
+impl<'ln> DirectiveLine<'ln> {
+    fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool {
+        self.revision.is_none() || self.revision == test_revision
+    }
 }
 
 pub(crate) struct CheckDirectiveResult<'ln> {
@@ -819,8 +835,8 @@ fn iter_header(
             "ignore-cross-compile",
         ];
         // Process the extra implied directives, with a dummy line number of 0.
-        for directive in extra_directives {
-            it(DirectiveLine { line_number: 0, header_revision: None, directive });
+        for raw_directive in extra_directives {
+            it(DirectiveLine { line_number: 0, revision: None, raw_directive });
         }
     }
 
@@ -847,24 +863,21 @@ fn iter_header(
             return;
         }
 
-        let Some((header_revision, non_revisioned_directive_line)) = line_directive(comment, ln)
-        else {
+        let Some(directive_line) = line_directive(line_number, comment, ln) else {
             continue;
         };
 
         // Perform unknown directive check on Rust files.
         if testfile.extension().map(|e| e == "rs").unwrap_or(false) {
-            let directive_ln = non_revisioned_directive_line.trim();
-
             let CheckDirectiveResult { is_known_directive, trailing_directive } =
-                check_directive(directive_ln, mode, ln);
+                check_directive(directive_line.raw_directive, mode, ln);
 
             if !is_known_directive {
                 *poisoned = true;
 
                 eprintln!(
                     "error: detected unknown compiletest test directive `{}` in {}:{}",
-                    directive_ln,
+                    directive_line.raw_directive,
                     testfile.display(),
                     line_number,
                 );
@@ -888,11 +901,7 @@ fn iter_header(
             }
         }
 
-        it(DirectiveLine {
-            line_number,
-            header_revision,
-            directive: non_revisioned_directive_line,
-        });
+        it(directive_line);
     }
 }
 
@@ -1292,8 +1301,8 @@ pub fn make_test_description<R: Read>(
         &mut local_poisoned,
         path,
         src,
-        &mut |DirectiveLine { header_revision, directive: ln, line_number }| {
-            if header_revision.is_some() && header_revision != test_revision {
+        &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| {
+            if !directive.applies_to_test_revision(test_revision) {
                 return;
             }
 
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 7d6ede9bcda..490df313228 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -175,6 +175,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "git-merge-commit-email",
             "email address used for finding merge commits",
             "EMAIL",
+        )
+        .optopt(
+            "",
+            "compiletest-diff-tool",
+            "What custom diff tool to use for displaying compiletest tests.",
+            "COMMAND",
         );
 
     let (argv0, args_) = args.split_first().unwrap();
@@ -356,6 +362,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         force_rerun: matches.opt_present("force-rerun"),
 
         target_cfgs: OnceLock::new(),
+        builtin_cfg_names: OnceLock::new(),
 
         nocapture: matches.opt_present("nocapture"),
 
@@ -364,6 +371,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(),
 
         profiler_runtime: matches.opt_present("profiler-runtime"),
+        diff_command: matches.opt_str("compiletest-diff-tool"),
     }
 }
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 36c5106ddad..a8a71c196fc 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -59,7 +59,7 @@ fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
     use std::sync::Mutex;
 
     use windows::Win32::System::Diagnostics::Debug::{
-        SEM_NOGPFAULTERRORBOX, SetErrorMode, THREAD_ERROR_MODE,
+        SEM_FAILCRITICALERRORS, SEM_NOGPFAULTERRORBOX, SetErrorMode, THREAD_ERROR_MODE,
     };
 
     static LOCK: Mutex<()> = Mutex::new(());
@@ -67,13 +67,21 @@ fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
     // Error mode is a global variable, so lock it so only one thread will change it
     let _lock = LOCK.lock().unwrap();
 
-    // Tell Windows to not show any UI on errors (such as terminating abnormally).
-    // This is important for running tests, since some of them use abnormal
-    // termination by design. This mode is inherited by all child processes.
+    // Tell Windows to not show any UI on errors (such as terminating abnormally). This is important
+    // for running tests, since some of them use abnormal termination by design. This mode is
+    // inherited by all child processes.
+    //
+    // Note that `run-make` tests require `SEM_FAILCRITICALERRORS` in addition to suppress Windows
+    // Error Reporting (WER) error dialogues that come from "critical failures" such as missing
+    // DLLs.
+    //
+    // See <https://github.com/rust-lang/rust/issues/132092> and
+    // <https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode?redirectedfrom=MSDN>.
     unsafe {
-        let old_mode = SetErrorMode(SEM_NOGPFAULTERRORBOX); // read inherited flags
+        // read inherited flags
+        let old_mode = SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS);
         let old_mode = THREAD_ERROR_MODE(old_mode);
-        SetErrorMode(old_mode | SEM_NOGPFAULTERRORBOX);
+        SetErrorMode(old_mode | SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS);
         let r = f();
         SetErrorMode(old_mode);
         r
@@ -478,6 +486,9 @@ impl<'test> TestCx<'test> {
                     "error: redundant cfg argument `{normalized_revision}` is already created by the revision"
                 );
             }
+            if self.config.builtin_cfg_names().contains(&normalized_revision) {
+                panic!("error: revision `{normalized_revision}` collides with a builtin cfg");
+            }
             cmd.args(cfg_arg);
         }
 
@@ -1085,6 +1096,10 @@ impl<'test> TestCx<'test> {
         self.config.target.contains("vxworks") && !self.is_vxworks_pure_static()
     }
 
+    fn has_aux_dir(&self) -> bool {
+        !self.props.aux.builds.is_empty() || !self.props.aux.crates.is_empty()
+    }
+
     fn aux_output_dir(&self) -> PathBuf {
         let aux_dir = self.aux_output_dir_name();
 
@@ -1638,7 +1653,11 @@ impl<'test> TestCx<'test> {
         }
 
         if let LinkToAux::Yes = link_to_aux {
-            rustc.arg("-L").arg(self.aux_output_dir_name());
+            // if we pass an `-L` argument to a directory that doesn't exist,
+            // macOS ld emits warnings which disrupt the .stderr files
+            if self.has_aux_dir() {
+                rustc.arg("-L").arg(self.aux_output_dir_name());
+            }
         }
 
         rustc.args(&self.props.compile_flags);
@@ -2459,7 +2478,7 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn compare_output(&self, kind: &str, actual: &str, expected: &str) -> usize {
+    fn compare_output(&self, stream: &str, actual: &str, expected: &str) -> usize {
         let are_different = match (self.force_color_svg(), expected.find('\n'), actual.find('\n')) {
             // FIXME: We ignore the first line of SVG files
             // because the width parameter is non-deterministic.
@@ -2499,56 +2518,66 @@ impl<'test> TestCx<'test> {
             (expected, actual)
         };
 
+        // Write the actual output to a file in build/
+        let test_name = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
+        let actual_path = self
+            .output_base_name()
+            .with_extra_extension(self.revision.unwrap_or(""))
+            .with_extra_extension(test_name)
+            .with_extra_extension(stream);
+
+        if let Err(err) = fs::write(&actual_path, &actual) {
+            self.fatal(&format!("failed to write {stream} to `{actual_path:?}`: {err}",));
+        }
+        println!("Saved the actual {stream} to {actual_path:?}");
+
+        let expected_path =
+            expected_output_path(self.testpaths, self.revision, &self.config.compare_mode, stream);
+
         if !self.config.bless {
             if expected.is_empty() {
-                println!("normalized {}:\n{}\n", kind, actual);
+                println!("normalized {}:\n{}\n", stream, actual);
             } else {
-                println!("diff of {}:\n", kind);
-                print!("{}", write_diff(expected, actual, 3));
+                println!("diff of {stream}:\n");
+                if let Some(diff_command) = self.config.diff_command.as_deref() {
+                    let mut args = diff_command.split_whitespace();
+                    let name = args.next().unwrap();
+                    match Command::new(name)
+                        .args(args)
+                        .args([&expected_path, &actual_path])
+                        .output()
+                    {
+                        Err(err) => {
+                            self.fatal(&format!(
+                                "failed to call custom diff command `{diff_command}`: {err}"
+                            ));
+                        }
+                        Ok(output) => {
+                            let output = String::from_utf8_lossy(&output.stdout);
+                            print!("{output}");
+                        }
+                    }
+                } else {
+                    print!("{}", write_diff(expected, actual, 3));
+                }
             }
-        }
-
-        let mode = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
-        let output_file = self
-            .output_base_name()
-            .with_extra_extension(self.revision.unwrap_or(""))
-            .with_extra_extension(mode)
-            .with_extra_extension(kind);
-
-        let mut files = vec![output_file];
-        if self.config.bless {
+        } else {
             // Delete non-revision .stderr/.stdout file if revisions are used.
             // Without this, we'd just generate the new files and leave the old files around.
             if self.revision.is_some() {
                 let old =
-                    expected_output_path(self.testpaths, None, &self.config.compare_mode, kind);
+                    expected_output_path(self.testpaths, None, &self.config.compare_mode, stream);
                 self.delete_file(&old);
             }
-            files.push(expected_output_path(
-                self.testpaths,
-                self.revision,
-                &self.config.compare_mode,
-                kind,
-            ));
-        }
 
-        for output_file in &files {
-            if actual.is_empty() {
-                self.delete_file(output_file);
-            } else if let Err(err) = fs::write(&output_file, &actual) {
-                self.fatal(&format!(
-                    "failed to write {} to `{}`: {}",
-                    kind,
-                    output_file.display(),
-                    err,
-                ));
+            if let Err(err) = fs::write(&expected_path, &actual) {
+                self.fatal(&format!("failed to write {stream} to `{expected_path:?}`: {err}"));
             }
+            println!("Blessing the {stream} of {test_name} in {expected_path:?}");
         }
 
-        println!("\nThe actual {0} differed from the expected {0}.", kind);
-        for output_file in files {
-            println!("Actual {} saved to {}", kind, output_file.display());
-        }
+        println!("\nThe actual {0} differed from the expected {0}.", stream);
+
         if self.config.bless { 0 } else { 1 }
     }
 
diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs
index 985fe6381e8..c15422fb6f6 100644
--- a/src/tools/compiletest/src/runtest/debugger.rs
+++ b/src/tools/compiletest/src/runtest/debugger.rs
@@ -4,7 +4,6 @@ use std::io::{BufRead, BufReader};
 use std::path::{Path, PathBuf};
 
 use crate::common::Config;
-use crate::header::line_directive;
 use crate::runtest::ProcRes;
 
 /// Representation of information to invoke a debugger and check its output
@@ -24,7 +23,6 @@ impl DebuggerCommands {
         file: &Path,
         config: &Config,
         debugger_prefixes: &[&str],
-        rev: Option<&str>,
     ) -> Result<Self, String> {
         let directives = debugger_prefixes
             .iter()
@@ -39,18 +37,17 @@ impl DebuggerCommands {
         for (line_no, line) in reader.lines().enumerate() {
             counter += 1;
             let line = line.map_err(|e| format!("Error while parsing debugger commands: {}", e))?;
-            let (lnrev, line) = line_directive("//", &line).unwrap_or((None, &line));
-
-            // Skip any revision specific directive that doesn't match the current
-            // revision being tested
-            if lnrev.is_some() && lnrev != rev {
-                continue;
-            }
 
+            // Breakpoints appear on lines with actual code, typically at the end of the line.
             if line.contains("#break") {
                 breakpoint_lines.push(counter);
+                continue;
             }
 
+            let Some(line) = line.trim_start().strip_prefix("//").map(str::trim_start) else {
+                continue;
+            };
+
             for &(ref command_directive, ref check_directive) in &directives {
                 config
                     .parse_name_value_directive(&line, command_directive)
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
index bd0845b4524..c621c22ac99 100644
--- a/src/tools/compiletest/src/runtest/debuginfo.rs
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -66,13 +66,8 @@ impl TestCx<'_> {
         };
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(
-            &self.testpaths.file,
-            self.config,
-            prefixes,
-            self.revision,
-        )
-        .unwrap_or_else(|e| self.fatal(&e));
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, prefixes)
+            .unwrap_or_else(|e| self.fatal(&e));
 
         // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
         let mut script_str = String::with_capacity(2048);
@@ -142,13 +137,8 @@ impl TestCx<'_> {
     }
 
     fn run_debuginfo_gdb_test_no_opt(&self) {
-        let dbg_cmds = DebuggerCommands::parse_from(
-            &self.testpaths.file,
-            self.config,
-            &["gdb"],
-            self.revision,
-        )
-        .unwrap_or_else(|e| self.fatal(&e));
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, &["gdb"])
+            .unwrap_or_else(|e| self.fatal(&e));
         let mut cmds = dbg_cmds.commands.join("\n");
 
         // compile test file (it should have 'compile-flags:-g' in the header)
@@ -413,13 +403,8 @@ impl TestCx<'_> {
         }
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(
-            &self.testpaths.file,
-            self.config,
-            &["lldb"],
-            self.revision,
-        )
-        .unwrap_or_else(|e| self.fatal(&e));
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, &["lldb"])
+            .unwrap_or_else(|e| self.fatal(&e));
 
         // Write debugger script:
         // We don't want to hang when calling `quit` while the process is still running
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index f8ffd0fbe3f..04bc2d7787d 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -2,7 +2,7 @@ use std::path::Path;
 use std::process::{Command, Output, Stdio};
 use std::{env, fs};
 
-use super::{ProcRes, TestCx};
+use super::{ProcRes, TestCx, disable_error_reporting};
 use crate::util::{copy_dir_all, dylib_env_var};
 
 impl TestCx<'_> {
@@ -329,6 +329,7 @@ impl TestCx<'_> {
             .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy()))
             .arg("--edition=2021")
             .arg(&self.testpaths.file.join("rmake.rs"))
+            .arg("-Cprefer-dynamic")
             // Provide necessary library search paths for rustc.
             .env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap());
 
@@ -514,8 +515,8 @@ impl TestCx<'_> {
             }
         }
 
-        let (Output { stdout, stderr, status }, truncated) =
-            self.read2_abbreviated(cmd.spawn().expect("failed to spawn `rmake`"));
+        let proc = disable_error_reporting(|| cmd.spawn().expect("failed to spawn `rmake`"));
+        let (Output { stdout, stderr, status }, truncated) = self.read2_abbreviated(proc);
         if !status.success() {
             let res = ProcRes {
                 status,
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index 8b0916f5111..2e491319822 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -1,11 +1,7 @@
 name: CI
 
 on:
-  push:
-    # Run in PRs and for bors, but not on master.
-    branches:
-      - 'auto'
-      - 'try'
+  merge_group:
   pull_request:
     branches:
       - 'master'
@@ -38,7 +34,7 @@ jobs:
       # The `style` job only runs on Linux; this makes sure the Windows-host-specific
       # code is also covered by clippy.
       - name: Check clippy
-        if: matrix.os == 'windows-latest'
+        if: ${{ matrix.os == 'windows-latest' }}
         run: ./miri clippy -- -D warnings
 
       - name: Test Miri
@@ -62,27 +58,25 @@ jobs:
       - name: rustdoc
         run: RUSTDOCFLAGS="-Dwarnings" ./miri doc --document-private-items
 
-  # These jobs doesn't actually test anything, but they're only used to tell
-  # bors the build completed, as there is no practical way to detect when a
-  # workflow is successful listening to webhooks only.
-  #
+  # Summary job for the merge queue.
   # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
-  end-success:
-    name: bors build finished
-    runs-on: ubuntu-latest
+  # And they should be added below in `cron-fail-notify` as well.
+  conclusion:
     needs: [build, style]
-    if: github.event.pusher.name == 'bors' && success()
-    steps:
-      - name: mark the job as a success
-        run: exit 0
-  end-failure:
-    name: bors build finished
+    # We need to ensure this job does *not* get skipped if its dependencies fail,
+    # because a skipped job is considered a success by GitHub. So we have to
+    # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
+    # when the workflow is canceled manually.
+    if: ${{ !cancelled() }}
     runs-on: ubuntu-latest
-    needs: [build, style]
-    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
     steps:
-      - name: mark the job as a failure
-        run: exit 1
+      # Manually check the status of all dependencies. `if: failure()` does not work.
+      - name: Conclusion
+        run: |
+            # Print the dependent jobs to see them in the CI log
+            jq -C <<< '${{ toJson(needs) }}'
+            # Check if all jobs that we depend on (in the needs array) were successful.
+            jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
 
   cron-fail-notify:
     name: cronjob failure notification
@@ -93,7 +87,7 @@ jobs:
         # ... and create a PR.
         pull-requests: write
     needs: [build, style]
-    if: github.event_name == 'schedule' && failure()
+    if: ${{ github.event_name == 'schedule' && failure() }}
     steps:
       # Send a Zulip notification
       - name: Install zulip-send
@@ -145,7 +139,7 @@ jobs:
           git push -u origin $BRANCH
       - name: Create Pull Request
         run: |
-          PR=$(gh pr create -B master --title 'Automatic Rustup' --body '')
+          PR=$(gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.')
           ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \
             --stream miri --subject "Miri Build Failure ($(date -u +%Y-%m))" \
             --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience."
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh
index ad1b2f4d0c3..4e7cbc50ca0 100755
--- a/src/tools/miri/ci/ci.sh
+++ b/src/tools/miri/ci/ci.sh
@@ -154,7 +154,7 @@ case $HOST_TARGET in
     TEST_TARGET=i686-unknown-freebsd   run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
     TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
     TEST_TARGET=x86_64-pc-solaris      run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
-    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap pthread --skip threadname
+    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap threadname pthread
     TEST_TARGET=wasm32-wasip2          run_tests_minimal $BASIC wasm
     TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
     TEST_TARGET=thumbv7em-none-eabihf  run_tests_minimal no_std
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 8b9e7efdff9..133edd3191d 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-17a19e684cdf3ca088af8b4da6a6209d128913f4
+814df6e50eaf89b90793e7d9618bb60f1f18377a
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
index 1684abeec6b..5624c4c479e 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
@@ -424,7 +424,11 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> {
     }
 
     #[inline(never)] // This is only called on fatal code paths
-    pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpErrorKind<'tcx> {
+    pub(super) fn protector_error(
+        &self,
+        item: &Item,
+        kind: ProtectorKind,
+    ) -> InterpErrorKind<'tcx> {
         let protected = match kind {
             ProtectorKind::WeakProtector => "weakly protected",
             ProtectorKind::StrongProtector => "strongly protected",
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 09ec2cb46b0..776d2561b43 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -145,6 +145,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(Scalar::from_bool(branch), dest)?;
             }
 
+            "floorf16" | "ceilf16" | "truncf16" | "roundf16" | "rintf16" => {
+                let [f] = check_arg_count(args)?;
+                let f = this.read_scalar(f)?.to_f16()?;
+                let mode = match intrinsic_name {
+                    "floorf16" => Round::TowardNegative,
+                    "ceilf16" => Round::TowardPositive,
+                    "truncf16" => Round::TowardZero,
+                    "roundf16" => Round::NearestTiesToAway,
+                    "rintf16" => Round::NearestTiesToEven,
+                    _ => bug!(),
+                };
+                let res = f.round_to_integral(mode).value;
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
             "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
@@ -175,6 +190,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
+            "floorf128" | "ceilf128" | "truncf128" | "roundf128" | "rintf128" => {
+                let [f] = check_arg_count(args)?;
+                let f = this.read_scalar(f)?.to_f128()?;
+                let mode = match intrinsic_name {
+                    "floorf128" => Round::TowardNegative,
+                    "ceilf128" => Round::TowardPositive,
+                    "truncf128" => Round::TowardZero,
+                    "roundf128" => Round::NearestTiesToAway,
+                    "rintf128" => Round::NearestTiesToEven,
+                    _ => bug!(),
+                };
+                let res = f.round_to_integral(mode).value;
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
 
             #[rustfmt::skip]
             | "sinf32"
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index 0799b93dbb0..f15b83a054f 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -1,7 +1,8 @@
 use either::Either;
 use rustc_apfloat::{Float, Round};
+use rustc_middle::ty::FloatTy;
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_middle::{mir, ty, ty::FloatTy};
+use rustc_middle::{mir, ty};
 use rustc_span::{Symbol, sym};
 use rustc_target::abi::{Endian, HasDataLayout};
 
@@ -630,12 +631,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let (right, right_len) = this.project_to_simd(right)?;
                 let (dest, dest_len) = this.project_to_simd(dest)?;
 
-                let index = generic_args[2]
-                    .expect_const()
-                    .try_to_valtree()
-                    .unwrap()
-                    .0
-                    .unwrap_branch();
+                let index =
+                    generic_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
                 let index_len = index.len();
 
                 assert_eq!(left_len, right_len);
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 660f2e493bc..938d1ca319e 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -147,7 +147,7 @@ pub use crate::range_map::RangeMap;
 pub use crate::shims::EmulateItemResult;
 pub use crate::shims::env::{EnvVars, EvalContextExt as _};
 pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
-pub use crate::shims::io_error::{EvalContextExt as _, LibcError};
+pub use crate::shims::io_error::{EvalContextExt as _, IoError, LibcError};
 pub use crate::shims::os_str::EvalContextExt as _;
 pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
 pub use crate::shims::time::EvalContextExt as _;
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index f6f91e58969..12f8facfd02 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -447,8 +447,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 } else {
                     // If this does not fit in an isize, return null and, on Unix, set errno.
                     if this.target_os_is_unix() {
-                        let einval = this.eval_libc("ENOMEM");
-                        this.set_last_error(einval)?;
+                        this.set_last_error(LibcError("ENOMEM"))?;
                     }
                     this.write_null(dest)?;
                 }
@@ -464,8 +463,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 } else {
                     // On size overflow, return null and, on Unix, set errno.
                     if this.target_os_is_unix() {
-                        let einval = this.eval_libc("ENOMEM");
-                        this.set_last_error(einval)?;
+                        this.set_last_error(LibcError("ENOMEM"))?;
                     }
                     this.write_null(dest)?;
                 }
@@ -486,8 +484,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 } else {
                     // If this does not fit in an isize, return null and, on Unix, set errno.
                     if this.target_os_is_unix() {
-                        let einval = this.eval_libc("ENOMEM");
-                        this.set_last_error(einval)?;
+                        this.set_last_error(LibcError("ENOMEM"))?;
                     }
                     this.write_null(dest)?;
                 }
diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs
index 38aa181cb4f..04491f0542b 100644
--- a/src/tools/miri/src/shims/io_error.rs
+++ b/src/tools/miri/src/shims/io_error.rs
@@ -141,6 +141,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(Scalar::from_i32(-1))
     }
 
+    /// Sets the last OS error and return `-1` as a `i64`-typed Scalar
+    fn set_last_error_and_return_i64(
+        &mut self,
+        err: impl Into<IoError>,
+    ) -> InterpResult<'tcx, Scalar> {
+        let this = self.eval_context_mut();
+        this.set_last_error(err)?;
+        interp_ok(Scalar::from_i64(-1))
+    }
+
     /// Gets the last error variable.
     fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs
index 12c7679608d..6436823b0fd 100644
--- a/src/tools/miri/src/shims/time.rs
+++ b/src/tools/miri/src/shims/time.rs
@@ -81,9 +81,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         } else if relative_clocks.contains(&clk_id) {
             this.machine.clock.now().duration_since(this.machine.clock.epoch())
         } else {
-            let einval = this.eval_libc("EINVAL");
-            this.set_last_error(einval)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         };
 
         let tv_sec = duration.as_secs();
@@ -109,9 +107,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Using tz is obsolete and should always be null
         let tz = this.read_pointer(tz_op)?;
         if !this.ptr_is_null(tz)? {
-            let einval = this.eval_libc("EINVAL");
-            this.set_last_error(einval)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         let duration = system_time_to_duration(&SystemTime::now())?;
@@ -323,9 +319,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let duration = match this.read_timespec(&req)? {
             Some(duration) => duration,
             None => {
-                let einval = this.eval_libc("EINVAL");
-                this.set_last_error(einval)?;
-                return interp_ok(Scalar::from_i32(-1));
+                return this.set_last_error_and_return_i32(LibcError("EINVAL"));
             }
         };
 
diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs
index 583a1f65009..b6f04951fc7 100644
--- a/src/tools/miri/src/shims/unix/android/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs
@@ -1,6 +1,7 @@
 use rustc_span::Symbol;
 use rustc_target::spec::abi::Abi;
 
+use crate::shims::unix::android::thread::prctl;
 use crate::*;
 
 pub fn is_dyn_sym(_name: &str) -> bool {
@@ -25,6 +26,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
             }
 
+            // Threading
+            "prctl" => prctl(this, link_name, abi, args, dest)?,
+
             _ => return interp_ok(EmulateItemResult::NotSupported),
         }
         interp_ok(EmulateItemResult::NeedsReturn)
diff --git a/src/tools/miri/src/shims/unix/android/mod.rs b/src/tools/miri/src/shims/unix/android/mod.rs
index 09c6507b24f..1f2a74bac59 100644
--- a/src/tools/miri/src/shims/unix/android/mod.rs
+++ b/src/tools/miri/src/shims/unix/android/mod.rs
@@ -1 +1,2 @@
 pub mod foreign_items;
+pub mod thread;
diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs
new file mode 100644
index 00000000000..6f5f0f74a22
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/android/thread.rs
@@ -0,0 +1,57 @@
+use rustc_span::Symbol;
+use rustc_target::abi::Size;
+use rustc_target::spec::abi::Abi;
+
+use crate::helpers::check_min_arg_count;
+use crate::shims::unix::thread::EvalContextExt as _;
+use crate::*;
+
+const TASK_COMM_LEN: usize = 16;
+
+pub fn prctl<'tcx>(
+    this: &mut MiriInterpCx<'tcx>,
+    link_name: Symbol,
+    abi: Abi,
+    args: &[OpTy<'tcx>],
+    dest: &MPlaceTy<'tcx>,
+) -> InterpResult<'tcx> {
+    // We do not use `check_shim` here because `prctl` is variadic. The argument
+    // count is checked bellow.
+    this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
+
+    // FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch.
+    let pr_set_name = 15;
+    let pr_get_name = 16;
+
+    let [op] = check_min_arg_count("prctl", args)?;
+    let res = match this.read_scalar(op)?.to_i32()? {
+        op if op == pr_set_name => {
+            let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?;
+            let name = this.read_scalar(name)?;
+            let thread = this.pthread_self()?;
+            // The Linux kernel silently truncates long names.
+            // https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html
+            let res =
+                this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
+            assert!(res);
+            Scalar::from_u32(0)
+        }
+        op if op == pr_get_name => {
+            let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?;
+            let name = this.read_scalar(name)?;
+            let thread = this.pthread_self()?;
+            let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, this);
+            this.check_ptr_access(
+                name.to_pointer(this)?,
+                Size::from_bytes(TASK_COMM_LEN),
+                CheckInAllocMsg::MemoryAccessTest,
+            )?;
+            let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
+            assert!(res);
+            Scalar::from_u32(0)
+        }
+        op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),
+    };
+    this.write_scalar(res, dest)?;
+    interp_ok(())
+}
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index e3914640037..f3db56695fc 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -423,7 +423,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         let Some(fd) = this.machine.fds.get(old_fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         interp_ok(Scalar::from_i32(this.machine.fds.insert(fd)))
     }
@@ -432,7 +432,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         let Some(fd) = this.machine.fds.get(old_fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         if new_fd_num != old_fd_num {
             // Close new_fd if it is previously opened.
@@ -448,7 +448,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn flock(&mut self, fd_num: i32, op: i32) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
 
         // We need to check that there aren't unsupported options in `op`.
@@ -498,11 +498,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
                 // always sets this flag when opening a file. However we still need to check that the
                 // file itself is open.
-                interp_ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) {
-                    this.eval_libc_i32("FD_CLOEXEC")
+                if !this.machine.fds.is_fd_num(fd_num) {
+                    this.set_last_error_and_return_i32(LibcError("EBADF"))
                 } else {
-                    this.fd_not_found()?
-                }))
+                    interp_ok(this.eval_libc("FD_CLOEXEC"))
+                }
             }
             cmd if cmd == f_dupfd || cmd == f_dupfd_cloexec => {
                 // Note that we always assume the FD_CLOEXEC flag is set for every open file, in part
@@ -521,7 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 if let Some(fd) = this.machine.fds.get(fd_num) {
                     interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start)))
                 } else {
-                    interp_ok(Scalar::from_i32(this.fd_not_found()?))
+                    this.set_last_error_and_return_i32(LibcError("EBADF"))
                 }
             }
             cmd if this.tcx.sess.target.os == "macos"
@@ -547,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let fd_num = this.read_scalar(fd_op)?.to_i32()?;
 
         let Some(fd) = this.machine.fds.remove(fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         let result = fd.close(this.machine.communicate(), this)?;
         // return `0` if close is successful
@@ -555,17 +555,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
     }
 
-    /// Function used when a file descriptor does not exist. It returns `Ok(-1)`and sets
-    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
-    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
-    /// types (like `read`, that returns an `i64`).
-    fn fd_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
-        let this = self.eval_context_mut();
-        let ebadf = this.eval_libc("EBADF");
-        this.set_last_error(ebadf)?;
-        interp_ok((-1).into())
-    }
-
     /// Read data from `fd` into buffer specified by `buf` and `count`.
     ///
     /// If `offset` is `None`, reads data from current cursor position associated with `fd`
@@ -599,9 +588,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // We temporarily dup the FD to be able to retain mutable access to `this`.
         let Some(fd) = this.machine.fds.get(fd_num) else {
             trace!("read: FD not found");
-            let res: i32 = this.fd_not_found()?;
-            this.write_int(res, dest)?;
-            return interp_ok(());
+            return this.set_last_error_and_return(LibcError("EBADF"), dest);
         };
 
         trace!("read: FD mapped to {fd:?}");
@@ -646,9 +633,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // We temporarily dup the FD to be able to retain mutable access to `this`.
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            let res: i32 = this.fd_not_found()?;
-            this.write_int(res, dest)?;
-            return interp_ok(());
+            return this.set_last_error_and_return(LibcError("EBADF"), dest);
         };
 
         match offset {
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 7ba98981920..355c93c444c 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -362,8 +362,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray
                 match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) {
                     None => {
-                        let enmem = this.eval_libc("ENOMEM");
-                        this.set_last_error(enmem)?;
+                        this.set_last_error(LibcError("ENOMEM"))?;
                         this.write_null(dest)?;
                     }
                     Some(len) => {
@@ -653,13 +652,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let chunk_size = CpuAffinityMask::chunk_size(this);
 
                 if this.ptr_is_null(mask)? {
-                    let efault = this.eval_libc("EFAULT");
-                    this.set_last_error(efault)?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
                 } else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 {
                     // we only copy whole chunks of size_of::<c_ulong>()
-                    this.set_last_error(LibcError("EINVAL"))?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
                 } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) {
                     let cpuset = cpuset.clone();
                     // we only copy whole chunks of size_of::<c_ulong>()
@@ -668,9 +664,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.write_null(dest)?;
                 } else {
                     // The thread whose ID is pid could not be found
-                    let esrch = this.eval_libc("ESRCH");
-                    this.set_last_error(esrch)?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("ESRCH"), dest)?;
                 }
             }
             "sched_setaffinity" => {
@@ -695,9 +689,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 };
 
                 if this.ptr_is_null(mask)? {
-                    let efault = this.eval_libc("EFAULT");
-                    this.set_last_error(efault)?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
                 } else {
                     // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
                     // Any unspecified bytes are treated as zero here (none of the CPUs are configured).
@@ -713,8 +705,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         }
                         None => {
                             // The intersection between the mask and the available CPUs was empty.
-                            this.set_last_error(LibcError("EINVAL"))?;
-                            this.write_int(-1, dest)?;
+                            this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
                         }
                     }
                 }
@@ -770,9 +761,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // macOS: https://keith.github.io/xcode-man-pages/getentropy.2.html
                 // Solaris/Illumos: https://illumos.org/man/3C/getentropy
                 if bufsize > 256 {
-                    let err = this.eval_libc("EIO");
-                    this.set_last_error(err)?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("EIO"), dest)?;
                 } else {
                     this.gen_random(buf, bufsize)?;
                     this.write_null(dest)?;
diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
index 5204e57705a..71953aca989 100644
--- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
@@ -29,6 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     max_len,
+                    /* truncate */ false,
                 )?;
             }
             "pthread_get_name_np" => {
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 4b3ae8e0520..f7436d7f089 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -149,6 +149,7 @@ impl FileDescription for FileHandle {
             // to handle possible errors correctly.
             let result = self.file.sync_all();
             // Now we actually close the file and return the result.
+            drop(*self);
             interp_ok(result)
         } else {
             // We drop the file, this closes it but ignores any errors
@@ -157,6 +158,7 @@ impl FileDescription for FileHandle {
             // `/dev/urandom` which are read-only. Check
             // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
             // for a deeper discussion.
+            drop(*self);
             interp_ok(Ok(()))
         }
     }
@@ -229,6 +231,8 @@ impl FileDescription for FileHandle {
                 TRUE => Ok(()),
                 FALSE => {
                     let mut err = io::Error::last_os_error();
+                    // This only runs on Windows hosts so we can use `raw_os_error`.
+                    // We have to be careful not to forward that error code to target code.
                     let code: u32 = err.raw_os_error().unwrap().try_into().unwrap();
                     if matches!(code, ERROR_IO_PENDING | ERROR_LOCK_VIOLATION) {
                         if lock_nb {
@@ -337,15 +341,10 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     _ => interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()),
                 }
             }
-            Err(e) =>
-                match e.raw_os_error() {
-                    Some(error) => interp_ok(error),
-                    None =>
-                        throw_unsup_format!(
-                            "the error {} couldn't be converted to a return value",
-                            e
-                        ),
-                },
+            Err(_) => {
+                // Fallback on error
+                interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into())
+            }
         }
     }
 }
@@ -528,8 +527,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             let o_tmpfile = this.eval_libc_i32("O_TMPFILE");
             if flag & o_tmpfile == o_tmpfile {
                 // if the flag contains `O_TMPFILE` then we return a graceful error
-                this.set_last_error(LibcError("EOPNOTSUPP"))?;
-                return interp_ok(Scalar::from_i32(-1));
+                return this.set_last_error_and_return_i32(LibcError("EOPNOTSUPP"));
             }
         }
 
@@ -548,9 +546,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // O_NOFOLLOW only fails when the trailing component is a symlink;
                 // the entire rest of the path can still contain symlinks.
                 if path.is_symlink() {
-                    let eloop = this.eval_libc("ELOOP");
-                    this.set_last_error(eloop)?;
-                    return interp_ok(Scalar::from_i32(-1));
+                    return this.set_last_error_and_return_i32(LibcError("ELOOP"));
                 }
             }
             mirror |= o_nofollow;
@@ -565,8 +561,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`open`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let fd = options
@@ -584,8 +579,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let seek_from = if whence == this.eval_libc_i32("SEEK_SET") {
             if offset < 0 {
                 // Negative offsets return `EINVAL`.
-                this.set_last_error(LibcError("EINVAL"))?;
-                return interp_ok(Scalar::from_i64(-1));
+                return this.set_last_error_and_return_i64(LibcError("EINVAL"));
             } else {
                 SeekFrom::Start(u64::try_from(offset).unwrap())
             }
@@ -594,14 +588,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         } else if whence == this.eval_libc_i32("SEEK_END") {
             SeekFrom::End(i64::try_from(offset).unwrap())
         } else {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i64(-1));
+            return this.set_last_error_and_return_i64(LibcError("EINVAL"));
         };
 
         let communicate = this.machine.communicate();
 
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            return interp_ok(Scalar::from_i64(this.fd_not_found()?));
+            return this.set_last_error_and_return_i64(LibcError("EBADF"));
         };
         let result = fd.seek(communicate, seek_from)?.map(|offset| i64::try_from(offset).unwrap());
         drop(fd);
@@ -618,8 +611,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`unlink`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let result = remove_file(path).map(|_| 0);
@@ -649,8 +641,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`symlink`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let result = create_link(&target, &linkpath).map(|_| 0);
@@ -674,15 +665,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`stat`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EACCES"));
         }
 
         // `stat` always follows symlinks.
         let metadata = match FileMetadata::from_path(this, &path, true)? {
-            Some(metadata) => metadata,
-            None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno
+            Ok(metadata) => metadata,
+            Err(err) => return this.set_last_error_and_return_i32(err),
         };
 
         interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
@@ -706,14 +695,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`lstat`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EACCES"));
         }
 
         let metadata = match FileMetadata::from_path(this, &path, false)? {
-            Some(metadata) => metadata,
-            None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno
+            Ok(metadata) => metadata,
+            Err(err) => return this.set_last_error_and_return_i32(err),
         };
 
         interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
@@ -736,12 +723,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`fstat`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         let metadata = match FileMetadata::from_fd_num(this, fd)? {
-            Some(metadata) => metadata,
-            None => return interp_ok(Scalar::from_i32(-1)),
+            Ok(metadata) => metadata,
+            Err(err) => return this.set_last_error_and_return_i32(err),
         };
         interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
     }
@@ -766,9 +753,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // If the statxbuf or pathname pointers are null, the function fails with `EFAULT`.
         if this.ptr_is_null(statxbuf_ptr)? || this.ptr_is_null(pathname_ptr)? {
-            let efault = this.eval_libc("EFAULT");
-            this.set_last_error(efault)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EFAULT"));
         }
 
         let statxbuf = this.deref_pointer_as(statxbuf_op, this.libc_ty_layout("statx"))?;
@@ -801,16 +786,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             let ecode = if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD") {
                 // since `path` is provided, either absolute or
                 // relative to CWD, `EACCES` is the most relevant.
-                this.eval_libc("EACCES")
+                LibcError("EACCES")
             } else {
                 // `dirfd` is set to target file, and `path` is empty
                 // (or we would have hit the `throw_unsup_format`
                 // above). `EACCES` would violate the spec.
                 assert!(empty_path_flag);
-                this.eval_libc("EBADF")
+                LibcError("EBADF")
             };
-            this.set_last_error(ecode)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ecode);
         }
 
         // the `_mask_op` parameter specifies the file information that the caller requested.
@@ -831,8 +815,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             FileMetadata::from_path(this, &path, follow_symlink)?
         };
         let metadata = match metadata {
-            Some(metadata) => metadata,
-            None => return interp_ok(Scalar::from_i32(-1)),
+            Ok(metadata) => metadata,
+            Err(err) => return this.set_last_error_and_return_i32(err),
         };
 
         // The `mode` field specifies the type of the file and the permissions over the file for
@@ -939,9 +923,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let newpath_ptr = this.read_pointer(newpath_op)?;
 
         if this.ptr_is_null(oldpath_ptr)? || this.ptr_is_null(newpath_ptr)? {
-            let efault = this.eval_libc("EFAULT");
-            this.set_last_error(efault)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EFAULT"));
         }
 
         let oldpath = this.read_path_from_c_str(oldpath_ptr)?;
@@ -950,8 +932,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`rename`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let result = rename(oldpath, newpath).map(|_| 0);
@@ -974,8 +955,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`mkdir`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         #[cfg_attr(not(unix), allow(unused_mut))]
@@ -1002,8 +982,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`rmdir`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let result = remove_dir(path).map(|_| 0i32);
@@ -1019,8 +998,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`opendir`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
+            this.set_last_error(LibcError("EACCES"))?;
             return interp_ok(Scalar::null_ptr(this));
         }
 
@@ -1052,8 +1030,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`readdir`", reject_with)?;
-            let eacc = this.eval_libc("EBADF");
-            this.set_last_error(eacc)?;
+            this.set_last_error(LibcError("EBADF"))?;
             return interp_ok(Scalar::null_ptr(this));
         }
 
@@ -1152,14 +1129,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`readdir_r`", reject_with)?;
-            // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            // Return error code, do *not* set `errno`.
+            return interp_ok(this.eval_libc("EBADF"));
         }
 
         let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| {
             err_unsup_format!("the DIR pointer passed to readdir_r did not come from opendir")
         })?;
-        interp_ok(Scalar::from_i32(match open_dir.read_dir.next() {
+        interp_ok(match open_dir.read_dir.next() {
             Some(Ok(dir_entry)) => {
                 // Write into entry, write pointer to result, return 0 on success.
                 // The name is written with write_os_str_to_c_str, while the rest of the
@@ -1237,25 +1214,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let result_place = this.deref_pointer(result_op)?;
                 this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;
 
-                0
+                Scalar::from_i32(0)
             }
             None => {
                 // end of stream: return 0, assign *result=NULL
                 this.write_null(&this.deref_pointer(result_op)?)?;
-                0
+                Scalar::from_i32(0)
             }
-            Some(Err(e)) =>
-                match e.raw_os_error() {
-                    // return positive error number on error
-                    Some(error) => error,
-                    None => {
-                        throw_unsup_format!(
-                            "the error {} couldn't be converted to a return value",
-                            e
-                        )
-                    }
-                },
-        }))
+            Some(Err(e)) => {
+                // return positive error number on error (do *not* set last error)
+                this.io_error_to_errnum(e)?
+            }
+        })
     }
 
     fn closedir(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
@@ -1264,20 +1234,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let dirp = this.read_target_usize(dirp_op)?;
 
         // Reject if isolation is enabled.
-        interp_ok(Scalar::from_i32(
-            if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
-                this.reject_in_isolation("`closedir`", reject_with)?;
-                this.fd_not_found()?
-            } else if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) {
-                if let Some(entry) = open_dir.entry {
-                    this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?;
-                }
-                drop(open_dir);
-                0
-            } else {
-                this.fd_not_found()?
-            },
-        ))
+        if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
+            this.reject_in_isolation("`closedir`", reject_with)?;
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
+        }
+
+        let Some(mut open_dir) = this.machine.dirs.streams.remove(&dirp) else {
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
+        };
+        if let Some(entry) = open_dir.entry.take() {
+            this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?;
+        }
+        // We drop the `open_dir`, which will close the host dir handle.
+        drop(open_dir);
+
+        interp_ok(Scalar::from_i32(0))
     }
 
     fn ftruncate64(&mut self, fd_num: i32, length: i128) -> InterpResult<'tcx, Scalar> {
@@ -1287,11 +1258,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`ftruncate64`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
 
         // FIXME: Support ftruncate64 for all FDs
@@ -1307,14 +1278,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 interp_ok(Scalar::from_i32(result))
             } else {
                 drop(fd);
-                this.set_last_error(LibcError("EINVAL"))?;
-                interp_ok(Scalar::from_i32(-1))
+                this.set_last_error_and_return_i32(LibcError("EINVAL"))
             }
         } else {
             drop(fd);
             // The file is not writable
-            this.set_last_error(LibcError("EINVAL"))?;
-            interp_ok(Scalar::from_i32(-1))
+            this.set_last_error_and_return_i32(LibcError("EINVAL"))
         }
     }
 
@@ -1332,7 +1301,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`fsync`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         self.ffullsync_fd(fd)
@@ -1341,7 +1310,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn ffullsync_fd(&mut self, fd_num: i32) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         // Only regular files support synchronization.
         let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
@@ -1361,11 +1330,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`fdatasync`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         let Some(fd) = this.machine.fds.get(fd) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         // Only regular files support synchronization.
         let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
@@ -1391,26 +1360,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let flags = this.read_scalar(flags_op)?.to_i32()?;
 
         if offset < 0 || nbytes < 0 {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
         let allowed_flags = this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_BEFORE")
             | this.eval_libc_i32("SYNC_FILE_RANGE_WRITE")
             | this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_AFTER");
         if flags & allowed_flags != flags {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`sync_file_range`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         let Some(fd) = this.machine.fds.get(fd) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         // Only regular files support synchronization.
         let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
@@ -1436,8 +1403,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`readlink`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
+            this.set_last_error(LibcError("EACCES"))?;
             return interp_ok(-1);
         }
 
@@ -1475,11 +1441,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             if fd.is_tty(this.machine.communicate()) {
                 return interp_ok(Scalar::from_i32(1));
             } else {
-                this.eval_libc("ENOTTY")
+                LibcError("ENOTTY")
             }
         } else {
             // FD does not exist
-            this.eval_libc("EBADF")
+            LibcError("EBADF")
         };
         this.set_last_error(error)?;
         interp_ok(Scalar::from_i32(0))
@@ -1499,8 +1465,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`realpath`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
+            this.set_last_error(LibcError("EACCES"))?;
             return interp_ok(Scalar::from_target_usize(0, this));
         }
 
@@ -1530,8 +1495,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         // Note that we do not explicitly handle `FILENAME_MAX`
                         // (different from `PATH_MAX` above) as it is Linux-specific and
                         // seems like a bit of a mess anyway: <https://eklitzke.org/path-max-is-tricky>.
-                        let enametoolong = this.eval_libc("ENAMETOOLONG");
-                        this.set_last_error(enametoolong)?;
+                        this.set_last_error(LibcError("ENAMETOOLONG"))?;
                         return interp_ok(Scalar::from_target_usize(0, this));
                     }
                     processed_ptr
@@ -1574,9 +1538,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`mkstemp`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EACCES"));
         }
 
         // Get the bytes of the suffix we expect in _target_ encoding.
@@ -1592,8 +1554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // If we don't find the suffix, it is an error.
         if last_six_char_bytes != suffix_bytes {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         // At this point we know we have 6 ASCII 'X' characters as a suffix.
@@ -1658,17 +1619,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         _ => {
                             // "On error, -1 is returned, and errno is set to
                             // indicate the error"
-                            this.set_last_error(e)?;
-                            return interp_ok(Scalar::from_i32(-1));
+                            return this.set_last_error_and_return_i32(e);
                         }
                     },
             }
         }
 
         // We ran out of attempts to create the file, return an error.
-        let eexist = this.eval_libc("EEXIST");
-        this.set_last_error(eexist)?;
-        interp_ok(Scalar::from_i32(-1))
+        this.set_last_error_and_return_i32(LibcError("EEXIST"))
     }
 }
 
@@ -1702,7 +1660,7 @@ impl FileMetadata {
         ecx: &mut MiriInterpCx<'tcx>,
         path: &Path,
         follow_symlink: bool,
-    ) -> InterpResult<'tcx, Option<FileMetadata>> {
+    ) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
         let metadata =
             if follow_symlink { std::fs::metadata(path) } else { std::fs::symlink_metadata(path) };
 
@@ -1712,9 +1670,9 @@ impl FileMetadata {
     fn from_fd_num<'tcx>(
         ecx: &mut MiriInterpCx<'tcx>,
         fd_num: i32,
-    ) -> InterpResult<'tcx, Option<FileMetadata>> {
+    ) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
         let Some(fd) = ecx.machine.fds.get(fd_num) else {
-            return ecx.fd_not_found().map(|_: i32| None);
+            return interp_ok(Err(LibcError("EBADF")));
         };
 
         let file = &fd
@@ -1734,12 +1692,11 @@ impl FileMetadata {
     fn from_meta<'tcx>(
         ecx: &mut MiriInterpCx<'tcx>,
         metadata: Result<std::fs::Metadata, std::io::Error>,
-    ) -> InterpResult<'tcx, Option<FileMetadata>> {
+    ) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
         let metadata = match metadata {
             Ok(metadata) => metadata,
             Err(e) => {
-                ecx.set_last_error(e)?;
-                return interp_ok(None);
+                return interp_ok(Err(e.into()));
             }
         };
 
@@ -1762,6 +1719,6 @@ impl FileMetadata {
         let modified = extract_sec_and_nsec(metadata.modified())?;
 
         // FIXME: Provide more fields using platform specific methods.
-        interp_ok(Some(FileMetadata { mode, size, created, accessed, modified }))
+        interp_ok(Ok(FileMetadata { mode, size, created, accessed, modified }))
     }
 }
diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs
index cafc7161d26..de108665e9f 100644
--- a/src/tools/miri/src/shims/unix/linux/epoll.rs
+++ b/src/tools/miri/src/shims/unix/linux/epoll.rs
@@ -256,23 +256,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let epollhup = this.eval_libc_u32("EPOLLHUP");
         let epollerr = this.eval_libc_u32("EPOLLERR");
 
-        // Fail on unsupported operations.
-        if op & epoll_ctl_add != epoll_ctl_add
-            && op & epoll_ctl_mod != epoll_ctl_mod
-            && op & epoll_ctl_del != epoll_ctl_del
-        {
-            throw_unsup_format!("epoll_ctl: encountered unknown unsupported operation {:#x}", op);
-        }
-
         // Throw EINVAL if epfd and fd have the same value.
         if epfd_value == fd {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         // Check if epfd is a valid epoll file descriptor.
         let Some(epfd) = this.machine.fds.get(epfd_value) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         let epoll_file_description = epfd
             .downcast::<Epoll>()
@@ -282,7 +273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let ready_list = &epoll_file_description.ready_list;
 
         let Some(fd_ref) = this.machine.fds.get(fd) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         let id = fd_ref.get_id();
 
@@ -332,15 +323,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Check the existence of fd in the interest list.
             if op == epoll_ctl_add {
                 if interest_list.contains_key(&epoll_key) {
-                    let eexist = this.eval_libc("EEXIST");
-                    this.set_last_error(eexist)?;
-                    return interp_ok(Scalar::from_i32(-1));
+                    return this.set_last_error_and_return_i32(LibcError("EEXIST"));
                 }
             } else {
                 if !interest_list.contains_key(&epoll_key) {
-                    let enoent = this.eval_libc("ENOENT");
-                    this.set_last_error(enoent)?;
-                    return interp_ok(Scalar::from_i32(-1));
+                    return this.set_last_error_and_return_i32(LibcError("ENOENT"));
                 }
             }
 
@@ -368,15 +355,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Notification will be returned for current epfd if there is event in the file
             // descriptor we registered.
             check_and_update_one_event_interest(&fd_ref, interest, id, this)?;
-            return interp_ok(Scalar::from_i32(0));
+            interp_ok(Scalar::from_i32(0))
         } else if op == epoll_ctl_del {
             let epoll_key = (id, fd);
 
             // Remove epoll_event_interest from interest_list.
             let Some(epoll_interest) = interest_list.remove(&epoll_key) else {
-                let enoent = this.eval_libc("ENOENT");
-                this.set_last_error(enoent)?;
-                return interp_ok(Scalar::from_i32(-1));
+                return this.set_last_error_and_return_i32(LibcError("ENOENT"));
             };
             // All related Weak<EpollEventInterest> will fail to upgrade after the drop.
             drop(epoll_interest);
@@ -394,9 +379,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 .unwrap()
                 .retain(|event| event.upgrade().is_some());
 
-            return interp_ok(Scalar::from_i32(0));
+            interp_ok(Scalar::from_i32(0))
+        } else {
+            throw_unsup_format!("unsupported epoll_ctl operation: {op}");
         }
-        interp_ok(Scalar::from_i32(-1))
     }
 
     /// The `epoll_wait()` system call waits for events on the `Epoll`
@@ -447,9 +433,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let timeout = this.read_scalar(timeout)?.to_i32()?;
 
         if epfd_value <= 0 || maxevents <= 0 {
-            this.set_last_error(LibcError("EINVAL"))?;
-            this.write_int(-1, dest)?;
-            return interp_ok(());
+            return this.set_last_error_and_return(LibcError("EINVAL"), dest);
         }
 
         // This needs to come after the maxevents value check, or else maxevents.try_into().unwrap()
@@ -460,9 +444,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         )?;
 
         let Some(epfd) = this.machine.fds.get(epfd_value) else {
-            let result_value: i32 = this.fd_not_found()?;
-            this.write_int(result_value, dest)?;
-            return interp_ok(());
+            return this.set_last_error_and_return(LibcError("EBADF"), dest);
         };
         // Create a weak ref of epfd and pass it to callback so we will make sure that epfd
         // is not close after the thread unblocks.
diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
index e73bde1ddb6..6616a9845c0 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -84,6 +84,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     TASK_COMM_LEN,
+                    /* truncate */ false,
                 )?;
                 let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
                 this.write_scalar(res, dest)?;
diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs
index 4f2e17d50c8..d5f9669b360 100644
--- a/src/tools/miri/src/shims/unix/linux/mem.rs
+++ b/src/tools/miri/src/shims/unix/linux/mem.rs
@@ -24,7 +24,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // old_address must be a multiple of the page size
         #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
         if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
@@ -38,7 +38,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 {
             // We only support MREMAP_MAYMOVE, so not passing the flag is just a failure
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs
index 941011bfac6..c258be78f76 100644
--- a/src/tools/miri/src/shims/unix/linux/sync.rs
+++ b/src/tools/miri/src/shims/unix/linux/sync.rs
@@ -63,8 +63,7 @@ pub fn futex<'tcx>(
             };
 
             if bitset == 0 {
-                this.set_last_error(LibcError("EINVAL"))?;
-                this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
+                this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
                 return interp_ok(());
             }
 
@@ -75,9 +74,7 @@ pub fn futex<'tcx>(
                 let duration = match this.read_timespec(&timeout)? {
                     Some(duration) => duration,
                     None => {
-                        this.set_last_error(LibcError("EINVAL"))?;
-                        this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
-                        return interp_ok(());
+                        return this.set_last_error_and_return(LibcError("EINVAL"), dest);
                     }
                 };
                 let timeout_clock = if op & futex_realtime == futex_realtime {
@@ -153,14 +150,12 @@ pub fn futex<'tcx>(
                     Scalar::from_target_isize(0, this), // retval_succ
                     Scalar::from_target_isize(-1, this), // retval_timeout
                     dest.clone(),
-                    this.eval_libc("ETIMEDOUT"),
+                    this.eval_libc("ETIMEDOUT"), // errno_timeout
                 );
             } else {
                 // The futex value doesn't match the expected value, so we return failure
                 // right away without sleeping: -1 and errno set to EAGAIN.
-                let eagain = this.eval_libc("EAGAIN");
-                this.set_last_error(eagain)?;
-                this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
+                return this.set_last_error_and_return(LibcError("EAGAIN"), dest);
             }
         }
         // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
@@ -180,9 +175,7 @@ pub fn futex<'tcx>(
                 u32::MAX
             };
             if bitset == 0 {
-                this.set_last_error(LibcError("EINVAL"))?;
-                this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
-                return interp_ok(());
+                return this.set_last_error_and_return(LibcError("EINVAL"), dest);
             }
             // Together with the SeqCst fence in futex_wait, this makes sure that futex_wait
             // will see the latest value on addr which could be changed by our caller
diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
index b199992245c..cd07bc9e013 100644
--- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
@@ -181,6 +181,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     thread,
                     this.read_scalar(name)?,
                     this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
+                    /* truncate */ false,
                 )? {
                     Scalar::from_u32(0)
                 } else {
diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs
index 9273748ef3b..9371edfc83d 100644
--- a/src/tools/miri/src/shims/unix/mem.rs
+++ b/src/tools/miri/src/shims/unix/mem.rs
@@ -57,11 +57,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // First, we do some basic argument validation as required by mmap
         if (flags & (map_private | map_shared)).count_ones() != 1 {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
         if length == 0 {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
@@ -103,11 +103,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         let align = this.machine.page_align();
         let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         };
         if map_length > this.target_usize_max() {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
@@ -134,16 +134,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // as a dealloc.
         #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
         if addr.addr().bytes() % this.machine.page_size != 0 {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         };
         if length > this.target_usize_max() {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
index 7f3d0f07bdc..c9c1b01b8b1 100644
--- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
@@ -30,6 +30,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     max_len,
+                    /* truncate */ false,
                 )?;
                 let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
                 this.write_scalar(res, dest)?;
diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs
index 7f97afc8e4b..51256d800a4 100644
--- a/src/tools/miri/src/shims/unix/thread.rs
+++ b/src/tools/miri/src/shims/unix/thread.rs
@@ -64,23 +64,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     /// Set the name of the specified thread. If the name including the null terminator
-    /// is longer than `name_max_len`, then `false` is returned.
+    /// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name
+    /// is used as the thread name, otherwise `false` is returned.
     fn pthread_setname_np(
         &mut self,
         thread: Scalar,
         name: Scalar,
         name_max_len: usize,
+        truncate: bool,
     ) -> InterpResult<'tcx, bool> {
         let this = self.eval_context_mut();
 
         let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
         let thread = ThreadId::try_from(thread).unwrap();
         let name = name.to_pointer(this)?;
-        let name = this.read_c_str(name)?.to_owned();
+        let mut name = this.read_c_str(name)?.to_owned();
 
         // Comparing with `>=` to account for null terminator.
         if name.len() >= name_max_len {
-            return interp_ok(false);
+            if truncate {
+                name.truncate(name_max_len.saturating_sub(1));
+            } else {
+                return interp_ok(false);
+            }
         }
 
         this.set_thread_name(thread, name);
diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs
index f8861085fe5..f7566a8112d 100644
--- a/src/tools/miri/src/shims/windows/sync.rs
+++ b/src/tools/miri/src/shims/windows/sync.rs
@@ -202,7 +202,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 Scalar::from_i32(1), // retval_succ
                 Scalar::from_i32(0), // retval_timeout
                 dest.clone(),
-                this.eval_windows("c", "ERROR_TIMEOUT"),
+                this.eval_windows("c", "ERROR_TIMEOUT"), // errno_timeout
             );
         }
 
diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock
index 64bfc84ef20..0a5e9f62dd9 100644
--- a/src/tools/miri/test_dependencies/Cargo.lock
+++ b/src/tools/miri/test_dependencies/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "addr2line"
@@ -128,9 +128,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.158"
+version = "0.2.161"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
 
 [[package]]
 name = "linux-raw-sys"
diff --git a/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs
new file mode 100644
index 00000000000..4b731866aca
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs
@@ -0,0 +1,10 @@
+//! Ensure we report UB when the buffer is smaller than 16 bytes (even if the thread
+//! name would fit in the smaller buffer).
+//@only-target: android  # Miri supports prctl for Android only
+
+fn main() {
+    let mut buf = vec![0u8; 15];
+    unsafe {
+        libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr().cast::<libc::c_char>()); //~ ERROR: memory access failed: expected a pointer to 16 bytes of memory, but got alloc952 which is only 15 bytes from the end of the allocation
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr
new file mode 100644
index 00000000000..275a38e593c
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr
@@ -0,0 +1,21 @@
+error: Undefined Behavior: memory access failed: expected a pointer to 16 bytes of memory, but got ALLOC which is only 15 bytes from the end of the allocation
+  --> tests/fail-dep/libc/prctl-threadname.rs:LL:CC
+   |
+LL |         libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr().cast::<libc::c_char>());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 16 bytes of memory, but got ALLOC which is only 15 bytes from the end of the allocation
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+help: ALLOC was allocated here:
+  --> tests/fail-dep/libc/prctl-threadname.rs:LL:CC
+   |
+LL |     let mut buf = vec![0u8; 15];
+   |                   ^^^^^^^^^^^^^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at tests/fail-dep/libc/prctl-threadname.rs:LL:CC
+   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs
index f4c009456d2..55491da9f60 100644
--- a/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs
+++ b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs
@@ -1,7 +1,7 @@
 //! This is a regression test for <https://github.com/rust-lang/miri/issues/3947>: we had some
 //! faulty logic around `release_clock` that led to this code not reporting a data race.
 //@ignore-target: windows # no libc socketpair on Windows
-//@compile-flags: -Zmiri-preemption-rate=0
+//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-rate=0
 use std::thread;
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
index d4479f32e56..c91f4ec158f 100644
--- a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
+++ b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
@@ -1,4 +1,3 @@
-
 // Ensure that a `ptr::without_provenance` ptr is truly invalid.
 fn main() {
     let x = 42;
diff --git a/src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs
new file mode 100644
index 00000000000..b69f7ee9dff
--- /dev/null
+++ b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs
@@ -0,0 +1,16 @@
+#![feature(explicit_tail_calls)]
+#![allow(incomplete_features)]
+
+fn g(x: *const i32) {
+    let _val = unsafe { *x }; //~ERROR: has been freed, so this pointer is dangling
+}
+
+fn f(_x: *const i32) {
+    let local = 0;
+    let ptr = &local as *const i32;
+    become g(ptr)
+}
+
+fn main() {
+    f(std::ptr::null());
+}
diff --git a/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr
new file mode 100644
index 00000000000..6acd69ab3f8
--- /dev/null
+++ b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr
@@ -0,0 +1,30 @@
+error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
+  --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+   |
+LL |     let _val = unsafe { *x };
+   |                         ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+help: ALLOC was allocated here:
+  --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+   |
+LL |     let local = 0;
+   |         ^^^^^
+help: ALLOC was deallocated here:
+  --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+   |
+LL | }
+   | ^
+   = note: BACKTRACE (of the first span):
+   = note: inside `g` at tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+note: inside `main`
+  --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+   |
+LL |     f(std::ptr::null());
+   |     ^^^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs b/src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs
new file mode 100644
index 00000000000..87ae3753fea
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs
@@ -0,0 +1,74 @@
+//@only-target: android  # Miri supports prctl for Android only
+use std::ffi::{CStr, CString};
+use std::thread;
+
+// The Linux kernel all names 16 bytes long including the null terminator.
+const MAX_THREAD_NAME_LEN: usize = 16;
+
+fn main() {
+    // The short name should be shorter than 16 bytes which POSIX promises
+    // for thread names. The length includes a null terminator.
+    let short_name = "test_named".to_owned();
+    let long_name = std::iter::once("test_named_thread_truncation")
+        .chain(std::iter::repeat(" yada").take(100))
+        .collect::<String>();
+
+    fn set_thread_name(name: &CStr) -> i32 {
+        unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr().cast::<libc::c_char>()) }
+    }
+
+    fn get_thread_name(name: &mut [u8]) -> i32 {
+        assert!(name.len() >= MAX_THREAD_NAME_LEN);
+        unsafe { libc::prctl(libc::PR_GET_NAME, name.as_mut_ptr().cast::<libc::c_char>()) }
+    }
+
+    // Set name via Rust API, get it via prctl.
+    let long_name2 = long_name.clone();
+    thread::Builder::new()
+        .name(long_name.clone())
+        .spawn(move || {
+            let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
+            assert_eq!(get_thread_name(&mut buf), 0);
+            let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+            let truncated_name = &long_name2[..long_name2.len().min(MAX_THREAD_NAME_LEN - 1)];
+            assert_eq!(cstr.to_bytes(), truncated_name.as_bytes());
+        })
+        .unwrap()
+        .join()
+        .unwrap();
+
+    // Set name via prctl and get it again (short name).
+    thread::Builder::new()
+        .spawn(move || {
+            // Set short thread name.
+            let cstr = CString::new(short_name.clone()).unwrap();
+            assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); // this should fit
+            assert_eq!(set_thread_name(&cstr), 0);
+
+            let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
+            assert_eq!(get_thread_name(&mut buf), 0);
+            let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+            assert_eq!(cstr.to_bytes(), short_name.as_bytes());
+        })
+        .unwrap()
+        .join()
+        .unwrap();
+
+    // Set name via prctl and get it again (long name).
+    thread::Builder::new()
+        .spawn(move || {
+            // Set full thread name.
+            let cstr = CString::new(long_name.clone()).unwrap();
+            assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN);
+            // Names are truncated by the Linux kernel.
+            assert_eq!(set_thread_name(&cstr), 0);
+
+            let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
+            assert_eq!(get_thread_name(&mut buf), 0);
+            let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+            assert_eq!(cstr.to_bytes(), &long_name.as_bytes()[..(MAX_THREAD_NAME_LEN - 1)]);
+        })
+        .unwrap()
+        .join()
+        .unwrap();
+}
diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
index 404ef7cc42d..0e5b501bbcc 100644
--- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
+++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
@@ -1,4 +1,5 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ignore-target: android # No pthread_{get,set}_name on Android
 use std::ffi::{CStr, CString};
 use std::thread;
 
@@ -65,6 +66,22 @@ fn main() {
         }
     }
 
+    // Set name via Rust API, get it via pthreads.
+    let long_name2 = long_name.clone();
+    thread::Builder::new()
+        .name(long_name.clone())
+        .spawn(move || {
+            let mut buf = vec![0u8; long_name2.len() + 1];
+            assert_eq!(get_thread_name(&mut buf), 0);
+            let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+            let truncated_name = &long_name2[..long_name2.len().min(MAX_THREAD_NAME_LEN - 1)];
+            assert_eq!(cstr.to_bytes(), truncated_name.as_bytes());
+        })
+        .unwrap()
+        .join()
+        .unwrap();
+
+    // Set name via pthread and get it again (short name).
     thread::Builder::new()
         .spawn(move || {
             // Set short thread name.
@@ -130,6 +147,7 @@ fn main() {
         .join()
         .unwrap();
 
+    // Set name via pthread and get it again (long name).
     thread::Builder::new()
         .spawn(move || {
             // Set full thread name.
diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs
index 306e9ab9c67..61410f7c4e0 100644
--- a/src/tools/miri/tests/pass/dyn-upcast.rs
+++ b/src/tools/miri/tests/pass/dyn-upcast.rs
@@ -433,7 +433,8 @@ fn replace_vptr() {
 }
 
 fn drop_principal() {
-    use std::{alloc::Layout, any::Any};
+    use std::alloc::Layout;
+    use std::any::Any;
 
     const fn yeet_principal(x: Box<dyn Any + Send>) -> Box<dyn Send> {
         x
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 853d3e80517..66843ca584b 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -157,13 +157,18 @@ fn basic() {
     assert_eq(-{ 5.0_f128 }, -5.0_f128);
 
     // infinities, NaN
-    // FIXME(f16_f128): add when constants and `is_infinite` are available
+    assert!((5.0_f16 / 0.0).is_infinite());
+    assert_ne!({ 5.0_f16 / 0.0 }, { -5.0_f16 / 0.0 });
     assert!((5.0_f32 / 0.0).is_infinite());
     assert_ne!({ 5.0_f32 / 0.0 }, { -5.0_f32 / 0.0 });
     assert!((5.0_f64 / 0.0).is_infinite());
     assert_ne!({ 5.0_f64 / 0.0 }, { 5.0_f64 / -0.0 });
+    assert!((5.0_f128 / 0.0).is_infinite());
+    assert_ne!({ 5.0_f128 / 0.0 }, { 5.0_f128 / -0.0 });
+    assert_ne!(f16::NAN, f16::NAN);
     assert_ne!(f32::NAN, f32::NAN);
     assert_ne!(f64::NAN, f64::NAN);
+    assert_ne!(f128::NAN, f128::NAN);
 
     // negative zero
     let posz = 0.0f16;
@@ -215,9 +220,14 @@ fn basic() {
     assert!((black_box(-1.0f128) % 1.0).is_sign_negative());
     assert!((black_box(-1.0f128) % -1.0).is_sign_negative());
 
-    // FIXME(f16_f128): add when `abs` is available
+    assert_eq!((-1.0f16).abs(), 1.0f16);
+    assert_eq!(34.2f16.abs(), 34.2f16);
     assert_eq!((-1.0f32).abs(), 1.0f32);
+    assert_eq!(34.2f32.abs(), 34.2f32);
+    assert_eq!((-1.0f64).abs(), 1.0f64);
     assert_eq!(34.2f64.abs(), 34.2f64);
+    assert_eq!((-1.0f128).abs(), 1.0f128);
+    assert_eq!(34.2f128.abs(), 34.2f128);
 }
 
 /// Test casts from floats to ints and back
@@ -654,6 +664,14 @@ fn casts() {
 }
 
 fn ops() {
+    // f16 min/max
+    assert_eq((1.0_f16).max(-1.0), 1.0);
+    assert_eq((1.0_f16).min(-1.0), -1.0);
+    assert_eq(f16::NAN.min(9.0), 9.0);
+    assert_eq(f16::NAN.max(-9.0), -9.0);
+    assert_eq((9.0_f16).min(f16::NAN), 9.0);
+    assert_eq((-9.0_f16).max(f16::NAN), -9.0);
+
     // f32 min/max
     assert_eq((1.0 as f32).max(-1.0), 1.0);
     assert_eq((1.0 as f32).min(-1.0), -1.0);
@@ -670,6 +688,21 @@ fn ops() {
     assert_eq((9.0 as f64).min(f64::NAN), 9.0);
     assert_eq((-9.0 as f64).max(f64::NAN), -9.0);
 
+    // f128 min/max
+    assert_eq((1.0_f128).max(-1.0), 1.0);
+    assert_eq((1.0_f128).min(-1.0), -1.0);
+    assert_eq(f128::NAN.min(9.0), 9.0);
+    assert_eq(f128::NAN.max(-9.0), -9.0);
+    assert_eq((9.0_f128).min(f128::NAN), 9.0);
+    assert_eq((-9.0_f128).max(f128::NAN), -9.0);
+
+    // f16 copysign
+    assert_eq(3.5_f16.copysign(0.42), 3.5_f16);
+    assert_eq(3.5_f16.copysign(-0.42), -3.5_f16);
+    assert_eq((-3.5_f16).copysign(0.42), 3.5_f16);
+    assert_eq((-3.5_f16).copysign(-0.42), -3.5_f16);
+    assert!(f16::NAN.copysign(1.0).is_nan());
+
     // f32 copysign
     assert_eq(3.5_f32.copysign(0.42), 3.5_f32);
     assert_eq(3.5_f32.copysign(-0.42), -3.5_f32);
@@ -683,6 +716,13 @@ fn ops() {
     assert_eq((-3.5_f64).copysign(0.42), 3.5_f64);
     assert_eq((-3.5_f64).copysign(-0.42), -3.5_f64);
     assert!(f64::NAN.copysign(1.0).is_nan());
+
+    // f128 copysign
+    assert_eq(3.5_f128.copysign(0.42), 3.5_f128);
+    assert_eq(3.5_f128.copysign(-0.42), -3.5_f128);
+    assert_eq((-3.5_f128).copysign(0.42), 3.5_f128);
+    assert_eq((-3.5_f128).copysign(-0.42), -3.5_f128);
+    assert!(f128::NAN.copysign(1.0).is_nan());
 }
 
 /// Tests taken from rustc test suite.
@@ -807,6 +847,18 @@ fn nan_casts() {
 
 fn rounding() {
     // Test cases taken from the library's tests for this feature
+    // f16
+    assert_eq(2.5f16.round_ties_even(), 2.0f16);
+    assert_eq(1.0f16.round_ties_even(), 1.0f16);
+    assert_eq(1.3f16.round_ties_even(), 1.0f16);
+    assert_eq(1.5f16.round_ties_even(), 2.0f16);
+    assert_eq(1.7f16.round_ties_even(), 2.0f16);
+    assert_eq(0.0f16.round_ties_even(), 0.0f16);
+    assert_eq((-0.0f16).round_ties_even(), -0.0f16);
+    assert_eq((-1.0f16).round_ties_even(), -1.0f16);
+    assert_eq((-1.3f16).round_ties_even(), -1.0f16);
+    assert_eq((-1.5f16).round_ties_even(), -2.0f16);
+    assert_eq((-1.7f16).round_ties_even(), -2.0f16);
     // f32
     assert_eq(2.5f32.round_ties_even(), 2.0f32);
     assert_eq(1.0f32.round_ties_even(), 1.0f32);
@@ -831,23 +883,59 @@ fn rounding() {
     assert_eq((-1.3f64).round_ties_even(), -1.0f64);
     assert_eq((-1.5f64).round_ties_even(), -2.0f64);
     assert_eq((-1.7f64).round_ties_even(), -2.0f64);
-
+    // f128
+    assert_eq(2.5f128.round_ties_even(), 2.0f128);
+    assert_eq(1.0f128.round_ties_even(), 1.0f128);
+    assert_eq(1.3f128.round_ties_even(), 1.0f128);
+    assert_eq(1.5f128.round_ties_even(), 2.0f128);
+    assert_eq(1.7f128.round_ties_even(), 2.0f128);
+    assert_eq(0.0f128.round_ties_even(), 0.0f128);
+    assert_eq((-0.0f128).round_ties_even(), -0.0f128);
+    assert_eq((-1.0f128).round_ties_even(), -1.0f128);
+    assert_eq((-1.3f128).round_ties_even(), -1.0f128);
+    assert_eq((-1.5f128).round_ties_even(), -2.0f128);
+    assert_eq((-1.7f128).round_ties_even(), -2.0f128);
+
+    assert_eq!(3.8f16.floor(), 3.0f16);
+    assert_eq!((-1.1f16).floor(), -2.0f16);
     assert_eq!(3.8f32.floor(), 3.0f32);
+    assert_eq!((-1.1f32).floor(), -2.0f32);
+    assert_eq!(3.8f64.floor(), 3.0f64);
     assert_eq!((-1.1f64).floor(), -2.0f64);
+    assert_eq!(3.8f128.floor(), 3.0f128);
+    assert_eq!((-1.1f128).floor(), -2.0f128);
 
+    assert_eq!(3.8f16.ceil(), 4.0f16);
+    assert_eq!((-2.3f16).ceil(), -2.0f16);
+    assert_eq!(3.8f32.ceil(), 4.0f32);
     assert_eq!((-2.3f32).ceil(), -2.0f32);
     assert_eq!(3.8f64.ceil(), 4.0f64);
+    assert_eq!((-2.3f64).ceil(), -2.0f64);
+    assert_eq!(3.8f128.ceil(), 4.0f128);
+    assert_eq!((-2.3f128).ceil(), -2.0f128);
 
+    assert_eq!(0.1f16.trunc(), 0.0f16);
+    assert_eq!((-0.1f16).trunc(), 0.0f16);
     assert_eq!(0.1f32.trunc(), 0.0f32);
+    assert_eq!((-0.1f32).trunc(), 0.0f32);
+    assert_eq!(0.1f64.trunc(), 0.0f64);
     assert_eq!((-0.1f64).trunc(), 0.0f64);
+    assert_eq!(0.1f128.trunc(), 0.0f128);
+    assert_eq!((-0.1f128).trunc(), 0.0f128);
 
+    assert_eq!(3.3_f16.round(), 3.0);
+    assert_eq!(2.5_f16.round(), 3.0);
     assert_eq!(3.3_f32.round(), 3.0);
     assert_eq!(2.5_f32.round(), 3.0);
     assert_eq!(3.9_f64.round(), 4.0);
     assert_eq!(2.5_f64.round(), 3.0);
+    assert_eq!(3.9_f128.round(), 4.0);
+    assert_eq!(2.5_f128.round(), 3.0);
 }
 
 fn mul_add() {
+    // FIXME(f16_f128): add when supported
+
     assert_eq!(3.0f32.mul_add(2.0f32, 5.0f32), 11.0);
     assert_eq!(0.0f32.mul_add(-2.0, f32::consts::E), f32::consts::E);
     assert_eq!(3.0f64.mul_add(2.0, 5.0), 11.0);
@@ -983,7 +1071,7 @@ fn test_fast() {
     use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast};
 
     #[inline(never)]
-    pub fn test_operations_f64(a: f64, b: f64) {
+    pub fn test_operations_f16(a: f16, b: f16) {
         // make sure they all map to the correct operation
         unsafe {
             assert_eq!(fadd_fast(a, b), a + b);
@@ -1006,10 +1094,38 @@ fn test_fast() {
         }
     }
 
-    test_operations_f64(1., 2.);
-    test_operations_f64(10., 5.);
+    #[inline(never)]
+    pub fn test_operations_f64(a: f64, b: f64) {
+        // make sure they all map to the correct operation
+        unsafe {
+            assert_eq!(fadd_fast(a, b), a + b);
+            assert_eq!(fsub_fast(a, b), a - b);
+            assert_eq!(fmul_fast(a, b), a * b);
+            assert_eq!(fdiv_fast(a, b), a / b);
+            assert_eq!(frem_fast(a, b), a % b);
+        }
+    }
+
+    #[inline(never)]
+    pub fn test_operations_f128(a: f128, b: f128) {
+        // make sure they all map to the correct operation
+        unsafe {
+            assert_eq!(fadd_fast(a, b), a + b);
+            assert_eq!(fsub_fast(a, b), a - b);
+            assert_eq!(fmul_fast(a, b), a * b);
+            assert_eq!(fdiv_fast(a, b), a / b);
+            assert_eq!(frem_fast(a, b), a % b);
+        }
+    }
+
+    test_operations_f16(11., 2.);
+    test_operations_f16(10., 15.);
     test_operations_f32(11., 2.);
     test_operations_f32(10., 15.);
+    test_operations_f64(1., 2.);
+    test_operations_f64(10., 5.);
+    test_operations_f128(1., 2.);
+    test_operations_f128(10., 5.);
 }
 
 fn test_algebraic() {
@@ -1018,7 +1134,7 @@ fn test_algebraic() {
     };
 
     #[inline(never)]
-    pub fn test_operations_f64(a: f64, b: f64) {
+    pub fn test_operations_f16(a: f16, b: f16) {
         // make sure they all map to the correct operation
         assert_eq!(fadd_algebraic(a, b), a + b);
         assert_eq!(fsub_algebraic(a, b), a - b);
@@ -1037,15 +1153,41 @@ fn test_algebraic() {
         assert_eq!(frem_algebraic(a, b), a % b);
     }
 
-    test_operations_f64(1., 2.);
-    test_operations_f64(10., 5.);
+    #[inline(never)]
+    pub fn test_operations_f64(a: f64, b: f64) {
+        // make sure they all map to the correct operation
+        assert_eq!(fadd_algebraic(a, b), a + b);
+        assert_eq!(fsub_algebraic(a, b), a - b);
+        assert_eq!(fmul_algebraic(a, b), a * b);
+        assert_eq!(fdiv_algebraic(a, b), a / b);
+        assert_eq!(frem_algebraic(a, b), a % b);
+    }
+
+    #[inline(never)]
+    pub fn test_operations_f128(a: f128, b: f128) {
+        // make sure they all map to the correct operation
+        assert_eq!(fadd_algebraic(a, b), a + b);
+        assert_eq!(fsub_algebraic(a, b), a - b);
+        assert_eq!(fmul_algebraic(a, b), a * b);
+        assert_eq!(fdiv_algebraic(a, b), a / b);
+        assert_eq!(frem_algebraic(a, b), a % b);
+    }
+
+    test_operations_f16(11., 2.);
+    test_operations_f16(10., 15.);
     test_operations_f32(11., 2.);
     test_operations_f32(10., 15.);
+    test_operations_f64(1., 2.);
+    test_operations_f64(10., 5.);
+    test_operations_f128(1., 2.);
+    test_operations_f128(10., 5.);
 }
 
 fn test_fmuladd() {
     use std::intrinsics::{fmuladdf32, fmuladdf64};
 
+    // FIXME(f16_f128): add when supported
+
     #[inline(never)]
     pub fn test_operations_f32(a: f32, b: f32, c: f32) {
         assert_approx_eq!(unsafe { fmuladdf32(a, b, c) }, a * b + c);
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index d3605cd3dce..3affa199fa5 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -13,3 +13,6 @@ gimli = "0.31.0"
 build_helper = { path = "../build_helper" }
 serde_json = "1.0"
 libc = "0.2"
+
+[lib]
+crate-type = ["lib", "dylib"]
diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs
index 6b58173b343..9e09527d6d0 100644
--- a/src/tools/run-make-support/src/command.rs
+++ b/src/tools/run-make-support/src/command.rs
@@ -224,6 +224,12 @@ pub struct CompletedProcess {
 impl CompletedProcess {
     #[must_use]
     #[track_caller]
+    pub fn stdout(&self) -> Vec<u8> {
+        self.output.stdout.clone()
+    }
+
+    #[must_use]
+    #[track_caller]
     pub fn stdout_utf8(&self) -> String {
         String::from_utf8(self.output.stdout.clone()).expect("stdout is not valid UTF-8")
     }
@@ -236,11 +242,23 @@ impl CompletedProcess {
 
     #[must_use]
     #[track_caller]
+    pub fn stderr(&self) -> Vec<u8> {
+        self.output.stderr.clone()
+    }
+
+    #[must_use]
+    #[track_caller]
     pub fn stderr_utf8(&self) -> String {
         String::from_utf8(self.output.stderr.clone()).expect("stderr is not valid UTF-8")
     }
 
     #[must_use]
+    #[track_caller]
+    pub fn invalid_stderr_utf8(&self) -> String {
+        String::from_utf8_lossy(&self.output.stderr.clone()).to_string()
+    }
+
+    #[must_use]
     pub fn status(&self) -> ExitStatus {
         self.output.status
     }
diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs
index 38a9ac923b4..9a6e35da3fe 100644
--- a/src/tools/run-make-support/src/external_deps/llvm.rs
+++ b/src/tools/run-make-support/src/external_deps/llvm.rs
@@ -60,6 +60,18 @@ pub fn llvm_pdbutil() -> LlvmPdbutil {
     LlvmPdbutil::new()
 }
 
+/// Construct a new `llvm-dis` invocation. This assumes that `llvm-dis` is available
+/// at `$LLVM_BIN_DIR/llvm-dis`.
+pub fn llvm_dis() -> LlvmDis {
+    LlvmDis::new()
+}
+
+/// Construct a new `llvm-objcopy` invocation. This assumes that `llvm-objcopy` is available
+/// at `$LLVM_BIN_DIR/llvm-objcopy`.
+pub fn llvm_objcopy() -> LlvmObjcopy {
+    LlvmObjcopy::new()
+}
+
 /// A `llvm-readobj` invocation builder.
 #[derive(Debug)]
 #[must_use]
@@ -123,6 +135,20 @@ pub struct LlvmPdbutil {
     cmd: Command,
 }
 
+/// A `llvm-dis` invocation builder.
+#[derive(Debug)]
+#[must_use]
+pub struct LlvmDis {
+    cmd: Command,
+}
+
+/// A `llvm-objcopy` invocation builder.
+#[derive(Debug)]
+#[must_use]
+pub struct LlvmObjcopy {
+    cmd: Command,
+}
+
 crate::macros::impl_common_helpers!(LlvmReadobj);
 crate::macros::impl_common_helpers!(LlvmProfdata);
 crate::macros::impl_common_helpers!(LlvmFilecheck);
@@ -132,6 +158,8 @@ crate::macros::impl_common_helpers!(LlvmNm);
 crate::macros::impl_common_helpers!(LlvmBcanalyzer);
 crate::macros::impl_common_helpers!(LlvmDwarfdump);
 crate::macros::impl_common_helpers!(LlvmPdbutil);
+crate::macros::impl_common_helpers!(LlvmDis);
+crate::macros::impl_common_helpers!(LlvmObjcopy);
 
 /// Generate the path to the bin directory of LLVM.
 #[must_use]
@@ -390,3 +418,41 @@ impl LlvmPdbutil {
         self
     }
 }
+
+impl LlvmObjcopy {
+    /// Construct a new `llvm-objcopy` invocation. This assumes that `llvm-objcopy` is available
+    /// at `$LLVM_BIN_DIR/llvm-objcopy`.
+    pub fn new() -> Self {
+        let llvm_objcopy = llvm_bin_dir().join("llvm-objcopy");
+        let cmd = Command::new(llvm_objcopy);
+        Self { cmd }
+    }
+
+    /// Dump the contents of `section` into the file at `path`.
+    #[track_caller]
+    pub fn dump_section<S: AsRef<str>, P: AsRef<Path>>(
+        &mut self,
+        section_name: S,
+        path: P,
+    ) -> &mut Self {
+        self.cmd.arg("--dump-section");
+        self.cmd.arg(format!("{}={}", section_name.as_ref(), path.as_ref().to_str().unwrap()));
+        self
+    }
+}
+
+impl LlvmDis {
+    /// Construct a new `llvm-dis` invocation. This assumes that `llvm-dis` is available
+    /// at `$LLVM_BIN_DIR/llvm-dis`.
+    pub fn new() -> Self {
+        let llvm_dis = llvm_bin_dir().join("llvm-dis");
+        let cmd = Command::new(llvm_dis);
+        Self { cmd }
+    }
+
+    /// Provide an input file.
+    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(path.as_ref());
+        self
+    }
+}
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 15d813ccf53..368b98c9f0d 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -49,14 +49,17 @@ pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rust
 
 // These rely on external dependencies.
 pub use cc::{cc, cxx, extra_c_flags, extra_cxx_flags, Cc};
-pub use c_build::{build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_optimized, build_native_static_lib_cxx};
+pub use c_build::{
+    build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_cxx,
+    build_native_static_lib_optimized,
+};
 pub use cargo::cargo;
 pub use clang::{clang, Clang};
 pub use htmldocck::htmldocck;
 pub use llvm::{
-    llvm_ar, llvm_bcanalyzer, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objdump, llvm_profdata,
-    llvm_readobj, LlvmAr, LlvmBcanalyzer, LlvmDwarfdump, LlvmFilecheck, LlvmNm, LlvmObjdump,
-    LlvmProfdata, LlvmReadobj,
+    llvm_ar, llvm_bcanalyzer, llvm_dis, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objcopy,
+    llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr, LlvmBcanalyzer, LlvmDis, LlvmDwarfdump,
+    LlvmFilecheck, LlvmNm, LlvmObjcopy, LlvmObjdump, LlvmProfdata, LlvmReadobj,
 };
 pub use python::python_command;
 pub use rustc::{aux_build, bare_rustc, rustc, rustc_path, Rustc};
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index 80831440720..9f4cc98993e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -6,7 +6,7 @@ use base_db::ra_salsa::Cycle;
 use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy};
 use hir_def::{
     layout::{
-        Abi, FieldsShape, Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutS,
+        Abi, FieldsShape, Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutData,
         Primitive, ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange,
     },
     LocalFieldId, StructId,
@@ -66,7 +66,7 @@ impl rustc_index::Idx for RustcFieldIdx {
     }
 }
 
-pub type Layout = LayoutS<RustcFieldIdx, RustcEnumVariantIdx>;
+pub type Layout = LayoutData<RustcFieldIdx, RustcEnumVariantIdx>;
 pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>;
 pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx>;
 
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index 060deb18344..27ccb205b50 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "adler2"
@@ -704,6 +704,7 @@ dependencies = [
  "semver",
  "serde_json",
  "tempfile",
+ "walkdir",
 ]
 
 [[package]]
diff --git a/src/tools/rustbook/src/main.rs b/src/tools/rustbook/src/main.rs
index 2118af61396..f905b9277ff 100644
--- a/src/tools/rustbook/src/main.rs
+++ b/src/tools/rustbook/src/main.rs
@@ -22,6 +22,11 @@ fn main() {
     .required(false)
     .value_parser(clap::value_parser!(String));
 
+    let root_arg = arg!(--"rust-root" <ROOT_DIR>
+"Path to the root of the rust source tree")
+    .required(false)
+    .value_parser(clap::value_parser!(PathBuf));
+
     let dir_arg = arg!([dir] "Root directory for the book\n\
                               (Defaults to the current directory when omitted)")
     .value_parser(clap::value_parser!(PathBuf));
@@ -37,6 +42,7 @@ fn main() {
                 .about("Build the book from the markdown files")
                 .arg(d_arg)
                 .arg(l_arg)
+                .arg(root_arg)
                 .arg(&dir_arg),
         )
         .subcommand(
@@ -96,7 +102,8 @@ pub fn build(args: &ArgMatches) -> Result3<()> {
     }
 
     if book.config.get_preprocessor("spec").is_some() {
-        book.with_preprocessor(Spec::new());
+        let rust_root = args.get_one::<PathBuf>("rust-root").cloned();
+        book.with_preprocessor(Spec::new(rust_root)?);
     }
 
     book.build()?;
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 7a0f98d59f0..0e156b9d1ac 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -2,7 +2,6 @@ run-make/branch-protection-check-IBT/Makefile
 run-make/cat-and-grep-sanity-check/Makefile
 run-make/extern-fn-reachable/Makefile
 run-make/incr-add-rust-src-component/Makefile
-run-make/issue-84395-lto-embed-bitcode/Makefile
 run-make/jobserver-error/Makefile
 run-make/libs-through-symlinks/Makefile
 run-make/split-debuginfo/Makefile
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 97c42752c12..94f8d23c158 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -3720,16 +3720,6 @@ ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs
 ui/rfcs/rfc-2528-type-changing-struct-update/issue-92010-trait-bound-not-satisfied.rs
 ui/rfcs/rfc-2528-type-changing-struct-update/issue-96878.rs
 ui/rfcs/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs
-ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs
-ui/rfcs/rfc-2632-const-trait-impl/issue-102156.rs
-ui/rfcs/rfc-2632-const-trait-impl/issue-102985.rs
-ui/rfcs/rfc-2632-const-trait-impl/issue-103677.rs
-ui/rfcs/rfc-2632-const-trait-impl/issue-79450.rs
-ui/rfcs/rfc-2632-const-trait-impl/issue-88155.rs
-ui/rfcs/rfc-2632-const-trait-impl/issue-92111.rs
-ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs
-ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs
-ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs
 ui/rust-2018/issue-51008-1.rs
 ui/rust-2018/issue-51008.rs
 ui/rust-2018/issue-52202-use-suggestions.rs
@@ -3983,6 +3973,16 @@ ui/traits/alias/issue-75983.rs
 ui/traits/alias/issue-83613.rs
 ui/traits/associated_type_bound/issue-51446.rs
 ui/traits/auxiliary/issue_89119_intercrate_caching.rs
+ui/traits/const-traits/issue-100222.rs
+ui/traits/const-traits/issue-102156.rs
+ui/traits/const-traits/issue-102985.rs
+ui/traits/const-traits/issue-103677.rs
+ui/traits/const-traits/issue-79450.rs
+ui/traits/const-traits/issue-88155.rs
+ui/traits/const-traits/issue-92111.rs
+ui/traits/const-traits/issue-92230-wf-super-trait-env.rs
+ui/traits/const-traits/specialization/issue-95186-specialize-on-tilde-const.rs
+ui/traits/const-traits/specialization/issue-95187-same-trait-bound-different-constness.rs
 ui/traits/issue-103563.rs
 ui/traits/issue-104322.rs
 ui/traits/issue-105231.rs