about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock10
-rw-r--r--compiler/rustc_abi/src/callconv.rs20
-rw-r--r--compiler/rustc_abi/src/layout.rs2
-rw-r--r--compiler/rustc_abi/src/lib.rs16
-rw-r--r--compiler/rustc_arena/src/lib.rs1
-rw-r--r--compiler/rustc_borrowck/src/lib.rs4
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs13
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/simd.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs19
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs18
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs6
-rw-r--r--compiler/rustc_const_eval/src/check_consts/qualifs.rs53
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs4
-rw-r--r--compiler/rustc_const_eval/src/lib.rs1
-rw-r--r--compiler/rustc_data_structures/Cargo.toml2
-rw-r--r--compiler/rustc_data_structures/src/lib.rs1
-rw-r--r--compiler/rustc_feature/src/accepted.rs3
-rw-r--r--compiler/rustc_feature/src/unstable.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs21
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs20
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs50
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs35
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs30
-rw-r--r--compiler/rustc_lint/src/builtin.rs2
-rw-r--r--compiler/rustc_lint/src/types.rs5
-rw-r--r--compiler/rustc_lint/src/unused.rs2
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs6
-rw-r--r--compiler/rustc_middle/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/mir/consts.rs11
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs7
-rw-r--r--compiler/rustc_middle/src/traits/select.rs7
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs134
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs2
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs12
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs7
-rw-r--r--compiler/rustc_middle/src/ty/util.rs18
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs7
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs12
-rw-r--r--compiler/rustc_mir_dataflow/src/elaborate_drops.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs16
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/mod.rs5
-rw-r--r--compiler/rustc_mir_dataflow/src/rustc_peek.rs3
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs3
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_drops.rs3
-rw-r--r--compiler/rustc_mir_transform/src/known_panics_lint.rs2
-rw-r--r--compiler/rustc_mir_transform/src/promote_consts.rs4
-rw-r--r--compiler/rustc_mir_transform/src/remove_uninit_drops.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs24
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs31
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs7
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs4
-rw-r--r--compiler/rustc_pattern_analysis/Cargo.toml2
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs6
-rw-r--r--compiler/rustc_resolve/src/lib.rs3
-rw-r--r--compiler/rustc_resolve/src/macros.rs32
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs8
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs5
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs256
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs36
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs112
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs204
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs15
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs11
-rw-r--r--compiler/rustc_transmute/src/lib.rs10
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs6
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs6
-rw-r--r--library/alloc/benches/lib.rs3
-rw-r--r--library/alloc/src/lib.rs3
-rw-r--r--library/alloc/tests/lib.rs3
-rw-r--r--library/core/src/intrinsics.rs1
-rw-r--r--library/core/src/lib.rs3
-rw-r--r--library/core/src/primitive_docs.rs8
-rw-r--r--library/core/src/ptr/const_ptr.rs92
-rw-r--r--library/core/src/ptr/mod.rs491
-rw-r--r--library/core/src/ptr/mut_ptr.rs95
-rw-r--r--library/core/src/ptr/non_null.rs22
-rw-r--r--library/core/src/sync/atomic.rs12
-rw-r--r--library/core/tests/lib.rs3
-rw-r--r--library/panic_unwind/src/lib.rs2
-rw-r--r--library/portable-simd/crates/core_simd/src/lib.rs1
-rw-r--r--library/portable-simd/crates/core_simd/tests/pointers.rs2
-rw-r--r--library/proc_macro/src/lib.rs1
-rw-r--r--library/std/src/fs.rs2
-rw-r--r--library/std/src/lib.rs4
-rw-r--r--library/std/src/sys/pal/windows/c/bindings.txt4
-rw-r--r--library/std/src/sys/pal/windows/c/windows_sys.rs8
-rw-r--r--library/std/src/sys/pal/windows/fs.rs41
-rw-r--r--library/unwind/src/lib.rs1
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs1220
-rw-r--r--src/bootstrap/src/core/builder/mod.rs (renamed from src/bootstrap/src/core/builder.rs)1218
-rw-r--r--src/bootstrap/src/core/builder/tests.rs2
-rw-r--r--src/ci/docker/host-x86_64/dist-various-2/Dockerfile6
-rw-r--r--src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile8
-rwxr-xr-xsrc/ci/docker/scripts/freebsd-toolchain.sh8
-rw-r--r--src/ci/github-actions/jobs.yml10
-rw-r--r--src/doc/rustc/src/platform-support.md18
-rw-r--r--src/doc/rustc/src/platform-support/arm-none-eabi.md15
-rw-r--r--src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md15
-rw-r--r--src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md37
-rw-r--r--src/doc/unstable-book/src/language-features/result-ffi-guarantees.md14
-rw-r--r--src/doc/unstable-book/src/language-features/strict-provenance-lints.md (renamed from src/doc/unstable-book/src/language-features/strict-provenance.md)7
-rw-r--r--src/librustdoc/clean/mod.rs6
-rw-r--r--src/rustdoc-json-types/Cargo.toml2
-rw-r--r--src/rustdoc-json-types/lib.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/indexing_slicing.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/utils.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/trailing_empty_array.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs19
-rw-r--r--src/tools/jsondoclint/Cargo.toml2
-rw-r--r--src/tools/linkchecker/main.rs11
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs9
-rw-r--r--src/tools/miri/src/lib.rs2
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs1
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs1
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs1
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/provenance_transmute.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/ptr_invalid.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs1
-rw-r--r--src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs1
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs1
-rw-r--r--src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs1
-rw-r--r--src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs1
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-affinity.rs1
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs1
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-mem.rs2
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-misc.rs1
-rw-r--r--src/tools/miri/tests/pass-dep/libc/mmap.rs1
-rw-r--r--src/tools/miri/tests/pass/align_offset_symbolic.rs1
-rw-r--r--src/tools/miri/tests/pass/atomic.rs2
-rw-r--r--src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs1
-rw-r--r--src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs1
-rw-r--r--src/tools/miri/tests/pass/const-addrs.rs1
-rw-r--r--src/tools/miri/tests/pass/drop_type_without_drop_glue.rs2
-rw-r--r--src/tools/miri/tests/pass/extern_types.rs2
-rw-r--r--src/tools/miri/tests/pass/provenance.rs1
-rw-r--r--src/tools/miri/tests/pass/ptr_int_from_exposed.rs1
-rw-r--r--src/tools/miri/tests/pass/ptr_raw.rs1
-rw-r--r--src/tools/miri/tests/pass/shims/ptr_mask.rs1
-rw-r--r--src/tools/miri/tests/pass/slices.rs1
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs1
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs1
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs1
-rw-r--r--src/tools/miri/tests/pass/transmute_ptr.rs1
-rw-r--r--src/tools/miri/tests/pass/underscore_pattern.rs1
-rw-r--r--src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs1
-rw-r--r--src/tools/miropt-test-tools/src/lib.rs3
-rw-r--r--src/tools/tidy/Cargo.toml2
-rw-r--r--tests/codegen/atomicptr.rs1
-rw-r--r--tests/codegen/issues/issue-103285-ptr-addr-overflow-check.rs1
-rw-r--r--tests/debuginfo/constant-ordering-prologue.rs6
-rw-r--r--tests/mir-opt/dest-prop/union.rs3
-rw-r--r--tests/mir-opt/gvn_ptr_eq_with_constant.rs2
-rw-r--r--tests/mir-opt/issues/issue_59352.rs1
-rw-r--r--tests/run-make/rustc-crates-on-stable/rmake.rs4
-rw-r--r--tests/rustdoc/impossible-default.rs8
-rw-r--r--tests/ui/argument-suggestions/issue-109425.fixed4
-rw-r--r--tests/ui/argument-suggestions/issue-109425.rs4
-rw-r--r--tests/ui/argument-suggestions/issue-109425.stderr42
-rw-r--r--tests/ui/argument-suggestions/issue-112507.stderr5
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs2
-rw-r--r--tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-result_ffi_guarantees.rs99
-rw-r--r--tests/ui/feature-gates/feature-gate-result_ffi_guarantees.stderr349
-rw-r--r--tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs (renamed from tests/ui/feature-gates/feature-gate-strict_provenance.rs)0
-rw-r--r--tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr (renamed from tests/ui/feature-gates/feature-gate-strict_provenance.stderr)12
-rw-r--r--tests/ui/lint/lint-ctypes-enum.rs1
-rw-r--r--tests/ui/lint/lint-ctypes-enum.stderr58
-rw-r--r--tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs2
-rw-r--r--tests/ui/lint/lint-strict-provenance-lossy-casts.rs2
-rw-r--r--tests/ui/lint/unit_bindings.deny_level.stderr40
-rw-r--r--tests/ui/lint/unit_bindings.rs60
-rw-r--r--tests/ui/lint/unused/unused-macro-rules-compile-error.stderr12
-rw-r--r--tests/ui/lint/unused/unused-macro-rules-decl.stderr16
-rw-r--r--tests/ui/lint/unused/unused-macro-rules.stderr16
-rw-r--r--tests/ui/mir/alignment/i686-pc-windows-msvc.rs2
-rw-r--r--tests/ui/mir/alignment/packed.rs2
-rw-r--r--tests/ui/rfcs/rfc-3391-result-ffi-guarantees.rs133
-rw-r--r--tests/ui/stats/hir-stats.stderr46
-rw-r--r--tests/ui/structs-enums/type-sizes.rs1
-rw-r--r--tests/ui/traits/next-solver/canonical/do-not-bail-due-to-placeholders.rs16
-rw-r--r--tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs52
-rw-r--r--tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs11
-rw-r--r--tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs6
-rw-r--r--tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr11
-rw-r--r--tests/ui/transmutability/dont-assume-err-is-yes-issue-126377.rs19
-rw-r--r--tests/ui/transmutability/dont-assume-err-is-yes-issue-126377.stderr34
231 files changed, 2810 insertions, 3348 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f3a505b6c16..4d54b5aeb4e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1907,7 +1907,7 @@ dependencies = [
  "anyhow",
  "clap",
  "fs-err",
- "rustc-hash 1.1.0",
+ "rustc-hash 2.0.0",
  "rustdoc-json-types",
  "serde",
  "serde_json",
@@ -3514,7 +3514,7 @@ dependencies = [
  "memmap2",
  "parking_lot",
  "portable-atomic",
- "rustc-hash 1.1.0",
+ "rustc-hash 2.0.0",
  "rustc-rayon",
  "rustc-stable-hash",
  "rustc_arena",
@@ -4211,7 +4211,7 @@ dependencies = [
 name = "rustc_pattern_analysis"
 version = "0.0.0"
 dependencies = [
- "rustc-hash 1.1.0",
+ "rustc-hash 2.0.0",
  "rustc_apfloat",
  "rustc_arena",
  "rustc_data_structures",
@@ -4609,7 +4609,7 @@ name = "rustdoc-json-types"
 version = "0.1.0"
 dependencies = [
  "bincode",
- "rustc-hash 1.1.0",
+ "rustc-hash 2.0.0",
  "serde",
  "serde_json",
 ]
@@ -5239,7 +5239,7 @@ dependencies = [
  "ignore",
  "miropt-test-tools",
  "regex",
- "rustc-hash 1.1.0",
+ "rustc-hash 2.0.0",
  "semver",
  "similar",
  "termcolor",
diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs
index 2ecac8a9df1..872cae59a4e 100644
--- a/compiler/rustc_abi/src/callconv.rs
+++ b/compiler/rustc_abi/src/callconv.rs
@@ -3,18 +3,23 @@ mod abi {
     pub(crate) use crate::Variants;
 }
 
+#[cfg(feature = "nightly")]
 use rustc_macros::HashStable_Generic;
 
-use crate::{Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
+#[cfg(feature = "nightly")]
+use crate::{Abi, FieldsShape, TyAbiInterface, TyAndLayout};
+use crate::{Align, HasDataLayout, Size};
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub enum RegKind {
     Integer,
     Float,
     Vector,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub struct Reg {
     pub kind: RegKind,
     pub size: Size,
@@ -108,15 +113,8 @@ impl HomogeneousAggregate {
     }
 }
 
+#[cfg(feature = "nightly")]
 impl<'a, Ty> TyAndLayout<'a, Ty> {
-    /// Returns `true` if this is an aggregate type (including a ScalarPair!)
-    pub fn is_aggregate(&self) -> bool {
-        match self.abi {
-            Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false,
-            Abi::ScalarPair(..) | Abi::Aggregate { .. } => true,
-        }
-    }
-
     /// Returns `Homogeneous` if this layout is an aggregate containing fields of
     /// only a single type (e.g., `(u32, u32)`). Such aggregates are often
     /// special-cased in ABIs.
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 59f42425bb9..0340d1bd6bc 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -11,8 +11,10 @@ use crate::{
     Variants, WrappingRange,
 };
 
+#[cfg(feature = "nightly")]
 mod ty;
 
+#[cfg(feature = "nightly")]
 pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
 
 // A variant is absent if it's uninhabited and only has ZST fields.
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 84d756b6d51..8e90130da4c 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -29,14 +29,14 @@ mod layout;
 mod tests;
 
 pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
-pub use layout::{
-    FIRST_VARIANT, FieldIdx, Layout, LayoutCalculator, LayoutCalculatorError, TyAbiInterface,
-    TyAndLayout, VariantIdx,
-};
+#[cfg(feature = "nightly")]
+pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
+pub use layout::{LayoutCalculator, LayoutCalculatorError};
 
 /// Requirements for a `StableHashingContext` to be used in this crate.
 /// This is a hack to allow using the `HashStable_Generic` derive macro
 /// instead of implementing everything in `rustc_middle`.
+#[cfg(feature = "nightly")]
 pub trait HashStableContext {}
 
 #[derive(Clone, Copy, PartialEq, Eq, Default)]
@@ -1644,6 +1644,14 @@ pub struct LayoutS<FieldIdx: Idx, VariantIdx: Idx> {
 }
 
 impl<FieldIdx: Idx, VariantIdx: Idx> LayoutS<FieldIdx, VariantIdx> {
+    /// Returns `true` if this is an aggregate type (including a ScalarPair!)
+    pub fn is_aggregate(&self) -> bool {
+        match self.abi {
+            Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false,
+            Abi::ScalarPair(..) | Abi::Aggregate { .. } => true,
+        }
+    }
+
     pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
         let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
         let size = scalar.size(cx);
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index cecf223b961..4d8525989cc 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -23,7 +23,6 @@
 #![feature(maybe_uninit_slice)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
-#![feature(strict_provenance)]
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index cbf8aa313c5..698fdafc936 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -187,10 +187,10 @@ fn do_mir_borrowck<'tcx>(
 
     let location_table = LocationTable::new(body);
 
-    let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true);
+    let move_data = MoveData::gather_moves(body, tcx, |_| true);
     let promoted_move_data = promoted
         .iter_enumerated()
-        .map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, param_env, |_| true)));
+        .map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, |_| true)));
 
     let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
         .into_engine(tcx, body)
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 238d7d0749a..7b60a632c30 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1128,7 +1128,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             }
             let projected_ty = curr_projected_ty.projection_ty_core(
                 tcx,
-                self.param_env,
                 proj,
                 |this, field, ()| {
                     let ty = this.field_ty(tcx, field);
@@ -1919,7 +1918,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 // than 1.
                 // If the length is larger than 1, the repeat expression will need to copy the
                 // element, so we require the `Copy` trait.
-                if len.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
+                if len.try_to_target_usize(tcx).is_none_or(|len| len > 1) {
                     match operand {
                         Operand::Copy(..) | Operand::Constant(..) => {
                             // These are always okay: direct use of a const, or a value that can evidently be copied.
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index cbe411d78d5..e7f9f894381 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -133,6 +133,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 .expect_const()
                 .try_to_valtree()
                 .expect("expected monomorphic const in codegen")
+                .0
                 .unwrap_branch();
 
             assert_eq!(x.layout(), y.layout());
@@ -806,8 +807,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => m.load_scalar(fx),
                 ty::Array(elem, len)
                     if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
-                        && len.try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all())
-                            == Some(expected_bytes) =>
+                        && len
+                            .try_to_target_usize(fx.tcx)
+                            .expect("expected monomorphic const in codegen")
+                            == expected_bytes =>
                 {
                     m.force_stack(fx).0.load(
                         fx,
@@ -907,8 +910,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {}
                 ty::Array(elem, len)
                     if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
-                        && len.try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all())
-                            == Some(expected_bytes) => {}
+                        && len
+                            .try_to_target_usize(fx.tcx)
+                            .expect("expected monomorphic const in codegen")
+                            == expected_bytes => {}
                 _ => {
                     fx.tcx.dcx().span_fatal(
                         span,
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
index 4e1b99fdebf..43dbfafa871 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
@@ -76,8 +76,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
             ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
             ty::Array(elem, len)
                 if matches!(*elem.kind(), ty::Uint(ty::UintTy::U8))
-                    && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all())
-                        == Some(expected_bytes) =>
+                    && len
+                        .try_to_target_usize(bx.tcx)
+                        .expect("expected monomorphic const in codegen")
+                        == expected_bytes =>
             {
                 let place = PlaceRef::alloca(bx, args[0].layout);
                 args[0].val.store(bx, place);
@@ -696,8 +698,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
             }
             ty::Array(elem, len)
                 if matches!(*elem.kind(), ty::Uint(ty::UintTy::U8))
-                    && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all())
-                        == Some(expected_bytes) =>
+                    && len
+                        .try_to_target_usize(bx.tcx)
+                        .expect("expected monomorphic const in codegen")
+                        == expected_bytes =>
             {
                 // Zero-extend iN to the array length:
                 let ze = bx.zext(result, bx.type_ix(expected_bytes * 8));
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 64f1d21b438..c9a17c9852d 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -1179,8 +1179,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
             ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
             ty::Array(elem, len)
                 if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
-                    && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all())
-                        == Some(expected_bytes) =>
+                    && len
+                        .try_to_target_usize(bx.tcx)
+                        .expect("expected monomorphic const in codegen")
+                        == expected_bytes =>
             {
                 let place = PlaceRef::alloca(bx, args[0].layout);
                 args[0].val.store(bx, place);
@@ -1245,12 +1247,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
     }
 
     if name == sym::simd_shuffle_generic {
-        let idx = fn_args[2]
-            .expect_const()
-            .eval(tcx, ty::ParamEnv::reveal_all(), span)
-            .unwrap()
-            .1
-            .unwrap_branch();
+        let idx = fn_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
         let n = idx.len() as u64;
 
         let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
@@ -1469,8 +1466,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
             }
             ty::Array(elem, len)
                 if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
-                    && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all())
-                        == Some(expected_bytes) =>
+                    && len
+                        .try_to_target_usize(bx.tcx)
+                        .expect("expected monomorphic const in codegen")
+                        == expected_bytes =>
             {
                 // Zero-extend iN to the array length:
                 let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8));
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 369ab387bea..526d2b86d48 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -23,7 +23,6 @@ use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
 use rustc_middle::ty::{
     self, ExistentialProjection, GenericArgKind, GenericArgsRef, ParamEnv, Ty, TyCtxt,
 };
-use rustc_span::DUMMY_SP;
 use rustc_target::abi::Integer;
 use smallvec::SmallVec;
 
@@ -685,21 +684,25 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
         ty::ConstKind::Param(param) => {
             write!(output, "{}", param.name)
         }
-        ty::ConstKind::Value(ty, _) => {
+        ty::ConstKind::Value(ty, valtree) => {
             match ty.kind() {
                 ty::Int(ity) => {
                     // FIXME: directly extract the bits from a valtree instead of evaluating an
                     // already evaluated `Const` in order to get the bits.
-                    let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all());
+                    let bits = ct
+                        .try_to_bits(tcx, ty::ParamEnv::reveal_all())
+                        .expect("expected monomorphic const in codegen");
                     let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
                     write!(output, "{val}")
                 }
                 ty::Uint(_) => {
-                    let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all());
+                    let val = ct
+                        .try_to_bits(tcx, ty::ParamEnv::reveal_all())
+                        .expect("expected monomorphic const in codegen");
                     write!(output, "{val}")
                 }
                 ty::Bool => {
-                    let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap();
+                    let val = ct.try_to_bool().expect("expected monomorphic const in codegen");
                     write!(output, "{val}")
                 }
                 _ => {
@@ -711,8 +714,9 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
                     // avoiding collisions and will make the emitted type names shorter.
                     let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
                         let mut hasher = StableHasher::new();
-                        let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP).unwrap();
-                        hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher));
+                        hcx.while_hashing_spans(false, |hcx| {
+                            (ty, valtree).hash_stable(hcx, &mut hasher)
+                        });
                         hasher.finish::<Hash64>()
                     });
 
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index cbd95146294..73bfa9dbd10 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -11,7 +11,6 @@
 #![feature(let_chains)]
 #![feature(negative_impls)]
 #![feature(rustdoc_internals)]
-#![feature(strict_provenance)]
 #![feature(trait_alias)]
 #![feature(try_blocks)]
 #![warn(unreachable_pub)]
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index a132ca69540..82fea4c58e1 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -361,12 +361,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
             (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
             (Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
-            (Pointer(..), Int(..)) => bx.ptrtoint(imm, to_backend_ty),
+            (Pointer(..), Int(..)) => {
+                // FIXME: this exposes the provenance, which shouldn't be necessary.
+                bx.ptrtoint(imm, to_backend_ty)
+            }
             (Float(_), Pointer(..)) => {
                 let int_imm = bx.bitcast(imm, bx.cx().type_isize());
                 bx.ptradd(bx.const_null(bx.type_ptr()), int_imm)
             }
             (Pointer(..), Float(_)) => {
+                // FIXME: this exposes the provenance, which shouldn't be necessary.
                 let int_imm = bx.ptrtoint(imm, bx.cx().type_isize());
                 bx.bitcast(int_imm, to_backend_ty)
             }
diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
index 7358a6c6d22..547030a1854 100644
--- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
@@ -6,13 +6,10 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_hir::LangItem;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::mir::*;
-use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
 use rustc_middle::{bug, mir};
-use rustc_trait_selection::traits::{
-    ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
-};
-use tracing::{instrument, trace};
+use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
+use tracing::instrument;
 
 use super::ConstCx;
 
@@ -195,50 +192,8 @@ impl Qualif for NeedsNonConstDrop {
             return false;
         }
 
-        // FIXME(effects): If `destruct` is not a `const_trait`,
-        // or effects are disabled in this crate, then give up.
-        let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
-        if !cx.tcx.has_host_param(destruct_def_id) || !cx.tcx.features().effects {
-            return NeedsDrop::in_any_value_of_ty(cx, ty);
-        }
-
-        let obligation = Obligation::new(
-            cx.tcx,
-            ObligationCause::dummy_with_span(cx.body.span),
-            cx.param_env,
-            ty::TraitRef::new(cx.tcx, destruct_def_id, [
-                ty::GenericArg::from(ty),
-                ty::GenericArg::from(cx.tcx.expected_host_effect_param_for_body(cx.def_id())),
-            ]),
-        );
-
-        let infcx = cx.tcx.infer_ctxt().build();
-        let mut selcx = SelectionContext::new(&infcx);
-        let Some(impl_src) = selcx.select(&obligation).ok().flatten() else {
-            // If we couldn't select a const destruct candidate, then it's bad
-            return true;
-        };
-
-        trace!(?impl_src);
-
-        if !matches!(
-            impl_src,
-            ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(_)
-        ) {
-            // If our const destruct candidate is not ConstDestruct or implied by the param env,
-            // then it's bad
-            return true;
-        }
-
-        if impl_src.borrow_nested_obligations().is_empty() {
-            return false;
-        }
-
-        // If we had any errors, then it's bad
-        let ocx = ObligationCtxt::new(&infcx);
-        ocx.register_obligations(impl_src.nested_obligations());
-        let errors = ocx.select_all_or_error();
-        !errors.is_empty()
+        // FIXME(effects): Reimplement const drop checking.
+        NeedsDrop::in_any_value_of_ty(cx, ty)
     }
 
     fn in_adt_inherently<'tcx>(
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index ba19f642795..e2e4754a45c 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -43,7 +43,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
 
     // We go to `usize` as we cannot allocate anything bigger anyway.
     let (field_count, variant, down) = match ty.kind() {
-        ty::Array(_, len) => (len.eval_target_usize(tcx.tcx, param_env) as usize, None, op),
+        ty::Array(_, len) => (len.try_to_target_usize(tcx.tcx)? as usize, None, op),
         ty::Adt(def, _) if def.variants().is_empty() => {
             return None;
         }
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 1def3d08328..64b15611316 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -391,7 +391,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 let ptr = self.read_pointer(src)?;
                 let val = Immediate::new_slice(
                     ptr,
-                    length.eval_target_usize(*self.tcx, self.param_env),
+                    length
+                        .try_to_target_usize(*self.tcx)
+                        .expect("expected monomorphic const in const eval"),
                     self,
                 );
                 self.write_immediate(val, dest)
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index 39e2d3b4ebb..0490195caf4 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -10,7 +10,6 @@
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
 #![feature(slice_ptr_get)]
-#![feature(strict_provenance)]
 #![feature(trait_alias)]
 #![feature(try_blocks)]
 #![feature(unqualified_local_imports)]
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index d73cf11ee64..5a477143a62 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -13,7 +13,7 @@ ena = "0.14.3"
 indexmap = { version = "2.4.0" }
 jobserver_crate = { version = "0.1.28", package = "jobserver" }
 measureme = "11"
-rustc-hash = "1.1.0"
+rustc-hash = "2.0.0"
 rustc-rayon = { version = "0.5.0", optional = true }
 rustc-stable-hash = { version = "0.1.0", features = ["nightly"] }
 rustc_arena = { path = "../rustc_arena" }
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index fba2707922b..afac08ae6f8 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -33,7 +33,6 @@
 #![feature(ptr_alignment_type)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
-#![feature(strict_provenance)]
 #![feature(test)]
 #![feature(thread_id_value)]
 #![feature(type_alias_impl_trait)]
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 0c5fe6e8d8b..53445804694 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -353,6 +353,9 @@ declare_features! (
     (accepted, repr_packed, "1.33.0", Some(33158)),
     /// Allows `#[repr(transparent)]` attribute on newtype structs.
     (accepted, repr_transparent, "1.28.0", Some(43036)),
+    /// Allows enums like Result<T, E> to be used across FFI, if T's niche value can
+    /// be used to describe E or vice-versa.
+    (accepted, result_ffi_guarantees, "CURRENT_RUSTC_VERSION", Some(110503)),
     /// Allows return-position `impl Trait` in traits.
     (accepted, return_position_impl_trait_in_trait, "1.75.0", Some(91611)),
     /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414).
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 1067156958d..f4795c75e48 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -580,9 +580,6 @@ declare_features! (
     (incomplete, repr128, "1.16.0", Some(56071)),
     /// Allows `repr(simd)` and importing the various simd intrinsics.
     (unstable, repr_simd, "1.4.0", Some(27731)),
-    /// Allows enums like Result<T, E> to be used across FFI, if T's niche value can
-    /// be used to describe E or vise-versa.
-    (unstable, result_ffi_guarantees, "1.80.0", Some(110503)),
     /// Allows bounding the return type of AFIT/RPITIT.
     (unstable, return_type_notation, "1.70.0", Some(109417)),
     /// Allows `extern "rust-cold"`.
@@ -598,7 +595,7 @@ declare_features! (
     /// Allows attributes on expressions and non-item statements.
     (unstable, stmt_expr_attributes, "1.6.0", Some(15701)),
     /// Allows lints part of the strict provenance effort.
-    (unstable, strict_provenance, "1.61.0", Some(95228)),
+    (unstable, strict_provenance_lints, "1.61.0", Some(130351)),
     /// Allows string patterns to dereference values to match them.
     (unstable, string_deref_patterns, "1.67.0", Some(87121)),
     /// Allows the use of `#[target_feature]` on safe functions.
diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index 7f4ab352ef2..9a2c38e51e2 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -51,7 +51,7 @@ impl<'tcx> Bounds<'tcx> {
         bound_trait_ref: ty::PolyTraitRef<'tcx>,
         span: Span,
         polarity: ty::PredicatePolarity,
-        constness: ty::BoundConstness,
+        constness: Option<ty::BoundConstness>,
         predicate_filter: PredicateFilter,
     ) {
         let clause = (
@@ -88,19 +88,20 @@ impl<'tcx> Bounds<'tcx> {
         // associated type of `<T as Tr>` and make sure that the effect is compatible.
         let compat_val = match (tcx.def_kind(defining_def_id), constness) {
             // FIXME(effects): revisit the correctness of this
-            (_, ty::BoundConstness::Const) => tcx.consts.false_,
+            (_, Some(ty::BoundConstness::Const)) => tcx.consts.false_,
             // body owners that can have trait bounds
-            (DefKind::Const | DefKind::Fn | DefKind::AssocFn, ty::BoundConstness::ConstIfConst) => {
-                tcx.expected_host_effect_param_for_body(defining_def_id)
-            }
+            (
+                DefKind::Const | DefKind::Fn | DefKind::AssocFn,
+                Some(ty::BoundConstness::ConstIfConst),
+            ) => tcx.expected_host_effect_param_for_body(defining_def_id),
 
-            (_, ty::BoundConstness::NotConst) => {
+            (_, None) => {
                 if !tcx.is_const_trait(bound_trait_ref.def_id()) {
                     return;
                 }
                 tcx.consts.true_
             }
-            (DefKind::Trait, ty::BoundConstness::ConstIfConst) => {
+            (DefKind::Trait, Some(ty::BoundConstness::ConstIfConst)) => {
                 // we are in a trait, where `bound_trait_ref` could be:
                 // (1) a super trait `trait Foo: ~const Bar`.
                 //     - This generates `<Self as Foo>::Effects: TyCompat<<Self as Bar>::Effects>`
@@ -138,7 +139,7 @@ impl<'tcx> Bounds<'tcx> {
                 return;
             }
 
-            (DefKind::Impl { of_trait: true }, ty::BoundConstness::ConstIfConst) => {
+            (DefKind::Impl { of_trait: true }, Some(ty::BoundConstness::ConstIfConst)) => {
                 // this is a where clause on an impl header.
                 // push `<T as Tr>::Effects` into the set for the `Min` bound.
                 let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else {
@@ -172,12 +173,12 @@ impl<'tcx> Bounds<'tcx> {
             //
             // FIXME(effects) this is equality for now, which wouldn't be helpful for a non-const implementor
             // that uses a `Bar` that implements `Trait` with `Maybe` effects.
-            (DefKind::AssocTy, ty::BoundConstness::ConstIfConst) => {
+            (DefKind::AssocTy, Some(ty::BoundConstness::ConstIfConst)) => {
                 // FIXME(effects): implement this
                 return;
             }
             // probably illegal in this position.
-            (_, ty::BoundConstness::ConstIfConst) => {
+            (_, Some(ty::BoundConstness::ConstIfConst)) => {
                 tcx.dcx().span_delayed_bug(span, "invalid `~const` encountered");
                 return;
             }
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 94da3d4ea84..34f596271aa 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1037,7 +1037,11 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
             return;
         }
 
-        if let Some(len) = len_const.try_eval_target_usize(tcx, tcx.param_env(def.did())) {
+        // FIXME(repr_simd): This check is nice, but perhaps unnecessary due to the fact
+        // we do not expect users to implement their own `repr(simd)` types. If they could,
+        // this check is easily side-steppable by hiding the const behind normalization.
+        // The consequence is that the error is, in general, only observable post-mono.
+        if let Some(len) = len_const.try_to_target_usize(tcx) {
             if len == 0 {
                 struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit();
                 return;
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index 71eb368185e..bbff00cd3b3 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -76,9 +76,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
 
                 let (size, ty) = match elem_ty.kind() {
                     ty::Array(ty, len) => {
-                        if let Some(len) =
-                            len.try_eval_target_usize(self.tcx, self.tcx.param_env(adt.did()))
-                        {
+                        if let Some(len) = len.try_to_target_usize(self.tcx) {
                             (len, *ty)
                         } else {
                             return None;
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 4346504450d..69ebd3a928a 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -13,6 +13,7 @@ use tracing::{debug, instrument};
 
 use super::ItemCtxt;
 use super::predicates_of::assert_only_contains_predicates_from;
+use crate::bounds::Bounds;
 use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter};
 
 /// For associated types we include both bounds written on the type
@@ -36,7 +37,8 @@ fn associated_type_bounds<'tcx>(
     );
 
     let icx = ItemCtxt::new(tcx, assoc_item_def_id);
-    let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter);
+    let mut bounds = Bounds::default();
+    icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
     // Associated types are implicitly sized unless a `?Sized` bound is found
     icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
 
@@ -303,7 +305,8 @@ fn opaque_type_bounds<'tcx>(
 ) -> &'tcx [(ty::Clause<'tcx>, Span)] {
     ty::print::with_reduced_queries!({
         let icx = ItemCtxt::new(tcx, opaque_def_id);
-        let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter);
+        let mut bounds = Bounds::default();
+        icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
         // Opaque types are implicitly sized unless a `?Sized` bound is found
         icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
         debug!(?bounds);
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 421ba40aa88..9bd8c70dcfe 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -181,9 +181,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     // on a trait we must also consider the bounds that follow the trait's name,
     // like `trait Foo: A + B + C`.
     if let Some(self_bounds) = is_trait {
-        let bounds = icx.lowerer().lower_mono_bounds(
+        let mut bounds = Bounds::default();
+        icx.lowerer().lower_bounds(
             tcx.types.self_param,
             self_bounds,
+            &mut bounds,
+            ty::List::empty(),
             PredicateFilter::All,
         );
         predicates.extend(bounds.clauses(tcx));
@@ -265,9 +268,9 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                 }
 
                 let mut bounds = Bounds::default();
-                icx.lowerer().lower_poly_bounds(
+                icx.lowerer().lower_bounds(
                     ty,
-                    bound_pred.bounds.iter(),
+                    bound_pred.bounds,
                     &mut bounds,
                     bound_vars,
                     PredicateFilter::All,
@@ -626,7 +629,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
         bug!("trait_def_id {trait_def_id:?} is not an item");
     };
 
-    let (generics, bounds) = match item.kind {
+    let (generics, superbounds) = match item.kind {
         hir::ItemKind::Trait(.., generics, supertraits, _) => (generics, supertraits),
         hir::ItemKind::TraitAlias(generics, supertraits) => (generics, supertraits),
         _ => span_bug!(item.span, "super_predicates invoked on non-trait"),
@@ -635,7 +638,8 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
     let icx = ItemCtxt::new(tcx, trait_def_id);
 
     let self_param_ty = tcx.types.self_param;
-    let superbounds = icx.lowerer().lower_mono_bounds(self_param_ty, bounds, filter);
+    let mut bounds = Bounds::default();
+    icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter);
 
     let where_bounds_that_match = icx.probe_ty_param_bounds_in_generics(
         generics,
@@ -646,7 +650,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
 
     // Combine the two lists to form the complete set of superbounds:
     let implied_bounds =
-        &*tcx.arena.alloc_from_iter(superbounds.clauses(tcx).chain(where_bounds_that_match));
+        &*tcx.arena.alloc_from_iter(bounds.clauses(tcx).chain(where_bounds_that_match));
     debug!(?implied_bounds);
 
     // Now require that immediate supertraits are lowered, which will, in
@@ -834,9 +838,9 @@ impl<'tcx> ItemCtxt<'tcx> {
             };
 
             let bound_vars = self.tcx.late_bound_vars(predicate.hir_id);
-            self.lowerer().lower_poly_bounds(
+            self.lowerer().lower_bounds(
                 bound_ty,
-                predicate.bounds.iter(),
+                predicate.bounds,
                 &mut bounds,
                 bound_vars,
                 filter,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 310f648b980..4721a3a0cf5 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -142,7 +142,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     /// There is an implied binder around `param_ty` and `hir_bounds`.
     /// See `lower_poly_trait_ref` for more details.
     #[instrument(level = "debug", skip(self, hir_bounds, bounds))]
-    pub(crate) fn lower_poly_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'tcx>>>(
+    pub(crate) fn lower_bounds<'hir, I: IntoIterator<Item = &'hir hir::GenericBound<'tcx>>>(
         &self,
         param_ty: Ty<'tcx>,
         hir_bounds: I,
@@ -171,16 +171,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 hir::GenericBound::Trait(poly_trait_ref) => {
                     let (constness, polarity) = match poly_trait_ref.modifiers {
                         hir::TraitBoundModifier::Const => {
-                            (ty::BoundConstness::Const, ty::PredicatePolarity::Positive)
-                        }
-                        hir::TraitBoundModifier::MaybeConst => {
-                            (ty::BoundConstness::ConstIfConst, ty::PredicatePolarity::Positive)
-                        }
-                        hir::TraitBoundModifier::None => {
-                            (ty::BoundConstness::NotConst, ty::PredicatePolarity::Positive)
+                            (Some(ty::BoundConstness::Const), ty::PredicatePolarity::Positive)
                         }
+                        hir::TraitBoundModifier::MaybeConst => (
+                            Some(ty::BoundConstness::ConstIfConst),
+                            ty::PredicatePolarity::Positive,
+                        ),
+                        hir::TraitBoundModifier::None => (None, ty::PredicatePolarity::Positive),
                         hir::TraitBoundModifier::Negative => {
-                            (ty::BoundConstness::NotConst, ty::PredicatePolarity::Negative)
+                            (None, ty::PredicatePolarity::Negative)
                         }
                         hir::TraitBoundModifier::Maybe => continue,
                     };
@@ -212,35 +211,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
     }
 
-    /// Lower HIR bounds into `bounds` given the self type `param_ty` and *no* overarching late-bound vars.
-    ///
-    /// ### Example
-    ///
-    /// ```ignore (illustrative)
-    /// fn foo<T: Bar + Baz>() { }
-    /// //     ^  ^^^^^^^^^ hir_bounds
-    /// //     param_ty
-    /// ```
-    pub(crate) fn lower_mono_bounds(
-        &self,
-        param_ty: Ty<'tcx>,
-        hir_bounds: &[hir::GenericBound<'tcx>],
-        predicate_filter: PredicateFilter,
-    ) -> Bounds<'tcx> {
-        let mut bounds = Bounds::default();
-
-        self.lower_poly_bounds(
-            param_ty,
-            hir_bounds.iter(),
-            &mut bounds,
-            ty::List::empty(),
-            predicate_filter,
-        );
-        debug!(?bounds);
-
-        bounds
-    }
-
     /// Lower an associated item constraint from the HIR into `bounds`.
     ///
     /// ### A Note on Binders
@@ -444,9 +414,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         // parameter to have a skipped binder.
                         let param_ty =
                             Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder());
-                        self.lower_poly_bounds(
+                        self.lower_bounds(
                             param_ty,
-                            hir_bounds.iter(),
+                            hir_bounds,
                             bounds,
                             projection_ty.bound_vars(),
                             predicate_filter,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
index 2cf97e29060..a1ee120e855 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
@@ -51,7 +51,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             } = self.lower_poly_trait_ref(
                 &trait_bound.trait_ref,
                 trait_bound.span,
-                ty::BoundConstness::NotConst,
+                None,
                 ty::PredicatePolarity::Positive,
                 dummy_self,
                 &mut bounds,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 828809726d2..fe0cd572609 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -652,7 +652,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         trait_ref: &hir::TraitRef<'tcx>,
         span: Span,
-        constness: ty::BoundConstness,
+        constness: Option<ty::BoundConstness>,
         polarity: ty::PredicatePolarity,
         self_ty: Ty<'tcx>,
         bounds: &mut Bounds<'tcx>,
@@ -675,7 +675,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             Some(self_ty),
         );
 
-        if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness
+        if let Some(constness) = constness
             && !self.tcx().is_const_trait(trait_def_id)
         {
             self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait {
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 13ba615d4c9..fa6a86c6911 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -537,40 +537,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 //
                 // This check is here because there is currently no way to express a trait bound for `FnDef` types only.
                 if let ty::FnDef(def_id, _args) = *arg_ty.kind() {
-                    let fn_once_def_id =
-                        self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span));
-                    let fn_once_output_def_id =
-                        self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span));
-                    if self.tcx.has_host_param(fn_once_def_id) {
-                        let const_param: ty::GenericArg<'tcx> =
-                            ([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into();
-                        self.register_predicate(traits::Obligation::new(
-                            self.tcx,
-                            self.misc(span),
-                            self.param_env,
-                            ty::TraitRef::new(self.tcx, fn_once_def_id, [
-                                arg_ty.into(),
-                                fn_sig.inputs()[0].into(),
-                                const_param,
-                            ]),
-                        ));
-
-                        self.register_predicate(traits::Obligation::new(
-                            self.tcx,
-                            self.misc(span),
-                            self.param_env,
-                            ty::ProjectionPredicate {
-                                projection_term: ty::AliasTerm::new(
-                                    self.tcx,
-                                    fn_once_output_def_id,
-                                    [arg_ty.into(), fn_sig.inputs()[0].into(), const_param],
-                                ),
-                                term: fn_sig.output().into(),
-                            },
-                        ));
-
-                        self.select_obligations_where_possible(|_| {});
-                    } else if idx == 0 && !self.tcx.is_const_fn_raw(def_id) {
+                    if idx == 0 && !self.tcx.is_const_fn_raw(def_id) {
                         self.dcx().emit_err(errors::ConstSelectMustBeConst { span });
                     }
                 } else {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index eccb18ad6c4..2bc84d1ac67 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1487,7 +1487,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
         } else if self.tcx.features().generic_const_exprs {
-            ct.normalize(self.tcx, self.param_env)
+            ct.normalize_internal(self.tcx, self.param_env)
         } else {
             ct
         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index bf8cc462189..f29d6199a87 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1097,6 +1097,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut only_extras_so_far = errors
             .peek()
             .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0));
+        let mut prev_extra_idx = None;
         let mut suggestions = vec![];
         while let Some(error) = errors.next() {
             only_extras_so_far &= matches!(error, Error::Extra(_));
@@ -1165,11 +1166,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         //     fn f() {}
                         //   - f(0, 1,)
                         //   + f()
-                        if only_extras_so_far
-                            && !errors
-                                .peek()
-                                .is_some_and(|next_error| matches!(next_error, Error::Extra(_)))
-                        {
+                        let trim_next_comma = match errors.peek() {
+                            Some(Error::Extra(provided_idx))
+                                if only_extras_so_far
+                                    && provided_idx.index() > arg_idx.index() + 1 =>
+                            // If the next Error::Extra ("next") doesn't next to current ("current"),
+                            // fn foo(_: (), _: u32) {}
+                            // - foo("current", (), 1u32, "next")
+                            // + foo((), 1u32)
+                            // If the previous error is not a `Error::Extra`, then do not trim the next comma
+                            // - foo((), "current", 42u32, "next")
+                            // + foo((), 42u32)
+                            {
+                                prev_extra_idx.map_or(true, |prev_extra_idx| {
+                                    prev_extra_idx + 1 == arg_idx.index()
+                                })
+                            }
+                            // If no error left, we need to delete the next comma
+                            None if only_extras_so_far => true,
+                            // Not sure if other error type need to be handled as well
+                            _ => false,
+                        };
+
+                        if trim_next_comma {
                             let next = provided_arg_tys
                                 .get(arg_idx + 1)
                                 .map(|&(_, sp)| sp)
@@ -1192,6 +1211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             SuggestionText::Remove(_) => SuggestionText::Remove(true),
                             _ => SuggestionText::DidYouMean,
                         };
+                        prev_extra_idx = Some(arg_idx.index())
                     }
                 }
                 Error::Missing(expected_idx) => {
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8bd9c899a62..ef8100da9e2 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -2601,7 +2601,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
                     ty.tuple_fields().iter().find_map(|field| ty_find_init_error(cx, field, init))
                 }
                 Array(ty, len) => {
-                    if matches!(len.try_eval_target_usize(cx.tcx, cx.param_env), Some(v) if v > 0) {
+                    if matches!(len.try_to_target_usize(cx.tcx), Some(v) if v > 0) {
                         // Array length known at array non-empty -- recurse.
                         ty_find_init_error(cx, *ty, init)
                     } else {
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index e9c61a41d6d..60ff925b40e 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -730,7 +730,6 @@ fn is_niche_optimization_candidate<'tcx>(
 /// can, return the type that `ty` can be safely converted to, otherwise return `None`.
 /// Currently restricted to function pointers, boxes, references, `core::num::NonZero`,
 /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
-/// FIXME: This duplicates code in codegen.
 pub(crate) fn repr_nullable_ptr<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -743,10 +742,6 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
             [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
                 ([], [field]) | ([field], []) => field.ty(tcx, args),
                 ([field1], [field2]) => {
-                    if !tcx.features().result_ffi_guarantees {
-                        return None;
-                    }
-
                     let ty1 = field1.ty(tcx, args);
                     let ty2 = field2.ty(tcx, args);
 
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 1a007250961..ddc18c755a8 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -346,7 +346,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                         None
                     }
                 }
-                ty::Array(ty, len) => match len.try_eval_target_usize(cx.tcx, cx.param_env) {
+                ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) {
                     // If the array is empty we don't lint, to avoid false positives
                     Some(0) | None => None,
                     // If the array is definitely non-empty, we can do `#[must_use]` checking.
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index c11571182fe..ffbe5363897 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -2667,7 +2667,6 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust
-    /// #![feature(strict_provenance)]
     /// #![warn(fuzzy_provenance_casts)]
     ///
     /// fn main() {
@@ -2701,7 +2700,7 @@ declare_lint! {
     pub FUZZY_PROVENANCE_CASTS,
     Allow,
     "a fuzzy integer to pointer cast is used",
-    @feature_gate = strict_provenance;
+    @feature_gate = strict_provenance_lints;
 }
 
 declare_lint! {
@@ -2711,7 +2710,6 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust
-    /// #![feature(strict_provenance)]
     /// #![warn(lossy_provenance_casts)]
     ///
     /// fn main() {
@@ -2747,7 +2745,7 @@ declare_lint! {
     pub LOSSY_PROVENANCE_CASTS,
     Allow,
     "a lossy pointer to integer cast is used",
-    @feature_gate = strict_provenance;
+    @feature_gate = strict_provenance_lints;
 }
 
 declare_lint! {
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index e9b73d25ba2..04a06ba7464 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -56,7 +56,6 @@
 #![feature(ptr_alignment_type)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
-#![feature(strict_provenance)]
 #![feature(trait_upcasting)]
 #![feature(trusted_len)]
 #![feature(try_blocks)]
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index 4262460d928..465aa0eee03 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -1,5 +1,6 @@
 use std::fmt::{self, Debug, Display, Formatter};
 
+use either::Either;
 use rustc_hir::def_id::DefId;
 use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
 use rustc_session::RemapFileNameExt;
@@ -320,8 +321,14 @@ impl<'tcx> Const<'tcx> {
             Const::Ty(_, c) => {
                 // We want to consistently have a "clean" value for type system constants (i.e., no
                 // data hidden in the padding), so we always go through a valtree here.
-                let (ty, val) = c.eval(tcx, param_env, span)?;
-                Ok(tcx.valtree_to_const_val((ty, val)))
+                match c.eval_valtree(tcx, param_env, span) {
+                    Ok((ty, val)) => Ok(tcx.valtree_to_const_val((ty, val))),
+                    Err(Either::Left(_bad_ty)) => Err(tcx
+                        .dcx()
+                        .delayed_bug("`mir::Const::eval` called on a non-valtree-compatible type")
+                        .into()),
+                    Err(Either::Right(e)) => Err(e),
+                }
             }
             Const::Unevaluated(uneval, _) => {
                 // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index 8d57d0d8654..476e352ed92 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -55,7 +55,7 @@ impl<'tcx> PlaceTy<'tcx> {
     /// `PlaceElem`, where we can just use the `Ty` that is already
     /// stored inline on field projection elems.
     pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
-        self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty, |_, ty| ty)
+        self.projection_ty_core(tcx, &elem, |_, _, ty| ty, |_, ty| ty)
     }
 
     /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
@@ -66,7 +66,6 @@ impl<'tcx> PlaceTy<'tcx> {
     pub fn projection_ty_core<V, T>(
         self,
         tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
         elem: &ProjectionElem<V, T>,
         mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
         mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>,
@@ -93,7 +92,9 @@ impl<'tcx> PlaceTy<'tcx> {
                     ty::Slice(..) => self.ty,
                     ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from),
                     ty::Array(inner, size) if from_end => {
-                        let size = size.eval_target_usize(tcx, param_env);
+                        let size = size
+                            .try_to_target_usize(tcx)
+                            .expect("expected subslice projection on fixed-size array");
                         let len = size - from - to;
                         Ty::new_array(tcx, *inner, len)
                     }
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index 66035464fa5..05556ae38a8 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -161,9 +161,7 @@ pub enum SelectionCandidate<'tcx> {
 
     /// Implementation of a `Fn`-family trait by one of the anonymous
     /// types generated for a fn pointer type (e.g., `fn(int) -> int`)
-    FnPointerCandidate {
-        fn_host_effect: ty::Const<'tcx>,
-    },
+    FnPointerCandidate,
 
     TraitAliasCandidate,
 
@@ -180,9 +178,6 @@ pub enum SelectionCandidate<'tcx> {
     BuiltinObjectCandidate,
 
     BuiltinUnsizeCandidate,
-
-    /// Implementation of `const Destruct`, optionally from a custom `impl const Drop`.
-    ConstDestructCandidate(Option<DefId>),
 }
 
 /// The result of trait evaluation. The order is important
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 389d20f315f..eb715be22b1 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -398,133 +398,65 @@ impl<'tcx> Const<'tcx> {
         }
     }
 
-    /// Returns the evaluated constant
-    #[inline]
-    pub fn eval(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-        span: Span,
-    ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> {
-        self.eval_valtree(tcx, param_env, span).map_err(|err| {
-            match err {
-                Either::Right(err) => err,
-                Either::Left(_bad_ty) => {
-                    // This can happen when we run on ill-typed code.
-                    let e = tcx.dcx().span_delayed_bug(
-                        span,
-                        "`ty::Const::eval` called on a non-valtree-compatible type",
-                    );
-                    e.into()
-                }
-            }
-        })
-    }
-
     /// Normalizes the constant to a value or an error if possible.
     #[inline]
-    pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
-        match self.eval(tcx, param_env, DUMMY_SP) {
+    pub fn normalize_internal(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
+        match self.eval_valtree(tcx, param_env, DUMMY_SP) {
             Ok((ty, val)) => Self::new_value(tcx, val, ty),
-            Err(ErrorHandled::Reported(r, _span)) => Self::new_error(tcx, r.into()),
-            Err(ErrorHandled::TooGeneric(_span)) => self,
+            Err(Either::Left(_bad_ty)) => {
+                // This can happen when we run on ill-typed code.
+                Self::new_error(
+                    tcx,
+                    tcx.dcx()
+                        .delayed_bug("`ty::Const::eval` called on a non-valtree-compatible type"),
+                )
+            }
+            Err(Either::Right(ErrorHandled::Reported(r, _span))) => Self::new_error(tcx, r.into()),
+            Err(Either::Right(ErrorHandled::TooGeneric(_span))) => self,
         }
     }
 
-    #[inline]
-    pub fn try_eval_scalar(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-    ) -> Option<(Ty<'tcx>, Scalar)> {
-        let (ty, val) = self.eval(tcx, param_env, DUMMY_SP).ok()?;
-        let val = val.try_to_scalar()?;
-        Some((ty, val))
-    }
-
-    #[inline]
-    /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
-    /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
-    /// contains const generic parameters or pointers).
-    pub fn try_eval_scalar_int(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-    ) -> Option<(Ty<'tcx>, ScalarInt)> {
-        let (ty, scalar) = self.try_eval_scalar(tcx, param_env)?;
-        let val = scalar.try_to_scalar_int().ok()?;
-        Some((ty, val))
-    }
-
-    #[inline]
-    /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
-    /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
-    /// contains const generic parameters or pointers).
-    pub fn try_eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u128> {
-        let (ty, scalar) = self.try_eval_scalar_int(tcx, param_env)?;
-        let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
-        // if `ty` does not depend on generic parameters, use an empty param_env
-        Some(scalar.to_bits(size))
-    }
-
-    #[inline]
-    /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
-    pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u128 {
-        self.try_eval_bits(tcx, param_env)
-            .unwrap_or_else(|| bug!("failed to evalate {:#?} to bits", self))
-    }
-
-    #[inline]
-    pub fn try_eval_target_usize(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-    ) -> Option<u64> {
-        let (_, scalar) = self.try_eval_scalar_int(tcx, param_env)?;
-        Some(scalar.to_target_usize(tcx))
-    }
-
-    #[inline]
-    pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
-        let (_, scalar) = self.try_eval_scalar_int(tcx, param_env)?;
-        scalar.try_into().ok()
-    }
-
-    #[inline]
-    /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
-    pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 {
-        self.try_eval_target_usize(tcx, param_env)
-            .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
-    }
-
     /// Panics if self.kind != ty::ConstKind::Value
-    pub fn to_valtree(self) -> ty::ValTree<'tcx> {
+    pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) {
         match self.kind() {
-            ty::ConstKind::Value(_, valtree) => valtree,
+            ty::ConstKind::Value(ty, valtree) => (valtree, ty),
             _ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
         }
     }
 
     /// Attempts to convert to a `ValTree`
-    pub fn try_to_valtree(self) -> Option<ty::ValTree<'tcx>> {
+    pub fn try_to_valtree(self) -> Option<(ty::ValTree<'tcx>, Ty<'tcx>)> {
         match self.kind() {
-            ty::ConstKind::Value(_, valtree) => Some(valtree),
+            ty::ConstKind::Value(ty, valtree) => Some((valtree, ty)),
             _ => None,
         }
     }
 
     #[inline]
-    pub fn try_to_scalar(self) -> Option<Scalar> {
-        self.try_to_valtree()?.try_to_scalar()
+    pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> {
+        let (valtree, ty) = self.try_to_valtree()?;
+        Some((valtree.try_to_scalar()?, ty))
     }
 
     pub fn try_to_bool(self) -> Option<bool> {
-        self.try_to_valtree()?.try_to_scalar_int()?.try_to_bool().ok()
+        self.try_to_valtree()?.0.try_to_scalar_int()?.try_to_bool().ok()
     }
 
     #[inline]
     pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
-        self.try_to_valtree()?.try_to_target_usize(tcx)
+        self.try_to_valtree()?.0.try_to_target_usize(tcx)
+    }
+
+    #[inline]
+    /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
+    /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
+    /// contains const generic parameters or pointers).
+    pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u128> {
+        let (scalar, ty) = self.try_to_scalar()?;
+        let scalar = scalar.try_to_scalar_int().ok()?;
+        let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
+        // if `ty` does not depend on generic parameters, use an empty param_env
+        Some(scalar.to_bits(size))
     }
 
     pub fn is_ct_infer(self) -> bool {
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
index 54b8507babf..bf741f63a3d 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
@@ -85,7 +85,7 @@ impl<'tcx> InhabitedPredicate<'tcx> {
         match self {
             Self::False => Ok(false),
             Self::True => Ok(true),
-            Self::ConstIsZero(const_) => match const_.try_eval_target_usize(tcx, param_env) {
+            Self::ConstIsZero(const_) => match const_.try_to_target_usize(tcx) {
                 None | Some(0) => Ok(true),
                 Some(1..) => Ok(false),
             },
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 6c12b691c26..3670b6bbc77 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -397,7 +397,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
                 }
             }
             ty::Array(inner, len) if tcx.features().transmute_generic_consts => {
-                let len_eval = len.try_eval_target_usize(tcx, param_env);
+                let len_eval = len.try_to_target_usize(tcx);
                 if len_eval == Some(0) {
                     return Ok(SizeSkeleton::Known(Size::from_bytes(0), None));
                 }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 7ada5fd93ba..b5495fa282b 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1956,7 +1956,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         define_scoped_cx!(self);
 
         match constness {
-            ty::BoundConstness::NotConst => {}
             ty::BoundConstness::Const => {
                 p!("const ");
             }
@@ -2948,7 +2947,10 @@ impl<'tcx> ty::TraitPredicate<'tcx> {
 }
 
 #[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)]
-pub struct TraitPredPrintWithBoundConstness<'tcx>(ty::TraitPredicate<'tcx>, ty::BoundConstness);
+pub struct TraitPredPrintWithBoundConstness<'tcx>(
+    ty::TraitPredicate<'tcx>,
+    Option<ty::BoundConstness>,
+);
 
 impl<'tcx> fmt::Debug for TraitPredPrintWithBoundConstness<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -2966,7 +2968,7 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> {
 
     fn print_with_bound_constness(
         self,
-        constness: ty::BoundConstness,
+        constness: Option<ty::BoundConstness>,
     ) -> ty::Binder<'tcx, TraitPredPrintWithBoundConstness<'tcx>> {
         self.map_bound(|trait_pred| TraitPredPrintWithBoundConstness(trait_pred, constness))
     }
@@ -3206,7 +3208,9 @@ define_print_and_forward_display! {
 
     TraitPredPrintWithBoundConstness<'tcx> {
         p!(print(self.0.trait_ref.self_ty()), ": ");
-        p!(pretty_print_bound_constness(self.1));
+        if let Some(constness) = self.1 {
+            p!(pretty_print_bound_constness(constness));
+        }
         if let ty::PredicatePolarity::Negative = self.0.polarity {
             p!("!");
         }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 3f00458d195..74de378c4d7 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1117,7 +1117,12 @@ impl<'tcx> Ty<'tcx> {
         // The way we evaluate the `N` in `[T; N]` here only works since we use
         // `simd_size_and_type` post-monomorphization. It will probably start to ICE
         // if we use it in generic code. See the `simd-array-trait` ui test.
-        (f0_len.eval_target_usize(tcx, ParamEnv::empty()), *f0_elem_ty)
+        (
+            f0_len
+                .try_to_target_usize(tcx)
+                .expect("expected SIMD field to have definite array size"),
+            *f0_elem_ty,
+        )
     }
 
     #[inline]
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 0a917120b3b..9a3049f3be6 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -907,24 +907,6 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    /// Constructs generic args for an item, optionally appending a const effect param type
-    pub fn with_opt_host_effect_param(
-        self,
-        caller_def_id: LocalDefId,
-        callee_def_id: DefId,
-        args: impl IntoIterator<Item: Into<ty::GenericArg<'tcx>>>,
-    ) -> ty::GenericArgsRef<'tcx> {
-        let generics = self.generics_of(callee_def_id);
-        assert_eq!(generics.parent, None);
-
-        let opt_const_param = generics
-            .host_effect_index
-            .is_some()
-            .then(|| ty::GenericArg::from(self.expected_host_effect_param_for_body(caller_def_id)));
-
-        self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param))
-    }
-
     /// Expand any [weak alias types][weak] contained within the given `value`.
     ///
     /// This should be used over other normalization routines in situations where
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index 020c202f965..7170aebee7d 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -454,12 +454,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         };
 
         let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span));
-        let method = trait_method(
-            self.tcx,
-            eq_def_id,
-            sym::eq,
-            self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [compare_ty, compare_ty]),
-        );
+        let method = trait_method(self.tcx, eq_def_id, sym::eq, [compare_ty, compare_ty]);
 
         let bool_ty = self.tcx.types.bool;
         let eq_result = self.temp(bool_ty, source_info.span);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 2c3611afca9..0dfa9168f7c 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -43,7 +43,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 }
 
 struct ConstToPat<'tcx> {
-    id: hir::HirId,
     span: Span,
     param_env: ty::ParamEnv<'tcx>,
 
@@ -62,7 +61,6 @@ impl<'tcx> ConstToPat<'tcx> {
     ) -> Self {
         trace!(?pat_ctxt.typeck_results.hir_owner);
         ConstToPat {
-            id,
             span,
             infcx,
             param_env: pat_ctxt.param_env,
@@ -149,15 +147,7 @@ impl<'tcx> ConstToPat<'tcx> {
             tcx,
             ObligationCause::dummy(),
             self.param_env,
-            ty::TraitRef::new_from_args(
-                tcx,
-                partial_eq_trait_id,
-                tcx.with_opt_host_effect_param(
-                    tcx.hir().enclosing_body_owner(self.id),
-                    partial_eq_trait_id,
-                    [ty, ty],
-                ),
-            ),
+            ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
         );
 
         // This *could* accept a type that isn't actually `PartialEq`, because region bounds get
diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
index a3b117a3f19..7f2a07e2f5e 100644
--- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
@@ -863,7 +863,7 @@ where
             ty::Adt(def, args) => self.open_drop_for_adt(*def, args),
             ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind),
             ty::Array(ety, size) => {
-                let size = size.try_eval_target_usize(self.tcx(), self.elaborator.param_env());
+                let size = size.try_to_target_usize(self.tcx());
                 self.open_drop_for_array(*ety, size)
             }
             ty::Slice(ety) => self.drop_loop_pair(*ety),
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 162245cb950..fd8e403ebc2 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -18,18 +18,12 @@ struct MoveDataBuilder<'a, 'tcx, F> {
     body: &'a Body<'tcx>,
     loc: Location,
     tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
     data: MoveData<'tcx>,
     filter: F,
 }
 
 impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
-    fn new(
-        body: &'a Body<'tcx>,
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        filter: F,
-    ) -> Self {
+    fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, filter: F) -> Self {
         let mut move_paths = IndexVec::new();
         let mut path_map = IndexVec::new();
         let mut init_path_map = IndexVec::new();
@@ -59,7 +53,6 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
             body,
             loc: Location::START,
             tcx,
-            param_env,
             data: MoveData {
                 moves: IndexVec::new(),
                 loc_map: LocationMap::new(body),
@@ -308,10 +301,9 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
 pub(super) fn gather_moves<'tcx>(
     body: &Body<'tcx>,
     tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
     filter: impl Fn(Ty<'tcx>) -> bool,
 ) -> MoveData<'tcx> {
-    let mut builder = MoveDataBuilder::new(body, tcx, param_env, filter);
+    let mut builder = MoveDataBuilder::new(body, tcx, filter);
 
     builder.gather_args();
 
@@ -550,7 +542,9 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
             };
             let base_ty = base_place.ty(self.body, self.tcx).ty;
             let len: u64 = match base_ty.kind() {
-                ty::Array(_, size) => size.eval_target_usize(self.tcx, self.param_env),
+                ty::Array(_, size) => size
+                    .try_to_target_usize(self.tcx)
+                    .expect("expected subslice projection on fixed-size array"),
                 _ => bug!("from_end: false slice pattern of non-array type"),
             };
             for offset in from..to {
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
index bc1177976b5..926bd187431 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
@@ -4,7 +4,7 @@ use std::ops::{Index, IndexMut};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::mir::*;
-use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
+use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_span::Span;
 use smallvec::SmallVec;
 
@@ -352,10 +352,9 @@ impl<'tcx> MoveData<'tcx> {
     pub fn gather_moves(
         body: &Body<'tcx>,
         tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
         filter: impl Fn(Ty<'tcx>) -> bool,
     ) -> MoveData<'tcx> {
-        builder::gather_moves(body, tcx, param_env, filter)
+        builder::gather_moves(body, tcx, filter)
     }
 
     /// For the move path `mpi`, returns the root local variable that starts the path.
diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
index 75732b19cd0..5727517bd61 100644
--- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs
+++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
@@ -40,8 +40,7 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
         debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
     }
 
-    let param_env = tcx.param_env(def_id);
-    let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true);
+    let move_data = MoveData::gather_moves(body, tcx, |_| true);
 
     if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
         let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 8f032728f6b..0a413d68020 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -1957,7 +1957,8 @@ fn check_must_not_suspend_ty<'tcx>(
             let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix);
             check_must_not_suspend_ty(tcx, ty, hir_id, param_env, SuspendCheckData {
                 descr_pre,
-                plural_len: len.try_eval_target_usize(tcx, param_env).unwrap_or(0) as usize + 1,
+                // FIXME(must_not_suspend): This is wrong. We should handle printing unevaluated consts.
+                plural_len: len.try_to_target_usize(tcx).unwrap_or(0) as usize + 1,
                 ..data
             })
         }
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index f4ac4d9fee6..30e1ac05e03 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -58,8 +58,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops {
         let param_env = tcx.param_env_reveal_all_normalized(def_id);
         // For types that do not need dropping, the behaviour is trivial. So we only need to track
         // init/uninit for types that do need dropping.
-        let move_data =
-            MoveData::gather_moves(body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env));
+        let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, param_env));
         let elaborate_patch = {
             let env = MoveDataParamEnv { move_data, param_env };
 
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 2f97c408f2a..08923748eb2 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -602,7 +602,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             Len(place) => {
                 let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind()
                 {
-                    n.try_eval_target_usize(self.tcx, self.param_env)?
+                    n.try_to_target_usize(self.tcx)?
                 } else {
                     match self.get_const(place)? {
                         Value::Immediate(src) => src.len(&self.ecx).discard_err()?,
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index d963ca5c485..86c4b241a2b 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -329,7 +329,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                     // Determine the type of the thing we are indexing.
                     && let ty::Array(_, len) = place_base.ty(self.body, self.tcx).ty.kind()
                     // It's an array; determine its length.
-                    && let Some(len) = len.try_eval_target_usize(self.tcx, self.param_env)
+                    && let Some(len) = len.try_to_target_usize(self.tcx)
                     // If the index is in-bounds, go ahead.
                     && idx < len
                 {
@@ -407,7 +407,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                 // mutably without consequences. However, only &mut []
                 // is allowed right now.
                 if let ty::Array(_, len) = ty.kind() {
-                    match len.try_eval_target_usize(self.tcx, self.param_env) {
+                    match len.try_to_target_usize(self.tcx) {
                         Some(0) => {}
                         _ => return Err(Unpromotable),
                     }
diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
index e6647edf3f5..09969a4c7cc 100644
--- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
@@ -19,8 +19,7 @@ pub(super) struct RemoveUninitDrops;
 impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         let param_env = tcx.param_env(body.source.def_id());
-        let move_data =
-            MoveData::gather_moves(body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env));
+        let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, param_env));
 
         let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
             .into_engine(tcx, body)
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index f49f3a1a3bf..71ce0cce772 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -16,7 +16,7 @@ use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::relate::solver_relating::RelateExt;
 use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner};
-use tracing::{instrument, trace};
+use tracing::{debug, instrument, trace};
 
 use crate::canonicalizer::{CanonicalizeMode, Canonicalizer};
 use crate::delegate::SolverDelegate;
@@ -165,12 +165,22 @@ where
         // HACK: We bail with overflow if the response would have too many non-region
         // inference variables. This tends to only happen if we encounter a lot of
         // ambiguous alias types which get replaced with fresh inference variables
-        // during generalization. This prevents a hang in nalgebra.
-        let num_non_region_vars = canonical.variables.iter().filter(|c| !c.is_region()).count();
-        if num_non_region_vars > self.cx().recursion_limit() {
-            return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
-                suggest_increasing_limit: true,
-            }));
+        // during generalization. This prevents hangs caused by an exponential blowup,
+        // see tests/ui/traits/next-solver/coherence-alias-hang.rs.
+        //
+        // We don't do so for `NormalizesTo` goals as we erased the expected term and
+        // bailing with overflow here would prevent us from detecting a type-mismatch,
+        // causing a coherence error in diesel, see #131969. We still bail with verflow
+        // when later returning from the parent AliasRelate goal.
+        if !self.is_normalizes_to_goal {
+            let num_non_region_vars =
+                canonical.variables.iter().filter(|c| !c.is_region() && c.is_existential()).count();
+            if num_non_region_vars > self.cx().recursion_limit() {
+                debug!(?num_non_region_vars, "too many inference variables -> overflow");
+                return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
+                    suggest_increasing_limit: true,
+                }));
+            }
         }
 
         Ok(canonical)
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index ff91fa13fd0..a475a58e483 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -295,6 +295,37 @@ where
             Ok(ty)
         }
     }
+
+    /// Normalize a const for when it is structurally matched on, or more likely
+    /// when it needs `.try_to_*` called on it (e.g. to turn it into a usize).
+    ///
+    /// This function is necessary in nearly all cases before matching on a const.
+    /// Not doing so is likely to be incomplete and therefore unsound during
+    /// coherence.
+    #[instrument(level = "trace", skip(self, param_env), ret)]
+    fn structurally_normalize_const(
+        &mut self,
+        param_env: I::ParamEnv,
+        ct: I::Const,
+    ) -> Result<I::Const, NoSolution> {
+        if let ty::ConstKind::Unevaluated(..) = ct.kind() {
+            let normalized_ct = self.next_const_infer();
+            let alias_relate_goal = Goal::new(
+                self.cx(),
+                param_env,
+                ty::PredicateKind::AliasRelate(
+                    ct.into(),
+                    normalized_ct.into(),
+                    ty::AliasRelationDirection::Equate,
+                ),
+            );
+            self.add_goal(GoalSource::Misc, alias_relate_goal);
+            self.try_evaluate_added_goals()?;
+            Ok(self.resolve_vars_if_possible(normalized_ct))
+        } else {
+            Ok(ct)
+        }
+    }
 }
 
 fn response_no_constraints_raw<I: Interner>(
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 2cbed0bceb2..a8d6536baad 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -627,11 +627,16 @@ where
         }
 
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
+            let assume = ecx.structurally_normalize_const(
+                goal.param_env,
+                goal.predicate.trait_ref.args.const_at(2),
+            )?;
+
             let certainty = ecx.is_transmutable(
                 goal.param_env,
                 goal.predicate.trait_ref.args.type_at(0),
                 goal.predicate.trait_ref.args.type_at(1),
-                goal.predicate.trait_ref.args.const_at(2),
+                assume,
             )?;
             ecx.evaluate_added_goals_and_make_canonical_response(certainty)
         })
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index a4c3d789176..27714a0fdcc 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -122,7 +122,9 @@ impl<'k> StatCollector<'k> {
         // We will soon sort, so the initial order does not matter.
         #[allow(rustc::potential_query_instability)]
         let mut nodes: Vec<_> = self.nodes.iter().collect();
-        nodes.sort_by_key(|(_, node)| node.stats.count * node.stats.size);
+        nodes.sort_by_cached_key(|(label, node)| {
+            (node.stats.count * node.stats.size, label.to_owned())
+        });
 
         let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum();
 
diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml
index 0cb47e03441..34fb1bdf6fa 100644
--- a/compiler/rustc_pattern_analysis/Cargo.toml
+++ b/compiler/rustc_pattern_analysis/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-rustc-hash = "1.1.0"
+rustc-hash = "2.0.0"
 rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena", optional = true }
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 0145ffd3a4b..0ca6bb8c07d 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1193,7 +1193,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
         if !ident.as_str().starts_with('_') {
             self.r.unused_macros.insert(def_id, (node_id, ident));
             for (rule_i, rule_span) in &self.r.macro_map[&def_id.to_def_id()].rule_spans {
-                self.r.unused_macro_rules.insert((def_id, *rule_i), (ident, *rule_span));
+                self.r
+                    .unused_macro_rules
+                    .entry(def_id)
+                    .or_default()
+                    .insert(*rule_i, (ident, *rule_span));
             }
         }
     }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 24a0c252e55..35d491cfc18 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1122,7 +1122,8 @@ pub struct Resolver<'ra, 'tcx> {
     local_macro_def_scopes: FxHashMap<LocalDefId, Module<'ra>>,
     ast_transform_scopes: FxHashMap<LocalExpnId, Module<'ra>>,
     unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>,
-    unused_macro_rules: FxHashMap<(LocalDefId, usize), (Ident, Span)>,
+    /// A map from the macro to all its potentially unused arms.
+    unused_macro_rules: FxIndexMap<LocalDefId, FxHashMap<usize, (Ident, Span)>>,
     proc_macro_stubs: FxHashSet<LocalDefId>,
     /// Traces collected during macro resolution and validated when it's complete.
     single_segment_macro_resolutions:
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index fa2be8216c7..e43c8718665 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -340,7 +340,9 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
 
     fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) {
         let did = self.local_def_id(id);
-        self.unused_macro_rules.remove(&(did, rule_i));
+        if let Some(rules) = self.unused_macro_rules.get_mut(&did) {
+            rules.remove(&rule_i);
+        }
     }
 
     fn check_unused_macros(&mut self) {
@@ -352,18 +354,24 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
                 BuiltinLintDiag::UnusedMacroDefinition(ident.name),
             );
         }
-        for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() {
-            if self.unused_macros.contains_key(&def_id) {
-                // We already lint the entire macro as unused
-                continue;
+
+        for (&def_id, unused_arms) in self.unused_macro_rules.iter() {
+            let mut unused_arms = unused_arms.iter().collect::<Vec<_>>();
+            unused_arms.sort_by_key(|&(&arm_i, _)| arm_i);
+
+            for (&arm_i, &(ident, rule_span)) in unused_arms {
+                if self.unused_macros.contains_key(&def_id) {
+                    // We already lint the entire macro as unused
+                    continue;
+                }
+                let node_id = self.def_id_to_node_id[def_id];
+                self.lint_buffer.buffer_lint(
+                    UNUSED_MACRO_RULES,
+                    node_id,
+                    rule_span,
+                    BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name),
+                );
             }
-            let node_id = self.def_id_to_node_id[def_id];
-            self.lint_buffer.buffer_lint(
-                UNUSED_MACRO_RULES,
-                node_id,
-                rule_span,
-                BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name),
-            );
         }
     }
 
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
index 53834198f63..ca75952fe3d 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
@@ -133,7 +133,9 @@ fn encode_const<'tcx>(
             // bool value false is encoded as 0 and true as 1.
             match ct_ty.kind() {
                 ty::Int(ity) => {
-                    let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all());
+                    let bits = c
+                        .try_to_bits(tcx, ty::ParamEnv::reveal_all())
+                        .expect("expected monomorphic const in cfi");
                     let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
                     if val < 0 {
                         s.push('n');
@@ -141,7 +143,9 @@ fn encode_const<'tcx>(
                     let _ = write!(s, "{val}");
                 }
                 ty::Uint(_) => {
-                    let val = c.eval_bits(tcx, ty::ParamEnv::reveal_all());
+                    let val = c
+                        .try_to_bits(tcx, ty::ParamEnv::reveal_all())
+                        .expect("expected monomorphic const in cfi");
                     let _ = write!(s, "{val}");
                 }
                 ty::Bool => {
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index ed5d2d1e799..8fa8f2ac402 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -406,7 +406,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let tcx = tables.tcx;
         let mir_const = cnst.internal(&mut *tables, tcx);
         mir_const
-            .try_eval_target_usize(tables.tcx, ParamEnv::empty())
+            .try_to_target_usize(tables.tcx)
             .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64")))
     }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 6f62b4f82d7..3ab482072b8 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1913,7 +1913,7 @@ symbols! {
         str_trim,
         str_trim_end,
         str_trim_start,
-        strict_provenance,
+        strict_provenance_lints,
         string_as_mut_str,
         string_as_str,
         string_deref_patterns,
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index f3da7ff1ca7..d092fa8f082 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -555,7 +555,6 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
 
     fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
         // We only mangle a typed value if the const can be evaluated.
-        let ct = ct.normalize(self.tcx, ty::ParamEnv::reveal_all());
         let (ct_ty, valtree) = match ct.kind() {
             ty::ConstKind::Value(ty, val) => (ty, val),
 
@@ -592,7 +591,9 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
             ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => {
                 ct_ty.print(self)?;
 
-                let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all());
+                let mut bits = ct
+                    .try_to_bits(self.tcx, ty::ParamEnv::reveal_all())
+                    .expect("expected const to be monomorphic");
 
                 // Negative integer values are mangled using `n` as a "sign prefix".
                 if let ty::Int(ity) = ct_ty.kind() {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 44373ca4866..549beafd196 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -290,7 +290,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         }
 
                         if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop)
-                            && matches!(predicate_constness, ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const)
+                            && matches!(predicate_constness, Some(ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const))
                         {
                             err.note("`~const Drop` was renamed to `~const Destruct`");
                             err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details");
@@ -2192,7 +2192,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         &self,
         trait_predicate: ty::PolyTraitPredicate<'tcx>,
         message: Option<String>,
-        predicate_constness: ty::BoundConstness,
+        predicate_constness: Option<ty::BoundConstness>,
         append_const_msg: Option<AppendConstMessage>,
         post_message: String,
     ) -> String {
@@ -2200,19 +2200,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             .and_then(|cannot_do_this| {
                 match (predicate_constness, append_const_msg) {
                     // do nothing if predicate is not const
-                    (ty::BoundConstness::NotConst, _) => Some(cannot_do_this),
+                    (None, _) => Some(cannot_do_this),
                     // suggested using default post message
                     (
-                        ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst,
+                        Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst),
                         Some(AppendConstMessage::Default),
                     ) => Some(format!("{cannot_do_this} in const contexts")),
                     // overridden post message
                     (
-                        ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst,
+                        Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst),
                         Some(AppendConstMessage::Custom(custom_msg, _)),
                     ) => Some(format!("{cannot_do_this}{custom_msg}")),
                     // fallback to generic message
-                    (ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, None) => None,
+                    (Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), None) => {
+                        None
+                    }
                 }
             })
             .unwrap_or_else(|| {
@@ -2230,124 +2232,143 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         span: Span,
     ) -> GetSafeTransmuteErrorAndReason {
         use rustc_transmute::Answer;
+        self.probe(|_| {
+            // We don't assemble a transmutability candidate for types that are generic
+            // and we should have ambiguity for types that still have non-region infer.
+            if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() {
+                return GetSafeTransmuteErrorAndReason::Default;
+            }
 
-        // We don't assemble a transmutability candidate for types that are generic
-        // and we should have ambiguity for types that still have non-region infer.
-        if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() {
-            return GetSafeTransmuteErrorAndReason::Default;
-        }
+            // Erase regions because layout code doesn't particularly care about regions.
+            let trait_ref =
+                self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
 
-        // Erase regions because layout code doesn't particularly care about regions.
-        let trait_ref =
-            self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
+            let src_and_dst = rustc_transmute::Types {
+                dst: trait_ref.args.type_at(0),
+                src: trait_ref.args.type_at(1),
+            };
 
-        let src_and_dst = rustc_transmute::Types {
-            dst: trait_ref.args.type_at(0),
-            src: trait_ref.args.type_at(1),
-        };
-        let Some(assume) = rustc_transmute::Assume::from_const(
-            self.infcx.tcx,
-            obligation.param_env,
-            trait_ref.args.const_at(2),
-        ) else {
-            self.dcx().span_delayed_bug(
-                span,
-                "Unable to construct rustc_transmute::Assume where it was previously possible",
-            );
-            return GetSafeTransmuteErrorAndReason::Silent;
-        };
+            let ocx = ObligationCtxt::new(self);
+            let Ok(assume) = ocx.structurally_normalize_const(
+                &obligation.cause,
+                obligation.param_env,
+                trait_ref.args.const_at(2),
+            ) else {
+                self.dcx().span_delayed_bug(
+                    span,
+                    "Unable to construct rustc_transmute::Assume where it was previously possible",
+                );
+                return GetSafeTransmuteErrorAndReason::Silent;
+            };
 
-        let dst = trait_ref.args.type_at(0);
-        let src = trait_ref.args.type_at(1);
+            let Some(assume) =
+                rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume)
+            else {
+                self.dcx().span_delayed_bug(
+                    span,
+                    "Unable to construct rustc_transmute::Assume where it was previously possible",
+                );
+                return GetSafeTransmuteErrorAndReason::Silent;
+            };
 
-        let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
+            let dst = trait_ref.args.type_at(0);
+            let src = trait_ref.args.type_at(1);
+            let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
 
-        match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
-            obligation.cause,
-            src_and_dst,
-            assume,
-        ) {
-            Answer::No(reason) => {
-                let safe_transmute_explanation = match reason {
-                    rustc_transmute::Reason::SrcIsNotYetSupported => {
-                        format!("analyzing the transmutability of `{src}` is not yet supported")
-                    }
+            match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
+                obligation.cause,
+                src_and_dst,
+                assume,
+            ) {
+                Answer::No(reason) => {
+                    let safe_transmute_explanation = match reason {
+                        rustc_transmute::Reason::SrcIsNotYetSupported => {
+                            format!("analyzing the transmutability of `{src}` is not yet supported")
+                        }
 
-                    rustc_transmute::Reason::DstIsNotYetSupported => {
-                        format!("analyzing the transmutability of `{dst}` is not yet supported")
-                    }
+                        rustc_transmute::Reason::DstIsNotYetSupported => {
+                            format!("analyzing the transmutability of `{dst}` is not yet supported")
+                        }
 
-                    rustc_transmute::Reason::DstIsBitIncompatible => {
-                        format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`")
-                    }
+                        rustc_transmute::Reason::DstIsBitIncompatible => {
+                            format!(
+                                "at least one value of `{src}` isn't a bit-valid value of `{dst}`"
+                            )
+                        }
 
-                    rustc_transmute::Reason::DstUninhabited => {
-                        format!("`{dst}` is uninhabited")
-                    }
+                        rustc_transmute::Reason::DstUninhabited => {
+                            format!("`{dst}` is uninhabited")
+                        }
 
-                    rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
-                        format!("`{dst}` may carry safety invariants")
-                    }
-                    rustc_transmute::Reason::DstIsTooBig => {
-                        format!("the size of `{src}` is smaller than the size of `{dst}`")
-                    }
-                    rustc_transmute::Reason::DstRefIsTooBig { src, dst } => {
-                        let src_size = src.size;
-                        let dst_size = dst.size;
-                        format!(
-                            "the referent size of `{src}` ({src_size} bytes) is smaller than that of `{dst}` ({dst_size} bytes)"
-                        )
-                    }
-                    rustc_transmute::Reason::SrcSizeOverflow => {
-                        format!(
-                            "values of the type `{src}` are too big for the target architecture"
-                        )
-                    }
-                    rustc_transmute::Reason::DstSizeOverflow => {
-                        format!(
-                            "values of the type `{dst}` are too big for the target architecture"
-                        )
-                    }
-                    rustc_transmute::Reason::DstHasStricterAlignment {
-                        src_min_align,
-                        dst_min_align,
-                    } => {
-                        format!(
-                            "the minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})"
-                        )
-                    }
-                    rustc_transmute::Reason::DstIsMoreUnique => {
-                        format!("`{src}` is a shared reference, but `{dst}` is a unique reference")
-                    }
-                    // Already reported by rustc
-                    rustc_transmute::Reason::TypeError => {
-                        return GetSafeTransmuteErrorAndReason::Silent;
-                    }
-                    rustc_transmute::Reason::SrcLayoutUnknown => {
-                        format!("`{src}` has an unknown layout")
-                    }
-                    rustc_transmute::Reason::DstLayoutUnknown => {
-                        format!("`{dst}` has an unknown layout")
+                        rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
+                            format!("`{dst}` may carry safety invariants")
+                        }
+                        rustc_transmute::Reason::DstIsTooBig => {
+                            format!("the size of `{src}` is smaller than the size of `{dst}`")
+                        }
+                        rustc_transmute::Reason::DstRefIsTooBig { src, dst } => {
+                            let src_size = src.size;
+                            let dst_size = dst.size;
+                            format!(
+                                "the referent size of `{src}` ({src_size} bytes) \
+                        is smaller than that of `{dst}` ({dst_size} bytes)"
+                            )
+                        }
+                        rustc_transmute::Reason::SrcSizeOverflow => {
+                            format!(
+                                "values of the type `{src}` are too big for the target architecture"
+                            )
+                        }
+                        rustc_transmute::Reason::DstSizeOverflow => {
+                            format!(
+                                "values of the type `{dst}` are too big for the target architecture"
+                            )
+                        }
+                        rustc_transmute::Reason::DstHasStricterAlignment {
+                            src_min_align,
+                            dst_min_align,
+                        } => {
+                            format!(
+                                "the minimum alignment of `{src}` ({src_min_align}) should \
+                        be greater than that of `{dst}` ({dst_min_align})"
+                            )
+                        }
+                        rustc_transmute::Reason::DstIsMoreUnique => {
+                            format!(
+                                "`{src}` is a shared reference, but `{dst}` is a unique reference"
+                            )
+                        }
+                        // Already reported by rustc
+                        rustc_transmute::Reason::TypeError => {
+                            return GetSafeTransmuteErrorAndReason::Silent;
+                        }
+                        rustc_transmute::Reason::SrcLayoutUnknown => {
+                            format!("`{src}` has an unknown layout")
+                        }
+                        rustc_transmute::Reason::DstLayoutUnknown => {
+                            format!("`{dst}` has an unknown layout")
+                        }
+                    };
+                    GetSafeTransmuteErrorAndReason::Error {
+                        err_msg,
+                        safe_transmute_explanation: Some(safe_transmute_explanation),
                     }
-                };
-                GetSafeTransmuteErrorAndReason::Error {
-                    err_msg,
-                    safe_transmute_explanation: Some(safe_transmute_explanation),
                 }
+                // Should never get a Yes at this point! We already ran it before, and did not get a Yes.
+                Answer::Yes => span_bug!(
+                    span,
+                    "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
+                ),
+                // Reached when a different obligation (namely `Freeze`) causes the
+                // transmutability analysis to fail. In this case, silence the
+                // transmutability error message in favor of that more specific
+                // error.
+                Answer::If(_) => GetSafeTransmuteErrorAndReason::Error {
+                    err_msg,
+                    safe_transmute_explanation: None,
+                },
             }
-            // Should never get a Yes at this point! We already ran it before, and did not get a Yes.
-            Answer::Yes => span_bug!(
-                span,
-                "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
-            ),
-            // Reached when a different obligation (namely `Freeze`) causes the
-            // transmutability analysis to fail. In this case, silence the
-            // transmutability error message in favor of that more specific
-            // error.
-            Answer::If(_) => {
-                GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None }
-            }
-        }
+        })
     }
 
     /// For effects predicates such as `<u32 as Add>::Effects: Compat<host>`, pretend that the
@@ -2358,26 +2379,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         p: ty::PolyTraitPredicate<'tcx>,
         leaf: ty::PolyTraitPredicate<'tcx>,
         span: Span,
-    ) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, ty::BoundConstness) {
+    ) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, Option<ty::BoundConstness>)
+    {
         let trait_ref = p.to_poly_trait_ref();
         if !self.tcx.is_lang_item(trait_ref.def_id(), LangItem::EffectsCompat) {
-            return (p, leaf, ty::BoundConstness::NotConst);
+            return (p, leaf, None);
         }
 
         let Some(ty::Alias(ty::AliasTyKind::Projection, projection)) =
             trait_ref.self_ty().no_bound_vars().map(Ty::kind)
         else {
-            return (p, leaf, ty::BoundConstness::NotConst);
+            return (p, leaf, None);
         };
 
         let constness = trait_ref.skip_binder().args.const_at(1);
 
         let constness = if constness == self.tcx.consts.true_ || constness.is_ct_infer() {
-            ty::BoundConstness::NotConst
+            None
         } else if constness == self.tcx.consts.false_ {
-            ty::BoundConstness::Const
+            Some(ty::BoundConstness::Const)
         } else if matches!(constness.kind(), ty::ConstKind::Param(_)) {
-            ty::BoundConstness::ConstIfConst
+            Some(ty::BoundConstness::ConstIfConst)
         } else {
             self.dcx().span_bug(span, format!("Unknown constness argument: {constness:?}"));
         };
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index cd41ab9fa6c..7f42c932fcf 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -231,7 +231,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
             if let ty::Array(aty, len) = self_ty.kind() {
                 flags.push((sym::_Self, Some("[]".to_string())));
-                let len = len.try_to_valtree().and_then(|v| v.try_to_target_usize(self.tcx));
+                let len = len.try_to_target_usize(self.tcx);
                 flags.push((sym::_Self, Some(format!("[{aty}; _]"))));
                 if let Some(n) = len {
                     flags.push((sym::_Self, Some(format!("[{aty}; {n}]"))));
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 733baaa99e5..677905a3085 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3701,12 +3701,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         }
                         _ => None,
                     };
-                    // Also add host param, if present
-                    let host = self.tcx.generics_of(trait_pred.def_id()).host_effect_index.map(|idx| trait_pred.skip_binder().trait_ref.args[idx]);
                     let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
                         trait_ref: ty::TraitRef::new(self.tcx,
                             trait_pred.def_id(),
-                            [field_ty].into_iter().chain(trait_args).chain(host),
+                            [field_ty].into_iter().chain(trait_args),
                         ),
                         ..*tr
                     });
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index f4a2483cebf..f8fb297e36c 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -298,6 +298,7 @@ fn equate_impl_headers<'tcx>(
 }
 
 /// The result of [fn impl_intersection_has_impossible_obligation].
+#[derive(Debug)]
 enum IntersectionHasImpossibleObligations<'tcx> {
     Yes,
     No {
@@ -328,6 +329,7 @@ enum IntersectionHasImpossibleObligations<'tcx> {
 /// of the two impls above to be empty.
 ///
 /// Importantly, this works even if there isn't a `impl !Error for MyLocalType`.
+#[instrument(level = "debug", skip(selcx), ret)]
 fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligations: &'a [PredicateObligation<'tcx>],
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index a45ec8b3c44..5e270b62b00 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -329,4 +329,15 @@ where
             .at(cause, param_env)
             .structurally_normalize(value, &mut **self.engine.borrow_mut())
     }
+
+    pub fn structurally_normalize_const(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        value: ty::Const<'tcx>,
+    ) -> Result<ty::Const<'tcx>, Vec<E>> {
+        self.infcx
+            .at(cause, param_env)
+            .structurally_normalize_const(value, &mut **self.engine.borrow_mut())
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 0fb795fc184..cffb9b59841 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -368,7 +368,7 @@ pub fn normalize_param_env_or_error<'tcx>(
                     // should actually be okay since without `feature(generic_const_exprs)` the only
                     // const arguments that have a non-empty param env are array repeat counts. These
                     // do not appear in the type system though.
-                    c.normalize(self.0, ty::ParamEnv::empty())
+                    c.normalize_internal(self.0, ty::ParamEnv::empty())
                 }
             }
 
@@ -562,11 +562,20 @@ fn is_impossible_associated_item(
 
     let generics = tcx.generics_of(trait_item_def_id);
     let predicates = tcx.predicates_of(trait_item_def_id);
+
+    // Be conservative in cases where we have `W<T: ?Sized>` and a method like `Self: Sized`,
+    // since that method *may* have some substitutions where the predicates hold.
+    //
+    // This replicates the logic we use in coherence.
+    let infcx =
+        tcx.infer_ctxt().ignoring_regions().with_next_trait_solver(true).intercrate(true).build();
+    let param_env = ty::ParamEnv::empty();
+    let fresh_args = infcx.fresh_args_for_item(tcx.def_span(impl_def_id), impl_def_id);
+
     let impl_trait_ref = tcx
         .impl_trait_ref(impl_def_id)
         .expect("expected impl to correspond to trait")
-        .instantiate_identity();
-    let param_env = tcx.param_env(impl_def_id);
+        .instantiate(tcx, fresh_args);
 
     let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id };
     let predicates_for_trait = predicates.predicates.iter().filter_map(|(pred, span)| {
@@ -580,16 +589,9 @@ fn is_impossible_associated_item(
         })
     });
 
-    let infcx = tcx.infer_ctxt().ignoring_regions().build();
-    for obligation in predicates_for_trait {
-        // Ignore overflow error, to be conservative.
-        if let Ok(result) = infcx.evaluate_obligation(&obligation)
-            && !result.may_apply()
-        {
-            return true;
-        }
-    }
-    false
+    let ocx = ObligationCtxt::new(&infcx);
+    ocx.register_obligations(predicates_for_trait);
+    !ocx.select_where_possible().is_empty()
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index 7eac3559348..d246d37f748 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -413,7 +413,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
                 self.selcx.infcx,
                 &mut self.universes,
                 constant,
-                |constant| constant.normalize(tcx, self.param_env),
+                |constant| constant.normalize_internal(tcx, self.param_env),
             )
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 7f7c9bced18..cc634b65a0b 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1642,24 +1642,9 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
         sig,
     );
 
-    let host_effect_param = match *fn_type.kind() {
-        ty::FnDef(def_id, args) => tcx
-            .generics_of(def_id)
-            .host_effect_index
-            .map_or(tcx.consts.true_, |idx| args.const_at(idx)),
-        ty::FnPtr(..) => tcx.consts.true_,
-        _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"),
-    };
-
-    confirm_callable_candidate(
-        selcx,
-        obligation,
-        sig,
-        util::TupleArgumentsFlag::Yes,
-        host_effect_param,
-    )
-    .with_addl_obligations(nested)
-    .with_addl_obligations(obligations)
+    confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes)
+        .with_addl_obligations(nested)
+        .with_addl_obligations(obligations)
 }
 
 fn confirm_closure_candidate<'cx, 'tcx>(
@@ -1739,16 +1724,9 @@ fn confirm_closure_candidate<'cx, 'tcx>(
 
     debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate");
 
-    confirm_callable_candidate(
-        selcx,
-        obligation,
-        closure_sig,
-        util::TupleArgumentsFlag::No,
-        // FIXME(effects): This doesn't handle const closures correctly!
-        selcx.tcx().consts.true_,
-    )
-    .with_addl_obligations(nested)
-    .with_addl_obligations(obligations)
+    confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No)
+        .with_addl_obligations(nested)
+        .with_addl_obligations(obligations)
 }
 
 fn confirm_callable_candidate<'cx, 'tcx>(
@@ -1756,7 +1734,6 @@ fn confirm_callable_candidate<'cx, 'tcx>(
     obligation: &ProjectionTermObligation<'tcx>,
     fn_sig: ty::PolyFnSig<'tcx>,
     flag: util::TupleArgumentsFlag,
-    fn_host_effect: ty::Const<'tcx>,
 ) -> Progress<'tcx> {
     let tcx = selcx.tcx();
 
@@ -1771,7 +1748,6 @@ fn confirm_callable_candidate<'cx, 'tcx>(
         obligation.predicate.self_ty(),
         fn_sig,
         flag,
-        fn_host_effect,
     )
     .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate {
         projection_term: ty::AliasTerm::new_from_args(tcx, fn_once_output_def_id, trait_ref.args),
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 18412b844ff..01e6516302c 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -340,7 +340,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> {
             self.infcx,
             &mut self.universes,
             constant,
-            |constant| constant.normalize(self.infcx.tcx, self.param_env),
+            |constant| constant.normalize_internal(self.infcx.tcx, self.param_env),
         );
         debug!(?constant, ?self.param_env);
         constant.try_super_fold_with(self)
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index aa313a526c1..ac38c9e24a2 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -543,23 +543,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // Provide an impl, but only for suitable `fn` pointers.
             ty::FnPtr(sig_tys, hdr) => {
                 if sig_tys.with(hdr).is_fn_trait_compatible() {
-                    candidates
-                        .vec
-                        .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ });
+                    candidates.vec.push(FnPointerCandidate);
                 }
             }
             // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
-            ty::FnDef(def_id, args) => {
+            ty::FnDef(def_id, _args) => {
                 let tcx = self.tcx();
                 if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible()
                     && tcx.codegen_fn_attrs(def_id).target_features.is_empty()
                 {
-                    candidates.vec.push(FnPointerCandidate {
-                        fn_host_effect: tcx
-                            .generics_of(def_id)
-                            .host_effect_index
-                            .map_or(tcx.consts.true_, |idx| args.const_at(idx)),
-                    });
+                    candidates.vec.push(FnPointerCandidate);
                 }
             }
             _ => {}
@@ -1170,103 +1163,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
     fn assemble_const_destruct_candidates(
         &mut self,
-        obligation: &PolyTraitObligation<'tcx>,
+        _obligation: &PolyTraitObligation<'tcx>,
         candidates: &mut SelectionCandidateSet<'tcx>,
     ) {
-        // If the predicate is `~const Destruct` in a non-const environment, we don't actually need
-        // to check anything. We'll short-circuit checking any obligations in confirmation, too.
-        let Some(host_effect_index) =
-            self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index
-        else {
-            candidates.vec.push(BuiltinCandidate { has_nested: false });
-            return;
-        };
-        // If the obligation has `host = true`, then the obligation is non-const and it's always
-        // trivially implemented.
-        if obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index)
-            == self.tcx().consts.true_
-        {
-            candidates.vec.push(BuiltinCandidate { has_nested: false });
-            return;
-        }
-
-        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
-        match self_ty.kind() {
-            ty::Alias(..)
-            | ty::Dynamic(..)
-            | ty::Error(_)
-            | ty::Bound(..)
-            | ty::Param(_)
-            | ty::Placeholder(_) => {
-                // We don't know if these are `~const Destruct`, at least
-                // not structurally... so don't push a candidate.
-            }
-
-            ty::Bool
-            | ty::Char
-            | ty::Int(_)
-            | ty::Uint(_)
-            | ty::Float(_)
-            | ty::Infer(ty::IntVar(_))
-            | ty::Infer(ty::FloatVar(_))
-            | ty::Str
-            | ty::RawPtr(_, _)
-            | ty::Ref(..)
-            | ty::FnDef(..)
-            | ty::FnPtr(..)
-            | ty::Never
-            | ty::Foreign(_)
-            | ty::Array(..)
-            | ty::Pat(..)
-            | ty::Slice(_)
-            | ty::Closure(..)
-            | ty::CoroutineClosure(..)
-            | ty::Coroutine(..)
-            | ty::Tuple(_)
-            | ty::CoroutineWitness(..) => {
-                // These are built-in, and cannot have a custom `impl const Destruct`.
-                candidates.vec.push(ConstDestructCandidate(None));
-            }
-
-            ty::Adt(..) => {
-                let mut relevant_impl = None;
-                self.tcx().for_each_relevant_impl(
-                    self.tcx().require_lang_item(LangItem::Drop, None),
-                    obligation.predicate.skip_binder().trait_ref.self_ty(),
-                    |impl_def_id| {
-                        if let Some(old_impl_def_id) = relevant_impl {
-                            self.tcx()
-                                .dcx()
-                                .struct_span_err(
-                                    self.tcx().def_span(impl_def_id),
-                                    "multiple drop impls found",
-                                )
-                                .with_span_note(
-                                    self.tcx().def_span(old_impl_def_id),
-                                    "other impl here",
-                                )
-                                .delay_as_bug();
-                        }
-
-                        relevant_impl = Some(impl_def_id);
-                    },
-                );
-
-                if let Some(impl_def_id) = relevant_impl {
-                    // Check that `impl Drop` is actually const, if there is a custom impl
-                    if self.tcx().constness(impl_def_id) == hir::Constness::Const {
-                        candidates.vec.push(ConstDestructCandidate(Some(impl_def_id)));
-                    }
-                } else {
-                    // Otherwise check the ADT like a built-in type (structurally)
-                    candidates.vec.push(ConstDestructCandidate(None));
-                }
-            }
-
-            ty::Infer(_) => {
-                candidates.ambiguous = true;
-            }
-        }
+        // FIXME(effects): Destruct is not const yet, and it is implemented
+        // by all types today in non-const setting.
+        candidates.vec.push(BuiltinCandidate { has_nested: false });
     }
 
     fn assemble_candidate_for_tuple(
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 0ba3b4e6e55..9a49fd7e77e 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -28,9 +28,9 @@ use super::{BuiltinImplConditions, PredicateObligations, SelectionContext};
 use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
 use crate::traits::util::{self, closure_trait_ref_and_return_type};
 use crate::traits::{
-    ImplDerivedCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation,
-    ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError,
-    SignatureMismatch, TraitDynIncompatible, TraitObligation, Unimplemented,
+    ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause,
+    PolyTraitObligation, PredicateObligation, Selection, SelectionError, SignatureMismatch,
+    TraitDynIncompatible, TraitObligation, Unimplemented,
 };
 
 impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
@@ -109,8 +109,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
             }
 
-            FnPointerCandidate { fn_host_effect } => {
-                let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?;
+            FnPointerCandidate => {
+                let data = self.confirm_fn_pointer_candidate(obligation)?;
                 ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
@@ -131,11 +131,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             TraitUpcastingUnsizeCandidate(idx) => {
                 self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?
             }
-
-            ConstDestructCandidate(def_id) => {
-                let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
-                ImplSource::Builtin(BuiltinImplSource::Misc, data)
-            }
         };
 
         // The obligations returned by confirmation are recursively evaluated
@@ -405,11 +400,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let predicate = obligation.predicate.skip_binder();
 
-        let Some(assume) = rustc_transmute::Assume::from_const(
-            self.infcx.tcx,
-            obligation.param_env,
-            predicate.trait_ref.args.const_at(2),
-        ) else {
+        let mut assume = predicate.trait_ref.args.const_at(2);
+        // FIXME(min_generic_const_exprs): We should shallowly normalize this.
+        if self.tcx().features().generic_const_exprs {
+            assume = assume.normalize_internal(self.tcx(), obligation.param_env);
+        }
+        let Some(assume) =
+            rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume)
+        else {
             return Err(Unimplemented);
         };
 
@@ -708,7 +706,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_fn_pointer_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-        fn_host_effect: ty::Const<'tcx>,
     ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         debug!(?obligation, "confirm_fn_pointer_candidate");
         let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
@@ -722,7 +719,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             self_ty,
             sig,
             util::TupleArgumentsFlag::Yes,
-            fn_host_effect,
         )
         .map_bound(|(trait_ref, _)| trait_ref);
 
@@ -904,11 +900,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
 
         let trait_ref = match *self_ty.kind() {
-            ty::Closure(..) => self.closure_trait_ref_unnormalized(
-                self_ty,
-                obligation.predicate.def_id(),
-                self.tcx().consts.true_,
-            ),
+            ty::Closure(..) => {
+                self.closure_trait_ref_unnormalized(self_ty, obligation.predicate.def_id())
+            }
             ty::CoroutineClosure(_, args) => {
                 args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
                     ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [
@@ -1341,170 +1335,4 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             _ => bug!("source: {source}, target: {target}"),
         })
     }
-
-    fn confirm_const_destruct_candidate(
-        &mut self,
-        obligation: &PolyTraitObligation<'tcx>,
-        impl_def_id: Option<DefId>,
-    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
-        let Some(host_effect_index) =
-            self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index
-        else {
-            bug!()
-        };
-        let host_effect_param: ty::GenericArg<'tcx> =
-            obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index).into();
-
-        let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None);
-
-        let tcx = self.tcx();
-        let self_ty = obligation.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty));
-
-        let mut nested = PredicateObligations::new();
-        let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
-
-        // If we have a custom `impl const Drop`, then
-        // first check it like a regular impl candidate.
-        // This is copied from confirm_impl_candidate but remaps the predicate to `~const Drop` beforehand.
-        if let Some(impl_def_id) = impl_def_id {
-            let mut new_obligation = obligation.clone();
-            new_obligation.predicate = new_obligation.predicate.map_bound(|mut trait_pred| {
-                trait_pred.trait_ref.def_id = drop_trait;
-                trait_pred
-            });
-            let args = self.rematch_impl(impl_def_id, &new_obligation);
-            debug!(?args, "impl args");
-
-            let cause = obligation.derived_cause(|derived| {
-                ObligationCauseCode::ImplDerived(Box::new(ImplDerivedCause {
-                    derived,
-                    impl_or_alias_def_id: impl_def_id,
-                    impl_def_predicate_index: None,
-                    span: obligation.cause.span,
-                }))
-            });
-            let obligations = ensure_sufficient_stack(|| {
-                self.vtable_impl(
-                    impl_def_id,
-                    args,
-                    &cause,
-                    new_obligation.recursion_depth + 1,
-                    new_obligation.param_env,
-                    obligation.predicate,
-                )
-            });
-            nested.extend(obligations.nested);
-        }
-
-        // We want to confirm the ADT's fields if we have an ADT
-        let mut stack = match *self_ty.skip_binder().kind() {
-            ty::Adt(def, args) => def.all_fields().map(|f| f.ty(tcx, args)).collect(),
-            _ => vec![self_ty.skip_binder()],
-        };
-
-        while let Some(nested_ty) = stack.pop() {
-            match *nested_ty.kind() {
-                // We know these types are trivially drop
-                ty::Bool
-                | ty::Char
-                | ty::Int(_)
-                | ty::Uint(_)
-                | ty::Float(_)
-                | ty::Infer(ty::IntVar(_))
-                | ty::Infer(ty::FloatVar(_))
-                | ty::Str
-                | ty::RawPtr(_, _)
-                | ty::Ref(..)
-                | ty::FnDef(..)
-                | ty::FnPtr(..)
-                | ty::Never
-                | ty::Foreign(_) => {}
-
-                // `ManuallyDrop` is trivially drop
-                ty::Adt(def, _) if def.is_manually_drop() => {}
-
-                // These types are built-in, so we can fast-track by registering
-                // nested predicates for their constituent type(s)
-                ty::Array(ty, _) | ty::Slice(ty) | ty::Pat(ty, _) => {
-                    stack.push(ty);
-                }
-                ty::Tuple(tys) => {
-                    stack.extend(tys.iter());
-                }
-                ty::Closure(_, args) => {
-                    stack.push(args.as_closure().tupled_upvars_ty());
-                }
-                ty::Coroutine(_, args) => {
-                    let coroutine = args.as_coroutine();
-                    stack.extend([coroutine.tupled_upvars_ty(), coroutine.witness()]);
-                }
-                ty::CoroutineWitness(def_id, args) => {
-                    let tcx = self.tcx();
-                    stack.extend(tcx.bound_coroutine_hidden_types(def_id).map(|bty| {
-                        self.infcx.enter_forall_and_leak_universe(bty.instantiate(tcx, args))
-                    }))
-                }
-
-                // If we have a projection type, make sure to normalize it so we replace it
-                // with a fresh infer variable
-                ty::Alias(ty::Projection | ty::Inherent, ..) => {
-                    let predicate = normalize_with_depth_to(
-                        self,
-                        obligation.param_env,
-                        cause.clone(),
-                        obligation.recursion_depth + 1,
-                        self_ty.rebind(ty::TraitPredicate {
-                            trait_ref: ty::TraitRef::new(
-                                self.tcx(),
-                                self.tcx().require_lang_item(LangItem::Destruct, Some(cause.span)),
-                                [nested_ty.into(), host_effect_param],
-                            ),
-                            polarity: ty::PredicatePolarity::Positive,
-                        }),
-                        &mut nested,
-                    );
-
-                    nested.push(Obligation::with_depth(
-                        tcx,
-                        cause.clone(),
-                        obligation.recursion_depth + 1,
-                        obligation.param_env,
-                        predicate,
-                    ));
-                }
-
-                // If we have any other type (e.g. an ADT), just register a nested obligation
-                // since it's either not `const Drop` (and we raise an error during selection),
-                // or it's an ADT (and we need to check for a custom impl during selection)
-                ty::Error(_)
-                | ty::Dynamic(..)
-                | ty::CoroutineClosure(..)
-                | ty::Param(_)
-                | ty::Bound(..)
-                | ty::Adt(..)
-                | ty::Alias(ty::Opaque | ty::Weak, _)
-                | ty::Infer(_)
-                | ty::Placeholder(_) => {
-                    let predicate = self_ty.rebind(ty::TraitPredicate {
-                        trait_ref: ty::TraitRef::new(
-                            self.tcx(),
-                            self.tcx().require_lang_item(LangItem::Destruct, Some(cause.span)),
-                            [nested_ty.into(), host_effect_param],
-                        ),
-                        polarity: ty::PredicatePolarity::Positive,
-                    });
-
-                    nested.push(Obligation::with_depth(
-                        tcx,
-                        cause.clone(),
-                        obligation.recursion_depth + 1,
-                        obligation.param_env,
-                        predicate,
-                    ));
-                }
-            }
-        }
-
-        Ok(nested)
-    }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 621babe9104..4757430a21c 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1831,12 +1831,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No,
 
             // (*)
-            (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => {
-                DropVictim::Yes
-            }
-            (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => {
-                DropVictim::No
-            }
+            (BuiltinCandidate { has_nested: false }, _) => DropVictim::Yes,
+            (_, BuiltinCandidate { has_nested: false }) => DropVictim::No,
 
             (ParamCandidate(other), ParamCandidate(victim)) => {
                 let same_except_bound_vars = other.skip_binder().trait_ref
@@ -1855,11 +1851,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 }
             }
 
-            // Drop otherwise equivalent non-const fn pointer candidates
-            (FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => {
-                DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_)
-            }
-
             (
                 ParamCandidate(other_cand),
                 ImplCandidate(..)
@@ -2766,7 +2757,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         &mut self,
         self_ty: Ty<'tcx>,
         fn_trait_def_id: DefId,
-        fn_host_effect: ty::Const<'tcx>,
     ) -> ty::PolyTraitRef<'tcx> {
         let ty::Closure(_, args) = *self_ty.kind() else {
             bug!("expected closure, found {self_ty}");
@@ -2779,7 +2769,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             self_ty,
             closure_sig,
             util::TupleArgumentsFlag::No,
-            fn_host_effect,
         )
         .map_bound(|(trait_ref, _)| trait_ref)
     }
diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
index 3814f8112e9..f8d98eb856e 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
@@ -82,6 +82,8 @@ impl<'tcx> At<'_, 'tcx> {
             }
 
             Ok(self.infcx.resolve_vars_if_possible(new_infer_ct))
+        } else if self.infcx.tcx.features().generic_const_exprs {
+            Ok(ct.normalize_internal(self.infcx.tcx, self.param_env))
         } else {
             Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx))
         }
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index aed2e3d61aa..b7a2f20b769 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -215,22 +215,13 @@ pub(crate) fn closure_trait_ref_and_return_type<'tcx>(
     self_ty: Ty<'tcx>,
     sig: ty::PolyFnSig<'tcx>,
     tuple_arguments: TupleArgumentsFlag,
-    fn_host_effect: ty::Const<'tcx>,
 ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> {
     assert!(!self_ty.has_escaping_bound_vars());
     let arguments_tuple = match tuple_arguments {
         TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
         TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()),
     };
-    let trait_ref = if tcx.has_host_param(fn_trait_def_id) {
-        ty::TraitRef::new(tcx, fn_trait_def_id, [
-            ty::GenericArg::from(self_ty),
-            ty::GenericArg::from(arguments_tuple),
-            ty::GenericArg::from(fn_host_effect),
-        ])
-    } else {
-        ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple])
-    };
+    let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]);
     sig.map_bound(|sig| (trait_ref, sig.output()))
 }
 
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index c1d0b704ab2..f7651e49cdd 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -85,7 +85,6 @@ mod rustc {
     use rustc_macros::TypeVisitable;
     use rustc_middle::traits::ObligationCause;
     use rustc_middle::ty::{Const, ParamEnv, Ty, TyCtxt, ValTree};
-    use rustc_span::DUMMY_SP;
 
     use super::*;
 
@@ -134,13 +133,8 @@ mod rustc {
             use rustc_middle::ty::ScalarInt;
             use rustc_span::symbol::sym;
 
-            let Ok((ty, cv)) = c.eval(tcx, param_env, DUMMY_SP) else {
-                return Some(Self {
-                    alignment: true,
-                    lifetimes: true,
-                    safety: true,
-                    validity: true,
-                });
+            let Some((cv, ty)) = c.try_to_valtree() else {
+                return None;
             };
 
             let adt_def = ty.ty_adt_def()?;
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 38b292afe8d..e755e90aa65 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -176,12 +176,12 @@ fn layout_of_uncached<'tcx>(
                     if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &mut layout.abi {
                         if let Some(start) = start {
                             scalar.valid_range_mut().start = start
-                                .try_eval_bits(tcx, param_env)
+                                .try_to_bits(tcx, param_env)
                                 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
                         }
                         if let Some(end) = end {
                             let mut end = end
-                                .try_eval_bits(tcx, param_env)
+                                .try_to_bits(tcx, param_env)
                                 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
                             if !include_end {
                                 end = end.wrapping_sub(1);
@@ -321,7 +321,7 @@ fn layout_of_uncached<'tcx>(
             }
 
             let count = count
-                .try_eval_target_usize(tcx, param_env)
+                .try_to_target_usize(tcx)
                 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
             let element = cx.layout_of(element)?;
             let size = element
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index 8146181df6c..b613505f826 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -726,9 +726,9 @@ pub struct CoercePredicate<I: Interner> {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))]
 pub enum BoundConstness {
-    /// `Type: Trait`
-    NotConst,
     /// `Type: const Trait`
+    ///
+    /// A bound is required to be unconditionally const, even in a runtime function.
     Const,
     /// `Type: ~const Trait`
     ///
@@ -739,7 +739,6 @@ pub enum BoundConstness {
 impl BoundConstness {
     pub fn as_str(self) -> &'static str {
         match self {
-            Self::NotConst => "",
             Self::Const => "const",
             Self::ConstIfConst => "~const",
         }
@@ -749,7 +748,6 @@ impl BoundConstness {
 impl fmt::Display for BoundConstness {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            Self::NotConst => f.write_str("normal"),
             Self::Const => f.write_str("const"),
             Self::ConstIfConst => f.write_str("~const"),
         }
diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs
index ae9608ec7bd..c1907361f93 100644
--- a/library/alloc/benches/lib.rs
+++ b/library/alloc/benches/lib.rs
@@ -4,7 +4,8 @@
 #![feature(iter_next_chunk)]
 #![feature(repr_simd)]
 #![feature(slice_partition_dedup)]
-#![feature(strict_provenance)]
+#![cfg_attr(bootstrap, feature(strict_provenance))]
+#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
 #![feature(test)]
 #![deny(fuzzy_provenance_casts)]
 
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index ae9b3739858..50a5f3c5b1e 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -147,7 +147,6 @@
 #![feature(slice_range)]
 #![feature(std_internals)]
 #![feature(str_internals)]
-#![feature(strict_provenance)]
 #![feature(trusted_fused)]
 #![feature(trusted_len)]
 #![feature(trusted_random_access)]
@@ -162,6 +161,8 @@
 //
 // Language features:
 // tidy-alphabetical-start
+#![cfg_attr(bootstrap, feature(strict_provenance))]
+#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
 #![cfg_attr(not(test), feature(coroutine_trait))]
 #![cfg_attr(test, feature(panic_update_hook))]
 #![cfg_attr(test, feature(test))]
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index 301126b5d4d..699a8e6776e 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -32,7 +32,8 @@
 #![feature(panic_update_hook)]
 #![feature(pointer_is_aligned_to)]
 #![feature(thin_box)]
-#![feature(strict_provenance)]
+#![cfg_attr(bootstrap, feature(strict_provenance))]
+#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
 #![feature(drain_keep_rest)]
 #![feature(local_waker)]
 #![feature(vec_pop_if)]
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 38e858626b9..97e727633c5 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2794,7 +2794,6 @@ where
 /// #![feature(is_val_statically_known)]
 /// #![feature(core_intrinsics)]
 /// # #![allow(internal_features)]
-/// #![feature(strict_provenance)]
 /// use std::intrinsics::is_val_statically_known;
 ///
 /// fn foo(x: &i32) -> bool {
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index ad034d3e576..16877566765 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -163,7 +163,6 @@
 #![feature(str_internals)]
 #![feature(str_split_inclusive_remainder)]
 #![feature(str_split_remainder)]
-#![feature(strict_provenance)]
 #![feature(ub_checks)]
 #![feature(unchecked_neg)]
 #![feature(unchecked_shifts)]
@@ -174,6 +173,8 @@
 //
 // Language features:
 // tidy-alphabetical-start
+#![cfg_attr(bootstrap, feature(strict_provenance))]
+#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
 #![feature(abi_unadjusted)]
 #![feature(adt_const_params)]
 #![feature(allow_internal_unsafe)]
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 22fd47b0596..95fa6c9c950 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -1784,9 +1784,11 @@ mod prim_ref {}
 ///   unique field that doesn't have size 0 and alignment 1 (if there is such a field).
 /// - `i32` is ABI-compatible with `NonZero<i32>`, and similar for all other integer types.
 /// - If `T` is guaranteed to be subject to the [null pointer
-///   optimization](option/index.html#representation), then `T` and `Option<T>` are ABI-compatible.
-///   Furthermore, if `U` satisfies the requirements [outlined here](result/index.html#representation),
-///   then `T` and `Result<T, U>` and `Result<U, T>` are all ABI-compatible.
+///   optimization](option/index.html#representation), and `E` is an enum satisfying the following
+///   requirements, then `T` and `E` are ABI-compatible. Such an enum `E` is called "option-like".
+///   - The enum `E` has exactly two variants.
+///   - One variant has exactly one field, of type `T`.
+///   - All fields of the other variant are zero-sized with 1-byte alignment.
 ///
 /// Furthermore, ABI compatibility satisfies the following general properties:
 ///
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 72f4bc2c9da..9ee0fb5948e 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -137,10 +137,11 @@ impl<T: ?Sized> *const T {
 
     /// Gets the "address" portion of the pointer.
     ///
-    /// This is similar to `self as usize`, which semantically discards *provenance* and
-    /// *address-space* information. However, unlike `self as usize`, casting the returned address
-    /// back to a pointer yields a [pointer without provenance][without_provenance], which is undefined behavior to dereference. To
-    /// properly restore the lost information and obtain a dereferenceable pointer, use
+    /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of
+    /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that
+    /// casting the returned address back to a pointer yields a [pointer without
+    /// provenance][without_provenance], which is undefined behavior to dereference. To properly
+    /// restore the lost information and obtain a dereferenceable pointer, use
     /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
     ///
     /// If using those APIs is not possible because there is no way to preserve a pointer with the
@@ -155,90 +156,81 @@ impl<T: ?Sized> *const T {
     /// perform a change of representation to produce a value containing only the address
     /// portion of the pointer. What that means is up to the platform to define.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such
-    /// might change in the future (including possibly weakening this so it becomes wholly
-    /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline(always)]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn addr(self) -> usize {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+        // A pointer-to-integer transmute currently has exactly the right semantics: it returns the
+        // address without exposing the provenance. Note that this is *not* a stable guarantee about
+        // transmute semantics, it relies on sysroot crates having special status.
         // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
         // provenance).
         unsafe { mem::transmute(self.cast::<()>()) }
     }
 
-    /// Exposes the "provenance" part of the pointer for future use in
-    /// [`with_exposed_provenance`][] and returns the "address" portion.
+    /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in
+    /// [`with_exposed_provenance`] and returns the "address" portion.
     ///
-    /// This is equivalent to `self as usize`, which semantically discards *provenance* and
-    /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit
-    /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can
-    /// later call [`with_exposed_provenance`][] to reconstitute the original pointer including its
-    /// provenance. (Reconstructing address space information, if required, is your responsibility.)
+    /// This is equivalent to `self as usize`, which semantically discards provenance information.
+    /// Furthermore, this (like the `as` cast) has the implicit side-effect of marking the
+    /// provenance as 'exposed', so on platforms that support it you can later call
+    /// [`with_exposed_provenance`] to reconstitute the original pointer including its provenance.
     ///
-    /// Using this method means that code is *not* following [Strict
-    /// Provenance][super#strict-provenance] rules. Supporting
-    /// [`with_exposed_provenance`][] complicates specification and reasoning and may not be supported by
-    /// tools that help you to stay conformant with the Rust memory model, so it is recommended to
-    /// use [`addr`][pointer::addr] wherever possible.
+    /// Due to its inherent ambiguity, [`with_exposed_provenance`] may not be supported by tools
+    /// that help you to stay conformant with the Rust memory model. It is recommended to use
+    /// [Strict Provenance][crate::ptr#strict-provenance] APIs such as [`with_addr`][pointer::with_addr]
+    /// wherever possible, in which case [`addr`][pointer::addr] should be used instead of `expose_provenance`.
     ///
     /// On most platforms this will produce a value with the same bytes as the original pointer,
     /// because all the bytes are dedicated to describing the address. Platforms which need to store
     /// additional information in the pointer may not support this operation, since the 'expose'
-    /// side-effect which is required for [`with_exposed_provenance`][] to work is typically not
+    /// side-effect which is required for [`with_exposed_provenance`] to work is typically not
     /// available.
     ///
-    /// It is unclear whether this method can be given a satisfying unambiguous specification. This
-    /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance].
+    /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
     ///
     /// [`with_exposed_provenance`]: with_exposed_provenance
     #[must_use]
     #[inline(always)]
-    #[unstable(feature = "exposed_provenance", issue = "95228")]
+    #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn expose_provenance(self) -> usize {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
         self.cast::<()>() as usize
     }
 
-    /// Creates a new pointer with the given address.
+    /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of
+    /// `self`.
     ///
-    /// This performs the same operation as an `addr as ptr` cast, but copies
-    /// the *address-space* and *provenance* of `self` to the new pointer.
-    /// This allows us to dynamically preserve and propagate this important
-    /// information in a way that is otherwise impossible with a unary cast.
+    /// This is similar to a `addr as *const T` cast, but copies
+    /// the *provenance* of `self` to the new pointer.
+    /// This avoids the inherent ambiguity of the unary cast.
     ///
     /// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset
     /// `self` to the given address, and therefore has all the same capabilities and restrictions.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn with_addr(self, addr: usize) -> Self {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
-        //
-        // In the mean-time, this operation is defined to be "as if" it was
-        // a wrapping_offset, so we can emulate it as such. This should properly
-        // restore pointer provenance even under today's compiler.
+        // This should probably be an intrinsic to avoid doing any sort of arithmetic, but
+        // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's
+        // provenance.
         let self_addr = self.addr() as isize;
         let dest_addr = addr as isize;
         let offset = dest_addr.wrapping_sub(self_addr);
-
-        // This is the canonical desugaring of this operation
         self.wrapping_byte_offset(offset)
     }
 
-    /// Creates a new pointer by mapping `self`'s address to a new one.
+    /// Creates a new pointer by mapping `self`'s address to a new one, preserving the
+    /// [provenance][crate::ptr#provenance] of `self`.
     ///
     /// This is a convenience for [`with_addr`][pointer::with_addr], see that method for details.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
         self.with_addr(f(self.addr()))
     }
@@ -379,7 +371,7 @@ impl<T: ?Sized> *const T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
     ///   [allocated object], and the entire memory range between `self` and the result must be in
     ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
     ///   of the address space.
@@ -560,7 +552,7 @@ impl<T: ?Sized> *const T {
     /// ## Examples
     ///
     /// ```
-    /// #![feature(ptr_mask, strict_provenance)]
+    /// #![feature(ptr_mask)]
     /// let v = 17_u32;
     /// let ptr: *const u32 = &v;
     ///
@@ -611,7 +603,7 @@ impl<T: ?Sized> *const T {
     /// * `self` and `origin` must either
     ///
     ///   * point to the same address, or
-    ///   * both be *derived from* a pointer to the same [allocated object], and the memory range between
+    ///   * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between
     ///     the two pointers must be in bounds of that object. (See below for an example.)
     ///
     /// * The distance between the pointers, in bytes, must be an exact multiple
@@ -871,7 +863,7 @@ impl<T: ?Sized> *const T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
     ///   [allocated object], and the entire memory range between `self` and the result must be in
     ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
     ///   of the address space.
@@ -978,7 +970,7 @@ impl<T: ?Sized> *const T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
     ///   [allocated object], and the entire memory range between `self` and the result must be in
     ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
     ///   of the address space.
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index f7036f30a99..f769b515877 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -18,10 +18,11 @@
 //! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer.
 //!   The following points are only concerned with non-zero-sized accesses.
 //! * A [null] pointer is *never* valid.
-//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer
-//!   be *dereferenceable*: the memory range of the given size starting at the pointer must all be
-//!   within the bounds of a single allocated object. Note that in Rust,
-//!   every (stack-allocated) variable is considered a separate allocated object.
+//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be
+//!   *dereferenceable*. The [provenance] of the pointer is used to determine which [allocated
+//!   object] it is derived from; a pointer is dereferenceable if the memory range of the given size
+//!   starting at the pointer is entirely contained within the bounds of that allocated object. Note
+//!   that in Rust, every (stack-allocated) variable is considered a separate allocated object.
 //! * All accesses performed by functions in this module are *non-atomic* in the sense
 //!   of [atomic operations] used to synchronize between threads. This means it is
 //!   undefined behavior to perform two concurrent accesses to the same location from different
@@ -130,123 +131,130 @@
 //!
 //! [`null()`]: null
 //!
-//! # Strict Provenance
-//!
-//! **The following text is non-normative, insufficiently formal, and is an extremely strict
-//! interpretation of provenance. It's ok if your code doesn't strictly conform to it.**
-//!
-//! [Strict Provenance][] is an experimental set of APIs that help tools that try
-//! to validate the memory-safety of your program's execution. Notably this includes [Miri][]
-//! and [CHERI][], which can detect when you access out of bounds memory or otherwise violate
-//! Rust's memory model.
-//!
-//! Provenance must exist in some form for any programming
-//! language compiled for modern computer architectures, but specifying a model for provenance
-//! in a way that is useful to both compilers and programmers is an ongoing challenge.
-//! The [Strict Provenance][] experiment seeks to explore the question: *what if we just said you
-//! couldn't do all the nasty operations that make provenance so messy?*
-//!
-//! What APIs would have to be removed? What APIs would have to be added? How much would code
-//! have to change, and is it worse or better now? Would any patterns become truly inexpressible?
-//! Could we carve out special exceptions for those patterns? Should we?
-//!
-//! A secondary goal of this project is to see if we can disambiguate the many functions of
-//! pointer<->integer casts enough for the definition of `usize` to be loosened so that it
-//! isn't *pointer*-sized but address-space/offset/allocation-sized (we'll probably continue
-//! to conflate these notions). This would potentially make it possible to more efficiently
-//! target platforms where pointers are larger than offsets, such as CHERI and maybe some
-//! segmented architectures.
-//!
-//! ## Provenance
-//!
-//! **This section is *non-normative* and is part of the [Strict Provenance][] experiment.**
+//! # Provenance
 //!
 //! Pointers are not *simply* an "integer" or "address". For instance, it's uncontroversial
 //! to say that a Use After Free is clearly Undefined Behaviour, even if you "get lucky"
 //! and the freed memory gets reallocated before your read/write (in fact this is the
 //! worst-case scenario, UAFs would be much less concerning if this didn't happen!).
-//! To rationalize this claim, pointers need to somehow be *more* than just their addresses:
-//! they must have provenance.
+//! As another example, consider that [`wrapping_offset`] is documented to "remember"
+//! the allocated object that the original pointer points to, even if it is offset far
+//! outside the memory range occupied by that allocated object.
+//! To rationalize claims like this, pointers need to somehow be *more* than just their addresses:
+//! they must have **provenance**.
 //!
-//! When an allocation is created, that allocation has a unique Original Pointer. For alloc
-//! APIs this is literally the pointer the call returns, and for local variables and statics,
-//! this is the name of the variable/static. This is mildly overloading the term "pointer"
-//! for the sake of brevity/exposition.
+//! A pointer value in Rust semantically contains the following information:
+//!
+//! * The **address** it points to, which can be represented by a `usize`.
+//! * The **provenance** it has, defining the memory it has permission to access. Provenance can be
+//!   absent, in which case the pointer does not have permission to access any memory.
 //!
-//! The Original Pointer for an allocation is guaranteed to have unique access to the entire
-//! allocation and *only* that allocation. In this sense, an allocation can be thought of
-//! as a "sandbox" that cannot be broken into or out of. *Provenance* is the permission
-//! to access an allocation's sandbox and has both a *spatial* and *temporal* component:
+//! The exact structure of provenance is not yet specified, but the permission defined by a
+//! pointer's provenance have a *spatial* component, a *temporal* component, and a *mutability*
+//! component:
 //!
-//! * Spatial: A range of bytes that the pointer is allowed to access.
-//! * Temporal: The lifetime (of the allocation) that access to these bytes is tied to.
+//! * Spatial: The set of memory addresses that the pointer is allowed to access.
+//! * Temporal: The timespan during which the pointer is allowed to access those memory addresses.
+//! * Mutability: Whether the pointer may only access the memory for reads, or also access it for
+//!   writes. Note that this can interact with the other components, e.g. a pointer might permit
+//!   mutation only for a subset of addresses, or only for a subset of its maximal timespan.
 //!
-//! Spatial provenance makes sure you don't go beyond your sandbox, while temporal provenance
-//! makes sure that you can't "get lucky" after your permission to access some memory
-//! has been revoked (either through deallocations or borrows expiring).
+//! When an [allocated object] is created, it has a unique Original Pointer. For alloc
+//! APIs this is literally the pointer the call returns, and for local variables and statics,
+//! this is the name of the variable/static. (This is mildly overloading the term "pointer"
+//! for the sake of brevity/exposition.)
+//!
+//! The Original Pointer for an allocated object has provenance that constrains the *spatial*
+//! permissions of this pointer to the memory range of the allocation, and the *temporal*
+//! permissions to the lifetime of the allocation. Provenance is implicitly inherited by all
+//! pointers transitively derived from the Original Pointer through operations like [`offset`],
+//! borrowing, and pointer casts. Some operations may *shrink* the permissions of the derived
+//! provenance, limiting how much memory it can access or how long it's valid for (i.e. borrowing a
+//! subfield and subslicing can shrink the spatial component of provenance, and all borrowing can
+//! shrink the temporal component of provenance). However, no operation can ever *grow* the
+//! permissions of the derived provenance: even if you "know" there is a larger allocation, you
+//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine" two
+//! contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`).
+//!
+//! A reference to a place always has provenance over at least the memory that place occupies.
+//! A reference to a slice always has provenance over at least the range that slice describes.
+//! Whether and when exactly the provenance of a reference gets "shrunk" to *exactly* fit
+//! the memory it points to is not yet determined.
+//!
+//! A *shared* reference only ever has provenance that permits reading from memory,
+//! and never permits writes, except inside [`UnsafeCell`].
+//!
+//! Provenance can affect whether a program has undefined behavior:
+//!
+//! * It is undefined behavior to access memory through a pointer that does not have provenance over
+//!   that memory. Note that a pointer "at the end" of its provenance is not actually outside its
+//!   provenance, it just has 0 bytes it can load/store. Zero-sized accesses do not require any
+//!   provenance since they access an empty range of memory.
+//!
+//! * It is undefined behavior to [`offset`] a pointer across a memory range that is not contained
+//!   in the allocated object it is derived from, or to [`offset_from`] two pointers not derived
+//!   from the same allocated object. Provenance is used to say what exactly "derived from" even
+//!   means: the lineage of a pointer is traced back to the Original Pointer it descends from, and
+//!   that identifies the relevant allocated object. In particular, it's always UB to offset a
+//!   pointer derived from something that is now deallocated, except if the offset is 0.
 //!
-//! Provenance is implicitly shared with all pointers transitively derived from
-//! The Original Pointer through operations like [`offset`], borrowing, and pointer casts.
-//! Some operations may *shrink* the derived provenance, limiting how much memory it can
-//! access or how long it's valid for (i.e. borrowing a subfield and subslicing).
+//! But it *is* still sound to:
 //!
-//! Shrinking provenance cannot be undone: even if you "know" there is a larger allocation, you
-//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine"
-//! two contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`).
+//! * Create a pointer without provenance from just an address (see [`ptr::dangling`]). Such a
+//!   pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be
+//!   useful for sentinel values like `null` *or* to represent a tagged pointer that will never be
+//!   dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for
+//!   fun" as long as you don't use operations on it which require it to be valid (non-zero-sized
+//!   offset, read, write, etc).
 //!
-//! A reference to a value always has provenance over exactly the memory that field occupies.
-//! A reference to a slice always has provenance over exactly the range that slice describes.
+//! * Forge an allocation of size zero at any sufficiently aligned non-null address.
+//!   i.e. the usual "ZSTs are fake, do what you want" rules apply.
 //!
-//! If an allocation is deallocated, all pointers with provenance to that allocation become
-//! invalidated, and effectively lose their provenance.
+//! * [`wrapping_offset`] a pointer outside its provenance. This includes pointers
+//!   which have "no" provenance. In particular, this makes it sound to do pointer tagging tricks.
 //!
-//! The strict provenance experiment is mostly only interested in exploring stricter *spatial*
-//! provenance. In this sense it can be thought of as a subset of the more ambitious and
-//! formal [Stacked Borrows][] research project, which is what tools like [Miri][] are based on.
-//! In particular, Stacked Borrows is necessary to properly describe what borrows are allowed
-//! to do and when they become invalidated. This necessarily involves much more complex
-//! *temporal* reasoning than simply identifying allocations. Adjusting APIs and code
-//! for the strict provenance experiment will also greatly help Stacked Borrows.
+//! * Compare arbitrary pointers by address. Pointer comparison ignores provenance and addresses
+//!   *are* just integers, so there is always a coherent answer, even if the pointers are dangling
+//!   or from different provenances. Note that if you get "lucky" and notice that a pointer at the
+//!   end of one allocated object is the "same" address as the start of another allocated object,
+//!   anything you do with that fact is *probably* going to be gibberish. The scope of that
+//!   gibberish is kept under control by the fact that the two pointers *still* aren't allowed to
+//!   access the other's allocation (bytes), because they still have different provenance.
 //!
+//! Note that the full definition of provenance in Rust is not decided yet, as this interacts
+//! with the as-yet undecided [aliasing] rules.
 //!
-//! ## Pointer Vs Addresses
+//! ## Pointers Vs Integers
 //!
-//! **This section is *non-normative* and is part of the [Strict Provenance][] experiment.**
+//! From this discussion, it becomes very clear that a `usize` *cannot* accurately represent a pointer,
+//! and converting from a pointer to a `usize` is generally an operation which *only* extracts the
+//! address. Converting this address back into pointer requires somehow answering the question:
+//! which provenance should the resulting pointer have?
 //!
-//! One of the largest historical issues with trying to define provenance is that programmers
-//! freely convert between pointers and integers. Once you allow for this, it generally becomes
-//! impossible to accurately track and preserve provenance information, and you need to appeal
-//! to very complex and unreliable heuristics. But of course, converting between pointers and
-//! integers is very useful, so what can we do?
+//! Rust provides two ways of dealing with this situation: *Strict Provenance* and *Exposed Provenance*.
 //!
-//! Also did you know WASM is actually a "Harvard Architecture"? As in function pointers are
-//! handled completely differently from data pointers? And we kind of just shipped Rust on WASM
-//! without really addressing the fact that we let you freely convert between function pointers
-//! and data pointers, because it mostly Just Works? Let's just put that on the "pointer casts
-//! are dubious" pile.
+//! Note that a pointer *can* represent a `usize` (via [`without_provenance`]), so the right type to
+//! use in situations where a value is "sometimes a pointer and sometimes a bare `usize`" is a
+//! pointer type.
 //!
-//! Strict Provenance attempts to square these circles by decoupling Rust's traditional conflation
-//! of pointers and `usize` (and `isize`), and defining a pointer to semantically contain the
-//! following information:
+//! ## Strict Provenance
 //!
-//! * The **address-space** it is part of (e.g. "data" vs "code" in WASM).
-//! * The **address** it points to, which can be represented by a `usize`.
-//! * The **provenance** it has, defining the memory it has permission to access.
-//!   Provenance can be absent, in which case the pointer does not have permission to access any memory.
+//! "Strict Provenance" refers to a set of APIs designed to make working with provenance more
+//! explicit. They are intended as substitutes for casting a pointer to an integer and back.
 //!
-//! Under Strict Provenance, a `usize` *cannot* accurately represent a pointer, and converting from
-//! a pointer to a `usize` is generally an operation which *only* extracts the address. It is
-//! therefore *impossible* to construct a valid pointer from a `usize` because there is no way
-//! to restore the address-space and provenance. In other words, pointer-integer-pointer
-//! roundtrips are not possible (in the sense that the resulting pointer is not dereferenceable).
+//! Entirely avoiding integer-to-pointer casts successfully side-steps the inherent ambiguity of
+//! that operation. This benefits compiler optimizations, and it is pretty much a requirement for
+//! using tools like [Miri] and architectures like [CHERI] that aim to detect and diagnose pointer
+//! misuse.
 //!
-//! The key insight to making this model *at all* viable is the [`with_addr`][] method:
+//! The key insight to making programming without integer-to-pointer casts *at all* viable is the
+//! [`with_addr`] method:
 //!
 //! ```text
 //!     /// Creates a new pointer with the given address.
 //!     ///
 //!     /// This performs the same operation as an `addr as ptr` cast, but copies
-//!     /// the *address-space* and *provenance* of `self` to the new pointer.
+//!     /// the *provenance* of `self` to the new pointer.
 //!     /// This allows us to dynamically preserve and propagate this important
 //!     /// information in a way that is otherwise impossible with a unary cast.
 //!     ///
@@ -257,23 +265,21 @@
 //!
 //! So you're still able to drop down to the address representation and do whatever
 //! clever bit tricks you want *as long as* you're able to keep around a pointer
-//! into the allocation you care about that can "reconstitute" the other parts of the pointer.
+//! into the allocation you care about that can "reconstitute" the provenance.
 //! Usually this is very easy, because you only are taking a pointer, messing with the address,
 //! and then immediately converting back to a pointer. To make this use case more ergonomic,
-//! we provide the [`map_addr`][] method.
+//! we provide the [`map_addr`] method.
 //!
 //! To help make it clear that code is "following" Strict Provenance semantics, we also provide an
-//! [`addr`][] method which promises that the returned address is not part of a
-//! pointer-usize-pointer roundtrip. In the future we may provide a lint for pointer<->integer
+//! [`addr`] method which promises that the returned address is not part of a
+//! pointer-integer-pointer roundtrip. In the future we may provide a lint for pointer<->integer
 //! casts to help you audit if your code conforms to strict provenance.
 //!
-//!
-//! ## Using Strict Provenance
+//! ### Using Strict Provenance
 //!
 //! Most code needs no changes to conform to strict provenance, as the only really concerning
-//! operation that *wasn't* obviously already Undefined Behaviour is casts from usize to a
-//! pointer. For code which *does* cast a `usize` to a pointer, the scope of the change depends
-//! on exactly what you're doing.
+//! operation is casts from usize to a pointer. For code which *does* cast a `usize` to a pointer,
+//! the scope of the change depends on exactly what you're doing.
 //!
 //! In general, you just need to make sure that if you want to convert a `usize` address to a
 //! pointer and then use that pointer to read/write memory, you need to keep around a pointer
@@ -284,8 +290,6 @@
 //! represent the tagged pointer as an actual pointer and not a `usize`*. For instance:
 //!
 //! ```
-//! #![feature(strict_provenance)]
-//!
 //! unsafe {
 //!     // A flag we want to pack into our pointer
 //!     static HAS_DATA: usize = 0x1;
@@ -314,122 +318,65 @@
 //! be using AtomicPtr instead. If that messes up the way you atomically manipulate pointers,
 //! we would like to know why, and what needs to be done to fix it.)
 //!
-//! Something more complicated and just generally *evil* like an XOR-List requires more significant
-//! changes like allocating all nodes in a pre-allocated Vec or Arena and using a pointer
-//! to the whole allocation to reconstitute the XORed addresses.
-//!
 //! Situations where a valid pointer *must* be created from just an address, such as baremetal code
-//! accessing a memory-mapped interface at a fixed address, are an open question on how to support.
-//! These situations *will* still be allowed, but we might require some kind of "I know what I'm
-//! doing" annotation to explain the situation to the compiler. It's also possible they need no
-//! special attention at all, because they're generally accessing memory outside the scope of
-//! "the abstract machine", or already using "I know what I'm doing" annotations like "volatile".
-//!
-//! Under [Strict Provenance] it is Undefined Behaviour to:
-//!
-//! * Access memory through a pointer that does not have provenance over that memory.
-//!
-//! * [`offset`] a pointer to or from an address it doesn't have provenance over.
-//!   This means it's always UB to offset a pointer derived from something deallocated,
-//!   even if the offset is 0. Note that a pointer "one past the end" of its provenance
-//!   is not actually outside its provenance, it just has 0 bytes it can load/store.
-//!
-//! But it *is* still sound to:
-//!
-//! * Create a pointer without provenance from just an address (see [`ptr::dangling`][]). Such a
-//!   pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be
-//!   useful for sentinel values like `null` *or* to represent a tagged pointer that will never be
-//!   dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for
-//!   fun" as long as you don't use operations on it which require it to be valid (non-zero-sized
-//!   offset, read, write, etc).
-//!
-//! * Forge an allocation of size zero at any sufficiently aligned non-null address.
-//!   i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies
-//!   for actual forgery (integers cast to pointers). If you borrow some struct's field
-//!   that *happens* to be zero-sized, the resulting pointer will have provenance tied to
-//!   that allocation, and it will still get invalidated if the allocation gets deallocated.
-//!   In the future we may introduce an API to make such a forged allocation explicit.
-//!
-//! * [`wrapping_offset`][] a pointer outside its provenance. This includes pointers
-//!   which have "no" provenance. Unfortunately there may be practical limits on this for a
-//!   particular platform, and it's an open question as to how to specify this (if at all).
-//!   Notably, [CHERI][] relies on a compression scheme that can't handle a
-//!   pointer getting offset "too far" out of bounds. If this happens, the address
-//!   returned by `addr` will be the value you expect, but the provenance will get invalidated
-//!   and using it to read/write will fault. The details of this are architecture-specific
-//!   and based on alignment, but the buffer on either side of the pointer's range is pretty
-//!   generous (think kilobytes, not bytes).
-//!
-//! * Compare arbitrary pointers by address. Addresses *are* just integers and so there is
-//!   always a coherent answer, even if the pointers are dangling or from different
-//!   address-spaces/provenances. Of course, comparing addresses from different address-spaces
-//!   is generally going to be *meaningless*, but so is comparing Kilograms to Meters, and Rust
-//!   doesn't prevent that either. Similarly, if you get "lucky" and notice that a pointer
-//!   one-past-the-end is the "same" address as the start of an unrelated allocation, anything
-//!   you do with that fact is *probably* going to be gibberish. The scope of that gibberish
-//!   is kept under control by the fact that the two pointers *still* aren't allowed to access
-//!   the other's allocation (bytes), because they still have different provenance.
-//!
-//! * Perform pointer tagging tricks. This falls out of [`wrapping_offset`] but is worth
-//!   mentioning in more detail because of the limitations of [CHERI][]. Low-bit tagging
-//!   is very robust, and often doesn't even go out of bounds because types ensure
-//!   size >= align (and over-aligning actually gives CHERI more flexibility). Anything
-//!   more complex than this rapidly enters "extremely platform-specific" territory as
-//!   certain things may or may not be allowed based on specific supported operations.
-//!   For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits
-//!   that and should support it.
+//! accessing a memory-mapped interface at a fixed address, cannot currently be handled with strict
+//! provenance APIs and should use [exposed provenance](#exposed-provenance).
 //!
 //! ## Exposed Provenance
 //!
-//! **This section is *non-normative* and is an extension to the [Strict Provenance] experiment.**
-//!
-//! As discussed above, pointer-usize-pointer roundtrips are not possible under [Strict Provenance].
+//! As discussed above, integer-to-pointer casts are not possible with Strict Provenance APIs.
 //! This is by design: the goal of Strict Provenance is to provide a clear specification that we are
-//! confident can be formalized unambiguously and can be subject to  precise formal reasoning.
+//! confident can be formalized unambiguously and can be subject to precise formal reasoning.
+//! Integer-to-pointer casts do not (currently) have such a clear specification.
 //!
-//! However, there exist situations where pointer-usize-pointer roundtrips cannot be avoided, or
+//! However, there exist situations where integer-to-pointer casts cannot be avoided, or
 //! where avoiding them would require major refactoring. Legacy platform APIs also regularly assume
-//! that `usize` can capture all the information that makes up a pointer. The goal of Strict
-//! Provenance is not to rule out such code; the goal is to put all the *other* pointer-manipulating
-//! code onto a more solid foundation. Strict Provenance is about improving the situation where
-//! possible (all the code that can be written with Strict Provenance) without making things worse
-//! for situations where Strict Provenance is insufficient.
-//!
-//! For these situations, there is a highly experimental extension to Strict Provenance called
-//! *Exposed Provenance*. This extension permits pointer-usize-pointer roundtrips. However, its
-//! semantics are on much less solid footing than Strict Provenance, and at this point it is not yet
-//! clear where a satisfying unambiguous semantics can be defined for Exposed Provenance.
-//! Furthermore, Exposed Provenance will not work (well) with tools like [Miri] and [CHERI].
+//! that `usize` can capture all the information that makes up a pointer.
+//! Bare-metal platforms can also require the synthesis of a pointer "out of thin air" without
+//! anywhere to obtain proper provenance from.
+//!
+//! Rust's model for dealing with integer-to-pointer casts is called *Exposed Provenance*. However,
+//! the semantics of Exposed Provenance are on much less solid footing than Strict Provenance, and
+//! at this point it is not yet clear whether a satisfying unambiguous semantics can be defined for
+//! Exposed Provenance. (If that sounds bad, be reassured that other popular languages that provide
+//! integer-to-pointer casts are not faring any better.) Furthermore, Exposed Provenance will not
+//! work (well) with tools like [Miri] and [CHERI].
 //!
 //! Exposed Provenance is provided by the [`expose_provenance`] and [`with_exposed_provenance`] methods,
-//! which are meant to replace `as` casts between pointers and integers. [`expose_provenance`] is a lot like
-//! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed'
-//! provenances. (This list is purely conceptual, it exists for the purpose of specifying Rust but
-//! is not materialized in actual executions, except in tools like [Miri].) [`with_exposed_provenance`]
-//! can be used to construct a pointer with one of these previously 'exposed' provenances.
-//! [`with_exposed_provenance`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is
-//! no indication of what the correct provenance for the returned pointer is -- and that is exactly
-//! what makes pointer-usize-pointer roundtrips so tricky to rigorously specify! There is no
-//! algorithm that decides which provenance will be used. You can think of this as "guessing" the
-//! right provenance, and the guess will be "maximally in your favor", in the sense that if there is
-//! any way to avoid undefined behavior, then that is the guess that will be taken. However, if
-//! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will
-//! be used, the program has undefined behavior.
-//!
-//! Using [`expose_provenance`] or [`with_exposed_provenance`] (or the `as` casts) means that code is
-//! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to
-//! determine how far one can get in Rust without the use of [`expose_provenance`] and
-//! [`with_exposed_provenance`], and to encourage code to be written with Strict Provenance APIs only.
-//! Maximizing the amount of such code is a major win for avoiding specification complexity and to
-//! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the
-//! confidence in (unsafe) Rust code.
+//! which are equivalent to `as` casts between pointers and integers.
+//! - [`expose_provenance`] is a lot like [`addr`], but additionally adds the provenance of the
+//!   pointer to a global list of 'exposed' provenances. (This list is purely conceptual, it exists
+//!   for the purpose of specifying Rust but is not materialized in actual executions, except in
+//!   tools like [Miri].)
+//!   Memory which is outside the control of the Rust abstract machine (MMIO registers, for example)
+//!   is always considered to be exposed, so long as this memory is disjoint from memory that will
+//!   be used by the abstract machine such as the stack, heap, and statics.
+//! - [`with_exposed_provenance`] can be used to construct a pointer with one of these previously
+//!   'exposed' provenances. [`with_exposed_provenance`] takes only `addr: usize` as arguments, so
+//!   unlike in [`with_addr`] there is no indication of what the correct provenance for the returned
+//!   pointer is -- and that is exactly what makes integer-to-pointer casts so tricky to rigorously
+//!   specify! The compiler will do its best to pick the right provenance for you, but currently we
+//!   cannot provide any guarantees about which provenance the resulting pointer will have. Only one
+//!   thing is clear: if there is *no* previously 'exposed' provenance that justifies the way the
+//!   returned pointer will be used, the program has undefined behavior.
+//!
+//! If at all possible, we encourage code to be ported to [Strict Provenance] APIs, thus avoiding
+//! the need for Exposed Provenance. Maximizing the amount of such code is a major win for avoiding
+//! specification complexity and to facilitate adoption of tools like [CHERI] and [Miri] that can be
+//! a big help in increasing the confidence in (unsafe) Rust code. However, we acknowledge that this
+//! is not always possible, and offer Exposed Provenance as a way to explicit "opt out" of the
+//! well-defined semantics of Strict Provenance, and "opt in" to the unclear semantics of
+//! integer-to-pointer casts.
 //!
 //! [aliasing]: ../../nomicon/aliasing.html
+//! [allocated object]: #allocated-object
+//! [provenance]: #provenance
 //! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
 //! [ub]: ../../reference/behavior-considered-undefined.html
 //! [zst]: ../../nomicon/exotic-sizes.html#zero-sized-types-zsts
 //! [atomic operations]: crate::sync::atomic
 //! [`offset`]: pointer::offset
+//! [`offset_from`]: pointer::offset_from
 //! [`wrapping_offset`]: pointer::wrapping_offset
 //! [`with_addr`]: pointer::with_addr
 //! [`map_addr`]: pointer::map_addr
@@ -439,8 +386,8 @@
 //! [`with_exposed_provenance`]: with_exposed_provenance
 //! [Miri]: https://github.com/rust-lang/miri
 //! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/
-//! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228
-//! [Stacked Borrows]: https://plv.mpi-sws.org/rustbelt/stacked-borrows/
+//! [Strict Provenance]: #strict-provenance
+//! [`UnsafeCell`]: core::cell::UnsafeCell
 
 #![stable(feature = "rust1", since = "1.0.0")]
 // There are many unsafe functions taking pointers that don't dereference them.
@@ -629,7 +576,7 @@ pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
     from_raw_parts_mut(without_provenance_mut::<()>(0), ())
 }
 
-/// Creates a pointer with the given address and no provenance.
+/// Creates a pointer with the given address and no [provenance][crate::ptr#provenance].
 ///
 /// This is equivalent to `ptr::null().with_addr(addr)`.
 ///
@@ -641,16 +588,15 @@ pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
 /// This is different from `addr as *const T`, which creates a pointer that picks up a previously
 /// exposed provenance. See [`with_exposed_provenance`] for more details on that operation.
 ///
-/// This API and its claimed semantics are part of the Strict Provenance experiment,
-/// see the [module documentation][crate::ptr] for details.
+/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
 #[inline(always)]
 #[must_use]
 #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
-#[unstable(feature = "strict_provenance", issue = "95228")]
+#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
 pub const fn without_provenance<T>(addr: usize) -> *const T {
-    // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
-    // We use transmute rather than a cast so tools like Miri can tell that this
-    // is *not* the same as with_exposed_provenance.
+    // An int-to-pointer transmute currently has exactly the intended semantics: it creates a
+    // pointer without provenance. Note that this is *not* a stable guarantee about transmute
+    // semantics, it relies on sysroot crates having special status.
     // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that
     // pointer).
     unsafe { mem::transmute(addr) }
@@ -668,12 +614,12 @@ pub const fn without_provenance<T>(addr: usize) -> *const T {
 #[inline(always)]
 #[must_use]
 #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
-#[unstable(feature = "strict_provenance", issue = "95228")]
+#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
 pub const fn dangling<T>() -> *const T {
     without_provenance(mem::align_of::<T>())
 }
 
-/// Creates a pointer with the given address and no provenance.
+/// Creates a pointer with the given address and no [provenance][crate::ptr#provenance].
 ///
 /// This is equivalent to `ptr::null_mut().with_addr(addr)`.
 ///
@@ -685,16 +631,15 @@ pub const fn dangling<T>() -> *const T {
 /// This is different from `addr as *mut T`, which creates a pointer that picks up a previously
 /// exposed provenance. See [`with_exposed_provenance_mut`] for more details on that operation.
 ///
-/// This API and its claimed semantics are part of the Strict Provenance experiment,
-/// see the [module documentation][crate::ptr] for details.
+/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
 #[inline(always)]
 #[must_use]
 #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
-#[unstable(feature = "strict_provenance", issue = "95228")]
+#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
 pub const fn without_provenance_mut<T>(addr: usize) -> *mut T {
-    // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
-    // We use transmute rather than a cast so tools like Miri can tell that this
-    // is *not* the same as with_exposed_provenance.
+    // An int-to-pointer transmute currently has exactly the intended semantics: it creates a
+    // pointer without provenance. Note that this is *not* a stable guarantee about transmute
+    // semantics, it relies on sysroot crates having special status.
     // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that
     // pointer).
     unsafe { mem::transmute(addr) }
@@ -712,96 +657,88 @@ pub const fn without_provenance_mut<T>(addr: usize) -> *mut T {
 #[inline(always)]
 #[must_use]
 #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
-#[unstable(feature = "strict_provenance", issue = "95228")]
+#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
 pub const fn dangling_mut<T>() -> *mut T {
     without_provenance_mut(mem::align_of::<T>())
 }
 
-/// Converts an address back to a pointer, picking up a previously 'exposed' provenance.
+/// Converts an address back to a pointer, picking up some previously 'exposed'
+/// [provenance][crate::ptr#provenance].
 ///
-/// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the
-/// returned pointer is that of *any* pointer that was previously exposed by passing it to
-/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory which is
-/// outside the control of the Rust abstract machine (MMIO registers, for example) is always
-/// considered to be exposed, so long as this memory is disjoint from memory that will be used by
-/// the abstract machine such as the stack, heap, and statics.
+/// This is fully equivalent to `addr as *const T`. The provenance of the returned pointer is that
+/// of *some* pointer that was previously exposed by passing it to
+/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory
+/// which is outside the control of the Rust abstract machine (MMIO registers, for example) is
+/// always considered to be accessible with an exposed provenance, so long as this memory is disjoint
+/// from memory that will be used by the abstract machine such as the stack, heap, and statics.
 ///
-/// If there is no 'exposed' provenance that justifies the way this pointer will be used,
-/// the program has undefined behavior. In particular, the aliasing rules still apply: pointers
-/// and references that have been invalidated due to aliasing accesses cannot be used anymore,
-/// even if they have been exposed!
+/// The exact provenance that gets picked is not specified. The compiler will do its best to pick
+/// the "right" provenance for you (whatever that may be), but currently we cannot provide any
+/// guarantees about which provenance the resulting pointer will have -- and therefore there
+/// is no definite specification for which memory the resulting pointer may access.
 ///
-/// Note that there is no algorithm that decides which provenance will be used. You can think of this
-/// as "guessing" the right provenance, and the guess will be "maximally in your favor", in the sense
-/// that if there is any way to avoid undefined behavior (while upholding all aliasing requirements),
-/// then that is the guess that will be taken.
+/// If there is *no* previously 'exposed' provenance that justifies the way the returned pointer
+/// will be used, the program has undefined behavior. In particular, the aliasing rules still apply:
+/// pointers and references that have been invalidated due to aliasing accesses cannot be used
+/// anymore, even if they have been exposed!
 ///
-/// On platforms with multiple address spaces, it is your responsibility to ensure that the
-/// address makes sense in the address space that this pointer will be used with.
-///
-/// Using this function means that code is *not* following [Strict
-/// Provenance][self#strict-provenance] rules. "Guessing" a
-/// suitable provenance complicates specification and reasoning and may not be supported by
-/// tools that help you to stay conformant with the Rust memory model, so it is recommended to
-/// use [`with_addr`][pointer::with_addr] wherever possible.
+/// Due to its inherent ambiguity, this operation may not be supported by tools that help you to
+/// stay conformant with the Rust memory model. It is recommended to use [Strict
+/// Provenance][self#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] wherever
+/// possible.
 ///
 /// On most platforms this will produce a value with the same bytes as the address. Platforms
 /// which need to store additional information in a pointer may not support this operation,
 /// since it is generally not possible to actually *compute* which provenance the returned
 /// pointer has to pick up.
 ///
-/// It is unclear whether this function can be given a satisfying unambiguous specification. This
-/// API and its claimed semantics are part of [Exposed Provenance][self#exposed-provenance].
+/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
 #[must_use]
 #[inline(always)]
-#[unstable(feature = "exposed_provenance", issue = "95228")]
+#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
-pub fn with_exposed_provenance<T>(addr: usize) -> *const T
-where
-    T: Sized,
-{
-    // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+pub fn with_exposed_provenance<T>(addr: usize) -> *const T {
     addr as *const T
 }
 
-/// Converts an address back to a mutable pointer, picking up a previously 'exposed' provenance.
+/// Converts an address back to a mutable pointer, picking up some previously 'exposed'
+/// [provenance][crate::ptr#provenance].
+///
+/// This is fully equivalent to `addr as *mut T`. The provenance of the returned pointer is that
+/// of *some* pointer that was previously exposed by passing it to
+/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory
+/// which is outside the control of the Rust abstract machine (MMIO registers, for example) is
+/// always considered to be accessible with an exposed provenance, so long as this memory is disjoint
+/// from memory that will be used by the abstract machine such as the stack, heap, and statics.
 ///
-/// This is a more rigorously specified alternative to `addr as *mut T`. The provenance of the
-/// returned pointer is that of *any* pointer that was previously passed to
-/// [`expose_provenance`][pointer::expose_provenance] or a `ptr as usize` cast. If there is no previously
-/// 'exposed' provenance that justifies the way this pointer will be used, the program has undefined
-/// behavior. Note that there is no algorithm that decides which provenance will be used. You can
-/// think of this as "guessing" the right provenance, and the guess will be "maximally in your
-/// favor", in the sense that if there is any way to avoid undefined behavior, then that is the
-/// guess that will be taken.
+/// The exact provenance that gets picked is not specified. The compiler will do its best to pick
+/// the "right" provenance for you (whatever that may be), but currently we cannot provide any
+/// guarantees about which provenance the resulting pointer will have -- and therefore there
+/// is no definite specification for which memory the resulting pointer may access.
 ///
-/// On platforms with multiple address spaces, it is your responsibility to ensure that the
-/// address makes sense in the address space that this pointer will be used with.
+/// If there is *no* previously 'exposed' provenance that justifies the way the returned pointer
+/// will be used, the program has undefined behavior. In particular, the aliasing rules still apply:
+/// pointers and references that have been invalidated due to aliasing accesses cannot be used
+/// anymore, even if they have been exposed!
 ///
-/// Using this function means that code is *not* following [Strict
-/// Provenance][self#strict-provenance] rules. "Guessing" a
-/// suitable provenance complicates specification and reasoning and may not be supported by
-/// tools that help you to stay conformant with the Rust memory model, so it is recommended to
-/// use [`with_addr`][pointer::with_addr] wherever possible.
+/// Due to its inherent ambiguity, this operation may not be supported by tools that help you to
+/// stay conformant with the Rust memory model. It is recommended to use [Strict
+/// Provenance][self#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] wherever
+/// possible.
 ///
 /// On most platforms this will produce a value with the same bytes as the address. Platforms
 /// which need to store additional information in a pointer may not support this operation,
 /// since it is generally not possible to actually *compute* which provenance the returned
 /// pointer has to pick up.
 ///
-/// It is unclear whether this function can be given a satisfying unambiguous specification. This
-/// API and its claimed semantics are part of [Exposed Provenance][self#exposed-provenance].
+/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
 #[must_use]
 #[inline(always)]
-#[unstable(feature = "exposed_provenance", issue = "95228")]
+#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
-pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T
-where
-    T: Sized,
-{
-    // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T {
     addr as *mut T
 }
 
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 613d2c91ac6..782934fc311 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -124,12 +124,12 @@ impl<T: ?Sized> *mut T {
 
     /// Gets the "address" portion of the pointer.
     ///
-    /// This is similar to `self as usize`, which semantically discards *provenance* and
-    /// *address-space* information. However, unlike `self as usize`, casting the returned address
-    /// back to a pointer yields a [pointer without provenance][without_provenance_mut], which is undefined
-    /// behavior to dereference. To properly restore the lost information and obtain a
-    /// dereferenceable pointer, use [`with_addr`][pointer::with_addr] or
-    /// [`map_addr`][pointer::map_addr].
+    /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of
+    /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that
+    /// casting the returned address back to a pointer yields a [pointer without
+    /// provenance][without_provenance_mut], which is undefined behavior to dereference. To properly
+    /// restore the lost information and obtain a dereferenceable pointer, use
+    /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
     ///
     /// If using those APIs is not possible because there is no way to preserve a pointer with the
     /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts
@@ -143,89 +143,80 @@ impl<T: ?Sized> *mut T {
     /// perform a change of representation to produce a value containing only the address
     /// portion of the pointer. What that means is up to the platform to define.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such
-    /// might change in the future (including possibly weakening this so it becomes wholly
-    /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline(always)]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn addr(self) -> usize {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+        // A pointer-to-integer transmute currently has exactly the right semantics: it returns the
+        // address without exposing the provenance. Note that this is *not* a stable guarantee about
+        // transmute semantics, it relies on sysroot crates having special status.
         // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
         // provenance).
         unsafe { mem::transmute(self.cast::<()>()) }
     }
 
-    /// Exposes the "provenance" part of the pointer for future use in
-    /// [`with_exposed_provenance`][] and returns the "address" portion.
+    /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in
+    /// [`with_exposed_provenance_mut`] and returns the "address" portion.
     ///
-    /// This is equivalent to `self as usize`, which semantically discards *provenance* and
-    /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit
-    /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can
-    /// later call [`with_exposed_provenance_mut`][] to reconstitute the original pointer including its
-    /// provenance. (Reconstructing address space information, if required, is your responsibility.)
+    /// This is equivalent to `self as usize`, which semantically discards provenance information.
+    /// Furthermore, this (like the `as` cast) has the implicit side-effect of marking the
+    /// provenance as 'exposed', so on platforms that support it you can later call
+    /// [`with_exposed_provenance_mut`] to reconstitute the original pointer including its provenance.
     ///
-    /// Using this method means that code is *not* following [Strict
-    /// Provenance][super#strict-provenance] rules. Supporting
-    /// [`with_exposed_provenance_mut`][] complicates specification and reasoning and may not be supported
-    /// by tools that help you to stay conformant with the Rust memory model, so it is recommended
-    /// to use [`addr`][pointer::addr] wherever possible.
+    /// Due to its inherent ambiguity, [`with_exposed_provenance_mut`] may not be supported by tools
+    /// that help you to stay conformant with the Rust memory model. It is recommended to use
+    /// [Strict Provenance][crate::ptr#strict-provenance] APIs such as [`with_addr`][pointer::with_addr]
+    /// wherever possible, in which case [`addr`][pointer::addr] should be used instead of `expose_provenance`.
     ///
     /// On most platforms this will produce a value with the same bytes as the original pointer,
     /// because all the bytes are dedicated to describing the address. Platforms which need to store
     /// additional information in the pointer may not support this operation, since the 'expose'
-    /// side-effect which is required for [`with_exposed_provenance_mut`][] to work is typically not
+    /// side-effect which is required for [`with_exposed_provenance_mut`] to work is typically not
     /// available.
     ///
-    /// It is unclear whether this method can be given a satisfying unambiguous specification. This
-    /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance].
+    /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
     ///
     /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut
     #[inline(always)]
-    #[unstable(feature = "exposed_provenance", issue = "95228")]
+    #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn expose_provenance(self) -> usize {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
         self.cast::<()>() as usize
     }
 
-    /// Creates a new pointer with the given address.
+    /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of
+    /// `self`.
     ///
-    /// This performs the same operation as an `addr as ptr` cast, but copies
-    /// the *address-space* and *provenance* of `self` to the new pointer.
-    /// This allows us to dynamically preserve and propagate this important
-    /// information in a way that is otherwise impossible with a unary cast.
+    /// This is similar to a `addr as *mut T` cast, but copies
+    /// the *provenance* of `self` to the new pointer.
+    /// This avoids the inherent ambiguity of the unary cast.
     ///
     /// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset
     /// `self` to the given address, and therefore has all the same capabilities and restrictions.
     ///
-    /// This API and its claimed semantics are an extension to the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn with_addr(self, addr: usize) -> Self {
-        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
-        //
-        // In the mean-time, this operation is defined to be "as if" it was
-        // a wrapping_offset, so we can emulate it as such. This should properly
-        // restore pointer provenance even under today's compiler.
+        // This should probably be an intrinsic to avoid doing any sort of arithmetic, but
+        // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's
+        // provenance.
         let self_addr = self.addr() as isize;
         let dest_addr = addr as isize;
         let offset = dest_addr.wrapping_sub(self_addr);
-
-        // This is the canonical desugaring of this operation
         self.wrapping_byte_offset(offset)
     }
 
-    /// Creates a new pointer by mapping `self`'s address to a new one.
+    /// Creates a new pointer by mapping `self`'s address to a new one, preserving the original
+    /// pointer's [provenance][crate::ptr#provenance].
     ///
     /// This is a convenience for [`with_addr`][pointer::with_addr], see that method for details.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
         self.with_addr(f(self.addr()))
     }
@@ -376,7 +367,7 @@ impl<T: ?Sized> *mut T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
     ///   [allocated object], and the entire memory range between `self` and the result must be in
     ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
     ///   of the address space.
@@ -558,7 +549,7 @@ impl<T: ?Sized> *mut T {
     /// ## Examples
     ///
     /// ```
-    /// #![feature(ptr_mask, strict_provenance)]
+    /// #![feature(ptr_mask)]
     /// let mut v = 17_u32;
     /// let ptr: *mut u32 = &mut v;
     ///
@@ -777,7 +768,7 @@ impl<T: ?Sized> *mut T {
     /// * `self` and `origin` must either
     ///
     ///   * point to the same address, or
-    ///   * both be *derived from* a pointer to the same [allocated object], and the memory range between
+    ///   * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between
     ///     the two pointers must be in bounds of that object. (See below for an example.)
     ///
     /// * The distance between the pointers, in bytes, must be an exact multiple
@@ -954,7 +945,7 @@ impl<T: ?Sized> *mut T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
     ///   [allocated object], and the entire memory range between `self` and the result must be in
     ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
     ///   of the address space.
@@ -1061,7 +1052,7 @@ impl<T: ?Sized> *mut T {
     /// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
     ///   "wrapping around"), must fit in an `isize`.
     ///
-    /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
+    /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
     ///   [allocated object], and the entire memory range between `self` and the result must be in
     ///   bounds of that allocated object. In particular, this range must not "wrap around" the edge
     ///   of the address space.
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 3e4cae2b3ca..d91bbe1a5a1 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -283,40 +283,39 @@ impl<T: ?Sized> NonNull<T> {
     ///
     /// For more details see the equivalent method on a raw pointer, [`pointer::addr`].
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [`ptr` module documentation][crate::ptr].
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn addr(self) -> NonZero<usize> {
         // SAFETY: The pointer is guaranteed by the type to be non-null,
         // meaning that the address will be non-zero.
         unsafe { NonZero::new_unchecked(self.pointer.addr()) }
     }
 
-    /// Creates a new pointer with the given address.
+    /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of
+    /// `self`.
     ///
     /// For more details see the equivalent method on a raw pointer, [`pointer::with_addr`].
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [`ptr` module documentation][crate::ptr].
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn with_addr(self, addr: NonZero<usize>) -> Self {
         // SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero.
         unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) }
     }
 
-    /// Creates a new pointer by mapping `self`'s address to a new one.
+    /// Creates a new pointer by mapping `self`'s address to a new one, preserving the
+    /// [provenance][crate::ptr#provenance] of `self`.
     ///
     /// For more details see the equivalent method on a raw pointer, [`pointer::map_addr`].
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [`ptr` module documentation][crate::ptr].
+    /// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
     #[must_use]
     #[inline]
-    #[unstable(feature = "strict_provenance", issue = "95228")]
+    #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
     pub fn map_addr(self, f: impl FnOnce(NonZero<usize>) -> NonZero<usize>) -> Self {
         self.with_addr(f(self.addr()))
     }
@@ -749,7 +748,6 @@ impl<T: ?Sized> NonNull<T> {
     /// *Incorrect* usage:
     ///
     /// ```rust,no_run
-    /// #![feature(strict_provenance)]
     /// use std::ptr::NonNull;
     ///
     /// let ptr1 = NonNull::new(Box::into_raw(Box::new(0u8))).unwrap();
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index 42b68e28273..17ba18c2a66 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -1758,7 +1758,7 @@ impl<T> AtomicPtr<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
+    /// #![feature(strict_provenance_atomic_ptr)]
     /// use core::sync::atomic::{AtomicPtr, Ordering};
     ///
     /// let atom = AtomicPtr::<i64>::new(core::ptr::null_mut());
@@ -1838,7 +1838,7 @@ impl<T> AtomicPtr<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
+    /// #![feature(strict_provenance_atomic_ptr)]
     /// use core::sync::atomic::{AtomicPtr, Ordering};
     ///
     /// let atom = AtomicPtr::<i64>::new(core::ptr::null_mut());
@@ -1874,7 +1874,7 @@ impl<T> AtomicPtr<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
+    /// #![feature(strict_provenance_atomic_ptr)]
     /// use core::sync::atomic::{AtomicPtr, Ordering};
     ///
     /// let atom = AtomicPtr::<i64>::new(core::ptr::without_provenance_mut(1));
@@ -1919,7 +1919,7 @@ impl<T> AtomicPtr<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
+    /// #![feature(strict_provenance_atomic_ptr)]
     /// use core::sync::atomic::{AtomicPtr, Ordering};
     ///
     /// let pointer = &mut 3i64 as *mut i64;
@@ -1970,7 +1970,7 @@ impl<T> AtomicPtr<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
+    /// #![feature(strict_provenance_atomic_ptr)]
     /// use core::sync::atomic::{AtomicPtr, Ordering};
     ///
     /// let pointer = &mut 3i64 as *mut i64;
@@ -2020,7 +2020,7 @@ impl<T> AtomicPtr<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
+    /// #![feature(strict_provenance_atomic_ptr)]
     /// use core::sync::atomic::{AtomicPtr, Ordering};
     ///
     /// let pointer = &mut 3i64 as *mut i64;
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 443090097c0..5d6921845d0 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -1,4 +1,6 @@
 // tidy-alphabetical-start
+#![cfg_attr(bootstrap, feature(strict_provenance))]
+#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
 #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
 #![cfg_attr(test, feature(cfg_match))]
 #![feature(alloc_layout_extra)]
@@ -85,7 +87,6 @@
 #![feature(std_internals)]
 #![feature(step_trait)]
 #![feature(str_internals)]
-#![feature(strict_provenance)]
 #![feature(strict_provenance_atomic_ptr)]
 #![feature(test)]
 #![feature(trait_upcasting)]
diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs
index 6cd4dffb8aa..1981675f409 100644
--- a/library/panic_unwind/src/lib.rs
+++ b/library/panic_unwind/src/lib.rs
@@ -19,8 +19,6 @@
 #![feature(panic_unwind)]
 #![feature(staged_api)]
 #![feature(std_internals)]
-#![feature(strict_provenance)]
-#![feature(exposed_provenance)]
 #![feature(rustc_attrs)]
 #![panic_runtime]
 #![feature(panic_runtime)]
diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs
index cc6246b4a0d..992a7705e3c 100644
--- a/library/portable-simd/crates/core_simd/src/lib.rs
+++ b/library/portable-simd/crates/core_simd/src/lib.rs
@@ -9,7 +9,6 @@
     repr_simd,
     simd_ffi,
     staged_api,
-    strict_provenance,
     prelude_import,
     ptr_metadata
 )]
diff --git a/library/portable-simd/crates/core_simd/tests/pointers.rs b/library/portable-simd/crates/core_simd/tests/pointers.rs
index 90bfc5d5fd6..d7db4e82b3c 100644
--- a/library/portable-simd/crates/core_simd/tests/pointers.rs
+++ b/library/portable-simd/crates/core_simd/tests/pointers.rs
@@ -1,4 +1,4 @@
-#![feature(portable_simd, strict_provenance, exposed_provenance)]
+#![feature(portable_simd)]
 
 use core_simd::simd::{
     ptr::{SimdConstPtr, SimdMutPtr},
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index 72b597a8083..ae47bb7adf4 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -32,7 +32,6 @@
 #![feature(restricted_std)]
 #![feature(rustc_attrs)]
 #![feature(min_specialization)]
-#![feature(strict_provenance)]
 #![recursion_limit = "256"]
 #![allow(internal_features)]
 #![deny(ffi_unwind_calls)]
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 675140ff18f..8a0d2a7f5cf 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -2618,7 +2618,7 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// # Platform-specific behavior
 ///
 /// This function currently corresponds to the `opendir` function on Unix
-/// and the `FindFirstFile` function on Windows. Advancing the iterator
+/// and the `FindFirstFileEx` function on Windows. Advancing the iterator
 /// currently corresponds to `readdir` on Unix and `FindNextFile` on Windows.
 /// Note that, this [may change in the future][changes].
 ///
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 3ab65238368..8c1a3c76829 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -279,6 +279,8 @@
 //
 // Language features:
 // tidy-alphabetical-start
+#![cfg_attr(bootstrap, feature(strict_provenance))]
+#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
 #![feature(alloc_error_handler)]
 #![feature(allocator_internals)]
 #![feature(allow_internal_unsafe)]
@@ -336,7 +338,6 @@
 #![feature(error_iter)]
 #![feature(exact_size_is_empty)]
 #![feature(exclusive_wrapper)]
-#![feature(exposed_provenance)]
 #![feature(extend_one)]
 #![feature(float_gamma)]
 #![feature(float_minimum_maximum)]
@@ -362,7 +363,6 @@
 #![feature(slice_range)]
 #![feature(std_internals)]
 #![feature(str_internals)]
-#![feature(strict_provenance)]
 #![feature(strict_provenance_atomic_ptr)]
 #![feature(ub_checks)]
 // tidy-alphabetical-end
diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt
index 9c2e4500da0..192c95fd203 100644
--- a/library/std/src/sys/pal/windows/c/bindings.txt
+++ b/library/std/src/sys/pal/windows/c/bindings.txt
@@ -2337,7 +2337,9 @@ Windows.Win32.Storage.FileSystem.FileStandardInfo
 Windows.Win32.Storage.FileSystem.FileStorageInfo
 Windows.Win32.Storage.FileSystem.FileStreamInfo
 Windows.Win32.Storage.FileSystem.FindClose
-Windows.Win32.Storage.FileSystem.FindFirstFileW
+Windows.Win32.Storage.FileSystem.FindExInfoBasic
+Windows.Win32.Storage.FileSystem.FindExSearchNameMatch
+Windows.Win32.Storage.FileSystem.FindFirstFileExW
 Windows.Win32.Storage.FileSystem.FindNextFileW
 Windows.Win32.Storage.FileSystem.FlushFileBuffers
 Windows.Win32.Storage.FileSystem.GetFileAttributesW
diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs
index ab5f8919d7a..52444c2c009 100644
--- a/library/std/src/sys/pal/windows/c/windows_sys.rs
+++ b/library/std/src/sys/pal/windows/c/windows_sys.rs
@@ -26,7 +26,7 @@ windows_targets::link!("kernel32.dll" "system" fn DeviceIoControl(hdevice : HAND
 windows_targets::link!("kernel32.dll" "system" fn DuplicateHandle(hsourceprocesshandle : HANDLE, hsourcehandle : HANDLE, htargetprocesshandle : HANDLE, lptargethandle : *mut HANDLE, dwdesiredaccess : u32, binherithandle : BOOL, dwoptions : DUPLICATE_HANDLE_OPTIONS) -> BOOL);
 windows_targets::link!("kernel32.dll" "system" fn ExitProcess(uexitcode : u32) -> !);
 windows_targets::link!("kernel32.dll" "system" fn FindClose(hfindfile : HANDLE) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn FindFirstFileW(lpfilename : PCWSTR, lpfindfiledata : *mut WIN32_FIND_DATAW) -> HANDLE);
+windows_targets::link!("kernel32.dll" "system" fn FindFirstFileExW(lpfilename : PCWSTR, finfolevelid : FINDEX_INFO_LEVELS, lpfindfiledata : *mut core::ffi::c_void, fsearchop : FINDEX_SEARCH_OPS, lpsearchfilter : *const core::ffi::c_void, dwadditionalflags : FIND_FIRST_EX_FLAGS) -> HANDLE);
 windows_targets::link!("kernel32.dll" "system" fn FindNextFileW(hfindfile : HANDLE, lpfindfiledata : *mut WIN32_FIND_DATAW) -> BOOL);
 windows_targets::link!("kernel32.dll" "system" fn FlushFileBuffers(hfile : HANDLE) -> BOOL);
 windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32);
@@ -2501,6 +2501,9 @@ pub const FILE_WRITE_ATTRIBUTES: FILE_ACCESS_RIGHTS = 256u32;
 pub const FILE_WRITE_DATA: FILE_ACCESS_RIGHTS = 2u32;
 pub const FILE_WRITE_EA: FILE_ACCESS_RIGHTS = 16u32;
 pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32;
+pub type FINDEX_INFO_LEVELS = i32;
+pub type FINDEX_SEARCH_OPS = i32;
+pub type FIND_FIRST_EX_FLAGS = u32;
 pub const FIONBIO: i32 = -2147195266i32;
 #[repr(C)]
 #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
@@ -2565,6 +2568,8 @@ pub const FileRenameInfoEx: FILE_INFO_BY_HANDLE_CLASS = 22i32;
 pub const FileStandardInfo: FILE_INFO_BY_HANDLE_CLASS = 1i32;
 pub const FileStorageInfo: FILE_INFO_BY_HANDLE_CLASS = 16i32;
 pub const FileStreamInfo: FILE_INFO_BY_HANDLE_CLASS = 7i32;
+pub const FindExInfoBasic: FINDEX_INFO_LEVELS = 1i32;
+pub const FindExSearchNameMatch: FINDEX_SEARCH_OPS = 0i32;
 pub type GENERIC_ACCESS_RIGHTS = u32;
 pub const GENERIC_ALL: GENERIC_ACCESS_RIGHTS = 268435456u32;
 pub const GENERIC_EXECUTE: GENERIC_ACCESS_RIGHTS = 536870912u32;
@@ -3307,7 +3312,6 @@ pub struct XSAVE_FORMAT {
     pub XmmRegisters: [M128A; 8],
     pub Reserved4: [u8; 224],
 }
-
 #[cfg(target_arch = "arm")]
 #[repr(C)]
 pub struct WSADATA {
diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs
index aab471e28ea..b237fa481e2 100644
--- a/library/std/src/sys/pal/windows/fs.rs
+++ b/library/std/src/sys/pal/windows/fs.rs
@@ -114,7 +114,7 @@ impl Iterator for ReadDir {
     fn next(&mut self) -> Option<io::Result<DirEntry>> {
         if self.handle.0 == c::INVALID_HANDLE_VALUE {
             // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle.
-            // Simply return `None` because this is only the case when `FindFirstFileW` in
+            // Simply return `None` because this is only the case when `FindFirstFileExW` in
             // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means
             // no matchhing files can be found.
             return None;
@@ -1047,8 +1047,22 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
     let path = maybe_verbatim(&star)?;
 
     unsafe {
-        let mut wfd = mem::zeroed();
-        let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
+        let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed();
+        // this is like FindFirstFileW (see https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexw),
+        // but with FindExInfoBasic it should skip filling WIN32_FIND_DATAW.cAlternateFileName
+        // (see https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw)
+        // (which will be always null string value and currently unused) and should be faster.
+        //
+        // We can pass FIND_FIRST_EX_LARGE_FETCH to dwAdditionalFlags to speed up things more,
+        // but as we don't know user's use profile of this function, lets be conservative.
+        let find_handle = c::FindFirstFileExW(
+            path.as_ptr(),
+            c::FindExInfoBasic,
+            &mut wfd as *mut _ as _,
+            c::FindExSearchNameMatch,
+            ptr::null(),
+            0,
+        );
 
         if find_handle != c::INVALID_HANDLE_VALUE {
             Ok(ReadDir {
@@ -1057,7 +1071,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
                 first: Some(wfd),
             })
         } else {
-            // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileW` function
+            // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function
             // if no matching files can be found, but not necessarily that the path to find the
             // files in does not exist.
             //
@@ -1079,7 +1093,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
 
             // Just return the error constructed from the raw OS error if the above is not the case.
             //
-            // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileW` function
+            // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileExW` function
             // when the path to search in does not exist in the first place.
             Err(Error::from_raw_os_error(last_error.code as i32))
         }
@@ -1220,7 +1234,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag());
 
     // Attempt to open the file normally.
-    // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`.
+    // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`.
     // If the fallback fails for any reason we return the original error.
     match File::open(path, &opts) {
         Ok(file) => file.file_attr(),
@@ -1237,13 +1251,20 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
             unsafe {
                 let path = maybe_verbatim(path)?;
 
-                // `FindFirstFileW` accepts wildcard file names.
+                // `FindFirstFileExW` accepts wildcard file names.
                 // Fortunately wildcards are not valid file names and
                 // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
                 // therefore it's safe to assume the file name given does not
                 // include wildcards.
-                let mut wfd = mem::zeroed();
-                let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
+                let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed();
+                let handle = c::FindFirstFileExW(
+                    path.as_ptr(),
+                    c::FindExInfoBasic,
+                    &mut wfd as *mut _ as _,
+                    c::FindExSearchNameMatch,
+                    ptr::null(),
+                    0,
+                );
 
                 if handle == c::INVALID_HANDLE_VALUE {
                     // This can fail if the user does not have read access to the
@@ -1253,7 +1274,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
                     // We no longer need the find handle.
                     c::FindClose(handle);
 
-                    // `FindFirstFileW` reads the cached file information from the
+                    // `FindFirstFileExW` reads the cached file information from the
                     // directory. The downside is that this metadata may be outdated.
                     let attrs = FileAttr::from(wfd);
                     if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() {
diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs
index 5a476d5843b..79baa5b0b83 100644
--- a/library/unwind/src/lib.rs
+++ b/library/unwind/src/lib.rs
@@ -2,7 +2,6 @@
 #![unstable(feature = "panic_unwind", issue = "32837")]
 #![feature(link_cfg)]
 #![feature(staged_api)]
-#![feature(strict_provenance)]
 #![cfg_attr(not(target_env = "msvc"), feature(libc))]
 #![cfg_attr(
     all(target_family = "wasm", not(target_os = "emscripten")),
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
new file mode 100644
index 00000000000..c3d0994f164
--- /dev/null
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -0,0 +1,1220 @@
+use std::env;
+use std::ffi::{OsStr, OsString};
+use std::path::{Path, PathBuf};
+
+use super::{Builder, Kind};
+use crate::core::build_steps::tool::SourceType;
+use crate::core::build_steps::{compile, test};
+use crate::core::config::SplitDebuginfo;
+use crate::core::config::flags::Color;
+use crate::utils::helpers::{
+    self, LldThreads, add_link_lib_path, check_cfg_arg, linker_args, linker_flags,
+};
+use crate::{
+    BootstrapCommand, CLang, Compiler, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode,
+    TargetSelection, command, prepare_behaviour_dump_dir, t,
+};
+
+/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler
+/// later.
+///
+/// `-Z crate-attr` flags will be applied recursively on the target code using the
+/// `rustc_parse::parser::Parser`. See `rustc_builtin_macros::cmdline_attrs::inject` for more
+/// information.
+#[derive(Debug, Clone)]
+struct Rustflags(String, TargetSelection);
+
+impl Rustflags {
+    fn new(target: TargetSelection) -> Rustflags {
+        let mut ret = Rustflags(String::new(), target);
+        ret.propagate_cargo_env("RUSTFLAGS");
+        ret
+    }
+
+    /// By default, cargo will pick up on various variables in the environment. However, bootstrap
+    /// reuses those variables to pass additional flags to rustdoc, so by default they get
+    /// overridden. Explicitly add back any previous value in the environment.
+    ///
+    /// `prefix` is usually `RUSTFLAGS` or `RUSTDOCFLAGS`.
+    fn propagate_cargo_env(&mut self, prefix: &str) {
+        // Inherit `RUSTFLAGS` by default ...
+        self.env(prefix);
+
+        // ... and also handle target-specific env RUSTFLAGS if they're configured.
+        let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix);
+        self.env(&target_specific);
+    }
+
+    fn env(&mut self, env: &str) {
+        if let Ok(s) = env::var(env) {
+            for part in s.split(' ') {
+                self.arg(part);
+            }
+        }
+    }
+
+    fn arg(&mut self, arg: &str) -> &mut Self {
+        assert_eq!(arg.split(' ').count(), 1);
+        if !self.0.is_empty() {
+            self.0.push(' ');
+        }
+        self.0.push_str(arg);
+        self
+    }
+}
+
+/// Flags that are passed to the `rustc` shim binary. These flags will only be applied when
+/// compiling host code, i.e. when `--target` is unset.
+#[derive(Debug, Default)]
+struct HostFlags {
+    rustc: Vec<String>,
+}
+
+impl HostFlags {
+    const SEPARATOR: &'static str = " ";
+
+    /// Adds a host rustc flag.
+    fn arg<S: Into<String>>(&mut self, flag: S) {
+        let value = flag.into().trim().to_string();
+        assert!(!value.contains(Self::SEPARATOR));
+        self.rustc.push(value);
+    }
+
+    /// Encodes all the flags into a single string.
+    fn encode(self) -> String {
+        self.rustc.join(Self::SEPARATOR)
+    }
+}
+
+#[derive(Debug)]
+pub struct Cargo {
+    command: BootstrapCommand,
+    compiler: Compiler,
+    target: TargetSelection,
+    rustflags: Rustflags,
+    rustdocflags: Rustflags,
+    hostflags: HostFlags,
+    allow_features: String,
+}
+
+impl Cargo {
+    /// Calls [`Builder::cargo`] and [`Cargo::configure_linker`] to prepare an invocation of `cargo`
+    /// to be run.
+    pub fn new(
+        builder: &Builder<'_>,
+        compiler: Compiler,
+        mode: Mode,
+        source_type: SourceType,
+        target: TargetSelection,
+        cmd_kind: Kind,
+    ) -> Cargo {
+        let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind);
+
+        match cmd_kind {
+            // No need to configure the target linker for these command types,
+            // as they don't invoke rustc at all.
+            Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {}
+            _ => {
+                cargo.configure_linker(builder);
+            }
+        }
+
+        cargo
+    }
+
+    pub fn into_cmd(self) -> BootstrapCommand {
+        self.into()
+    }
+
+    /// Same as [`Cargo::new`] except this one doesn't configure the linker with
+    /// [`Cargo::configure_linker`].
+    pub fn new_for_mir_opt_tests(
+        builder: &Builder<'_>,
+        compiler: Compiler,
+        mode: Mode,
+        source_type: SourceType,
+        target: TargetSelection,
+        cmd_kind: Kind,
+    ) -> Cargo {
+        builder.cargo(compiler, mode, source_type, target, cmd_kind)
+    }
+
+    pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo {
+        self.rustdocflags.arg(arg);
+        self
+    }
+
+    pub fn rustflag(&mut self, arg: &str) -> &mut Cargo {
+        self.rustflags.arg(arg);
+        self
+    }
+
+    pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Cargo {
+        self.command.arg(arg.as_ref());
+        self
+    }
+
+    pub fn args<I, S>(&mut self, args: I) -> &mut Cargo
+    where
+        I: IntoIterator<Item = S>,
+        S: AsRef<OsStr>,
+    {
+        for arg in args {
+            self.arg(arg.as_ref());
+        }
+        self
+    }
+
+    /// Add an env var to the cargo command instance. Note that `RUSTFLAGS`/`RUSTDOCFLAGS` must go
+    /// through [`Cargo::rustdocflags`] and [`Cargo::rustflags`] because inconsistent `RUSTFLAGS`
+    /// and `RUSTDOCFLAGS` usages will trigger spurious rebuilds.
+    pub fn env(&mut self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) -> &mut Cargo {
+        assert_ne!(key.as_ref(), "RUSTFLAGS");
+        assert_ne!(key.as_ref(), "RUSTDOCFLAGS");
+        self.command.env(key.as_ref(), value.as_ref());
+        self
+    }
+
+    pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) {
+        builder.add_rustc_lib_path(self.compiler, &mut self.command);
+    }
+
+    pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo {
+        self.command.current_dir(dir);
+        self
+    }
+
+    /// Adds nightly-only features that this invocation is allowed to use.
+    ///
+    /// By default, all nightly features are allowed. Once this is called, it will be restricted to
+    /// the given set.
+    pub fn allow_features(&mut self, features: &str) -> &mut Cargo {
+        if !self.allow_features.is_empty() {
+            self.allow_features.push(',');
+        }
+        self.allow_features.push_str(features);
+        self
+    }
+
+    fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo {
+        let target = self.target;
+        let compiler = self.compiler;
+
+        // Dealing with rpath here is a little special, so let's go into some
+        // detail. First off, `-rpath` is a linker option on Unix platforms
+        // which adds to the runtime dynamic loader path when looking for
+        // dynamic libraries. We use this by default on Unix platforms to ensure
+        // that our nightlies behave the same on Windows, that is they work out
+        // of the box. This can be disabled by setting `rpath = false` in `[rust]`
+        // table of `config.toml`
+        //
+        // Ok, so the astute might be wondering "why isn't `-C rpath` used
+        // here?" and that is indeed a good question to ask. This codegen
+        // option is the compiler's current interface to generating an rpath.
+        // Unfortunately it doesn't quite suffice for us. The flag currently
+        // takes no value as an argument, so the compiler calculates what it
+        // should pass to the linker as `-rpath`. This unfortunately is based on
+        // the **compile time** directory structure which when building with
+        // Cargo will be very different than the runtime directory structure.
+        //
+        // All that's a really long winded way of saying that if we use
+        // `-Crpath` then the executables generated have the wrong rpath of
+        // something like `$ORIGIN/deps` when in fact the way we distribute
+        // rustc requires the rpath to be `$ORIGIN/../lib`.
+        //
+        // So, all in all, to set up the correct rpath we pass the linker
+        // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it
+        // fun to pass a flag to a tool to pass a flag to pass a flag to a tool
+        // to change a flag in a binary?
+        if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) {
+            let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap();
+            let rpath = if target.contains("apple") {
+                // Note that we need to take one extra step on macOS to also pass
+                // `-Wl,-instal_name,@rpath/...` to get things to work right. To
+                // do that we pass a weird flag to the compiler to get it to do
+                // so. Note that this is definitely a hack, and we should likely
+                // flesh out rpath support more fully in the future.
+                self.rustflags.arg("-Zosx-rpath-install-name");
+                Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
+            } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") {
+                self.rustflags.arg("-Clink-args=-Wl,-z,origin");
+                Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
+            } else {
+                None
+            };
+            if let Some(rpath) = rpath {
+                self.rustflags.arg(&format!("-Clink-args={rpath}"));
+            }
+        }
+
+        for arg in linker_args(builder, compiler.host, LldThreads::Yes) {
+            self.hostflags.arg(&arg);
+        }
+
+        if let Some(target_linker) = builder.linker(target) {
+            let target = crate::envify(&target.triple);
+            self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker);
+        }
+        // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not
+        // `linker_args` here.
+        for flag in linker_flags(builder, target, LldThreads::Yes) {
+            self.rustflags.arg(&flag);
+        }
+        for arg in linker_args(builder, target, LldThreads::Yes) {
+            self.rustdocflags.arg(&arg);
+        }
+
+        if !builder.config.dry_run()
+            && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz")
+        {
+            self.rustflags.arg("-Clink-arg=-gz");
+        }
+
+        // Throughout the build Cargo can execute a number of build scripts
+        // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
+        // obtained previously to those build scripts.
+        // Build scripts use either the `cc` crate or `configure/make` so we pass
+        // the options through environment variables that are fetched and understood by both.
+        //
+        // FIXME: the guard against msvc shouldn't need to be here
+        if target.is_msvc() {
+            if let Some(ref cl) = builder.config.llvm_clang_cl {
+                // FIXME: There is a bug in Clang 18 when building for ARM64:
+                // https://github.com/llvm/llvm-project/pull/81849. This is
+                // fixed in LLVM 19, but can't be backported.
+                if !target.starts_with("aarch64") && !target.starts_with("arm64ec") {
+                    self.command.env("CC", cl).env("CXX", cl);
+                }
+            }
+        } else {
+            let ccache = builder.config.ccache.as_ref();
+            let ccacheify = |s: &Path| {
+                let ccache = match ccache {
+                    Some(ref s) => s,
+                    None => return s.display().to_string(),
+                };
+                // FIXME: the cc-rs crate only recognizes the literal strings
+                // `ccache` and `sccache` when doing caching compilations, so we
+                // mirror that here. It should probably be fixed upstream to
+                // accept a new env var or otherwise work with custom ccache
+                // vars.
+                match &ccache[..] {
+                    "ccache" | "sccache" => format!("{} {}", ccache, s.display()),
+                    _ => s.display().to_string(),
+                }
+            };
+            let triple_underscored = target.triple.replace('-', "_");
+            let cc = ccacheify(&builder.cc(target));
+            self.command.env(format!("CC_{triple_underscored}"), &cc);
+
+            let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" ");
+            self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags);
+
+            if let Some(ar) = builder.ar(target) {
+                let ranlib = format!("{} s", ar.display());
+                self.command
+                    .env(format!("AR_{triple_underscored}"), ar)
+                    .env(format!("RANLIB_{triple_underscored}"), ranlib);
+            }
+
+            if let Ok(cxx) = builder.cxx(target) {
+                let cxx = ccacheify(&cxx);
+                let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ");
+                self.command
+                    .env(format!("CXX_{triple_underscored}"), &cxx)
+                    .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags);
+            }
+        }
+
+        self
+    }
+}
+
+impl From<Cargo> for BootstrapCommand {
+    fn from(mut cargo: Cargo) -> BootstrapCommand {
+        let rustflags = &cargo.rustflags.0;
+        if !rustflags.is_empty() {
+            cargo.command.env("RUSTFLAGS", rustflags);
+        }
+
+        let rustdocflags = &cargo.rustdocflags.0;
+        if !rustdocflags.is_empty() {
+            cargo.command.env("RUSTDOCFLAGS", rustdocflags);
+        }
+
+        let encoded_hostflags = cargo.hostflags.encode();
+        if !encoded_hostflags.is_empty() {
+            cargo.command.env("RUSTC_HOST_FLAGS", encoded_hostflags);
+        }
+
+        if !cargo.allow_features.is_empty() {
+            cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
+        }
+        cargo.command
+    }
+}
+
+impl Builder<'_> {
+    /// Like [`Builder::cargo`], but only passes flags that are valid for all commands.
+    pub fn bare_cargo(
+        &self,
+        compiler: Compiler,
+        mode: Mode,
+        target: TargetSelection,
+        cmd_kind: Kind,
+    ) -> BootstrapCommand {
+        let mut cargo = match cmd_kind {
+            Kind::Clippy => {
+                let mut cargo = self.cargo_clippy_cmd(compiler);
+                cargo.arg(cmd_kind.as_str());
+                cargo
+            }
+            Kind::MiriSetup => {
+                let mut cargo = self.cargo_miri_cmd(compiler);
+                cargo.arg("miri").arg("setup");
+                cargo
+            }
+            Kind::MiriTest => {
+                let mut cargo = self.cargo_miri_cmd(compiler);
+                cargo.arg("miri").arg("test");
+                cargo
+            }
+            _ => {
+                let mut cargo = command(&self.initial_cargo);
+                cargo.arg(cmd_kind.as_str());
+                cargo
+            }
+        };
+
+        // Run cargo from the source root so it can find .cargo/config.
+        // This matters when using vendoring and the working directory is outside the repository.
+        cargo.current_dir(&self.src);
+
+        let out_dir = self.stage_out(compiler, mode);
+        cargo.env("CARGO_TARGET_DIR", &out_dir);
+
+        // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger`
+        // from out of tree it shouldn't matter, since x.py is only used for
+        // building in-tree.
+        let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"];
+        match self.build.config.color {
+            Color::Always => {
+                cargo.arg("--color=always");
+                for log in &color_logs {
+                    cargo.env(log, "always");
+                }
+            }
+            Color::Never => {
+                cargo.arg("--color=never");
+                for log in &color_logs {
+                    cargo.env(log, "never");
+                }
+            }
+            Color::Auto => {} // nothing to do
+        }
+
+        if cmd_kind != Kind::Install {
+            cargo.arg("--target").arg(target.rustc_target_arg());
+        } else {
+            assert_eq!(target, compiler.host);
+        }
+
+        if self.config.rust_optimize.is_release() &&
+        // cargo bench/install do not accept `--release` and miri doesn't want it
+        !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest)
+        {
+            cargo.arg("--release");
+        }
+
+        // Remove make-related flags to ensure Cargo can correctly set things up
+        cargo.env_remove("MAKEFLAGS");
+        cargo.env_remove("MFLAGS");
+
+        cargo
+    }
+
+    /// This will create a [`BootstrapCommand`] that represents a pending execution of cargo. This
+    /// cargo will be configured to use `compiler` as the actual rustc compiler, its output will be
+    /// scoped by `mode`'s output directory, it will pass the `--target` flag for the specified
+    /// `target`, and will be executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for
+    /// commands to be run with Miri.
+    fn cargo(
+        &self,
+        compiler: Compiler,
+        mode: Mode,
+        source_type: SourceType,
+        target: TargetSelection,
+        cmd_kind: Kind,
+    ) -> Cargo {
+        let mut cargo = self.bare_cargo(compiler, mode, target, cmd_kind);
+        let out_dir = self.stage_out(compiler, mode);
+
+        let mut hostflags = HostFlags::default();
+
+        // Codegen backends are not yet tracked by -Zbinary-dep-depinfo,
+        // so we need to explicitly clear out if they've been updated.
+        for backend in self.codegen_backends(compiler) {
+            self.clear_if_dirty(&out_dir, &backend);
+        }
+
+        if cmd_kind == Kind::Doc {
+            let my_out = match mode {
+                // This is the intended out directory for compiler documentation.
+                Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target),
+                Mode::Std => {
+                    if self.config.cmd.json() {
+                        out_dir.join(target).join("json-doc")
+                    } else {
+                        out_dir.join(target).join("doc")
+                    }
+                }
+                _ => panic!("doc mode {mode:?} not expected"),
+            };
+            let rustdoc = self.rustdoc(compiler);
+            self.clear_if_dirty(&my_out, &rustdoc);
+        }
+
+        let profile_var = |name: &str| {
+            let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" };
+            format!("CARGO_PROFILE_{}_{}", profile, name)
+        };
+
+        // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config
+        // needs to not accidentally link to libLLVM in stage0/lib.
+        cargo.env("REAL_LIBRARY_PATH_VAR", helpers::dylib_path_var());
+        if let Some(e) = env::var_os(helpers::dylib_path_var()) {
+            cargo.env("REAL_LIBRARY_PATH", e);
+        }
+
+        // Set a flag for `check`/`clippy`/`fix`, so that certain build
+        // scripts can do less work (i.e. not building/requiring LLVM).
+        if matches!(cmd_kind, Kind::Check | Kind::Clippy | Kind::Fix) {
+            // If we've not yet built LLVM, or it's stale, then bust
+            // the rustc_llvm cache. That will always work, even though it
+            // may mean that on the next non-check build we'll need to rebuild
+            // rustc_llvm. But if LLVM is stale, that'll be a tiny amount
+            // of work comparatively, and we'd likely need to rebuild it anyway,
+            // so that's okay.
+            if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target, false)
+                .should_build()
+            {
+                cargo.env("RUST_CHECK", "1");
+            }
+        }
+
+        let stage = if compiler.stage == 0 && self.local_rebuild {
+            // Assume the local-rebuild rustc already has stage1 features.
+            1
+        } else {
+            compiler.stage
+        };
+
+        // We synthetically interpret a stage0 compiler used to build tools as a
+        // "raw" compiler in that it's the exact snapshot we download. Normally
+        // the stage0 build means it uses libraries build by the stage0
+        // compiler, but for tools we just use the precompiled libraries that
+        // we've downloaded
+        let use_snapshot = mode == Mode::ToolBootstrap;
+        assert!(!use_snapshot || stage == 0 || self.local_rebuild);
+
+        let maybe_sysroot = self.sysroot(compiler);
+        let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
+        let libdir = self.rustc_libdir(compiler);
+
+        let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8");
+        if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) {
+            println!("using sysroot {sysroot_str}");
+        }
+
+        let mut rustflags = Rustflags::new(target);
+        if stage != 0 {
+            if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
+                cargo.args(s.split_whitespace());
+            }
+            rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP");
+        } else {
+            if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") {
+                cargo.args(s.split_whitespace());
+            }
+            rustflags.env("RUSTFLAGS_BOOTSTRAP");
+            rustflags.arg("--cfg=bootstrap");
+        }
+
+        if cmd_kind == Kind::Clippy {
+            // clippy overwrites sysroot if we pass it to cargo.
+            // Pass it directly to clippy instead.
+            // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
+            // so it has no way of knowing the sysroot.
+            rustflags.arg("--sysroot");
+            rustflags.arg(sysroot_str);
+        }
+
+        let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling {
+            Some(setting) => {
+                // If an explicit setting is given, use that
+                setting
+            }
+            None => {
+                if mode == Mode::Std {
+                    // The standard library defaults to the legacy scheme
+                    false
+                } else {
+                    // The compiler and tools default to the new scheme
+                    true
+                }
+            }
+        };
+
+        // By default, windows-rs depends on a native library that doesn't get copied into the
+        // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native
+        // library unnecessary. This can be removed when windows-rs enables raw-dylib
+        // unconditionally.
+        if let Mode::Rustc | Mode::ToolRustc = mode {
+            rustflags.arg("--cfg=windows_raw_dylib");
+        }
+
+        if use_new_symbol_mangling {
+            rustflags.arg("-Csymbol-mangling-version=v0");
+        } else {
+            rustflags.arg("-Csymbol-mangling-version=legacy");
+        }
+
+        // FIXME: the following components don't build with `-Zrandomize-layout` yet:
+        // - wasm-component-ld, due to the `wast`crate
+        // - rust-analyzer, due to the rowan crate
+        // so we exclude entire categories of steps here due to lack of fine-grained control over
+        // rustflags.
+        if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc {
+            rustflags.arg("-Zrandomize-layout");
+        }
+
+        // Enable compile-time checking of `cfg` names, values and Cargo `features`.
+        //
+        // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like
+        // backtrace, core_simd, std_float, ...), those dependencies have their own
+        // features but cargo isn't involved in the #[path] process and so cannot pass the
+        // complete list of features, so for that reason we don't enable checking of
+        // features for std crates.
+        if mode == Mode::Std {
+            rustflags.arg("--check-cfg=cfg(feature,values(any()))");
+        }
+
+        // Add extra cfg not defined in/by rustc
+        //
+        // Note: Although it would seems that "-Zunstable-options" to `rustflags` is useless as
+        // cargo would implicitly add it, it was discover that sometimes bootstrap only use
+        // `rustflags` without `cargo` making it required.
+        rustflags.arg("-Zunstable-options");
+        for (restricted_mode, name, values) in EXTRA_CHECK_CFGS {
+            if restricted_mode.is_none() || *restricted_mode == Some(mode) {
+                rustflags.arg(&check_cfg_arg(name, *values));
+            }
+        }
+
+        // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments
+        // to the host invocation here, but rather Cargo should know what flags to pass rustc
+        // itself.
+        if stage == 0 {
+            hostflags.arg("--cfg=bootstrap");
+        }
+        // Cargo doesn't pass RUSTFLAGS to proc_macros:
+        // https://github.com/rust-lang/cargo/issues/4423
+        // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
+        // We also declare that the flag is expected, which we need to do to not
+        // get warnings about it being unexpected.
+        hostflags.arg("-Zunstable-options");
+        hostflags.arg("--check-cfg=cfg(bootstrap)");
+
+        // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`,
+        // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See
+        // #71458.
+        let mut rustdocflags = rustflags.clone();
+        rustdocflags.propagate_cargo_env("RUSTDOCFLAGS");
+        if stage == 0 {
+            rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP");
+        } else {
+            rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP");
+        }
+
+        if let Ok(s) = env::var("CARGOFLAGS") {
+            cargo.args(s.split_whitespace());
+        }
+
+        match mode {
+            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}
+            Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
+                // Build proc macros both for the host and the target unless proc-macros are not
+                // supported by the target.
+                if target != compiler.host && cmd_kind != Kind::Check {
+                    let error = command(self.rustc(compiler))
+                        .arg("--target")
+                        .arg(target.rustc_target_arg())
+                        .arg("--print=file-names")
+                        .arg("--crate-type=proc-macro")
+                        .arg("-")
+                        .run_capture(self)
+                        .stderr();
+                    let not_supported = error
+                        .lines()
+                        .any(|line| line.contains("unsupported crate type `proc-macro`"));
+                    if !not_supported {
+                        cargo.arg("-Zdual-proc-macros");
+                        rustflags.arg("-Zdual-proc-macros");
+                    }
+                }
+            }
+        }
+
+        // This tells Cargo (and in turn, rustc) to output more complete
+        // dependency information.  Most importantly for bootstrap, this
+        // includes sysroot artifacts, like libstd, which means that we don't
+        // need to track those in bootstrap (an error prone process!). This
+        // feature is currently unstable as there may be some bugs and such, but
+        // it represents a big improvement in bootstrap's reliability on
+        // rebuilds, so we're using it here.
+        //
+        // For some additional context, see #63470 (the PR originally adding
+        // this), as well as #63012 which is the tracking issue for this
+        // feature on the rustc side.
+        cargo.arg("-Zbinary-dep-depinfo");
+        let allow_features = match mode {
+            Mode::ToolBootstrap | Mode::ToolStd => {
+                // Restrict the allowed features so we don't depend on nightly
+                // accidentally.
+                //
+                // binary-dep-depinfo is used by bootstrap itself for all
+                // compilations.
+                //
+                // Lots of tools depend on proc_macro2 and proc-macro-error.
+                // Those have build scripts which assume nightly features are
+                // available if the `rustc` version is "nighty" or "dev". See
+                // bin/rustc.rs for why that is a problem. Instead of labeling
+                // those features for each individual tool that needs them,
+                // just blanket allow them here.
+                //
+                // If this is ever removed, be sure to add something else in
+                // its place to keep the restrictions in place (or make a way
+                // to unset RUSTC_BOOTSTRAP).
+                "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic"
+                    .to_string()
+            }
+            Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(),
+        };
+
+        cargo.arg("-j").arg(self.jobs().to_string());
+
+        // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
+        // Force cargo to output binaries with disambiguating hashes in the name
+        let mut metadata = if compiler.stage == 0 {
+            // Treat stage0 like a special channel, whether it's a normal prior-
+            // release rustc or a local rebuild with the same version, so we
+            // never mix these libraries by accident.
+            "bootstrap".to_string()
+        } else {
+            self.config.channel.to_string()
+        };
+        // We want to make sure that none of the dependencies between
+        // std/test/rustc unify with one another. This is done for weird linkage
+        // reasons but the gist of the problem is that if librustc, libtest, and
+        // libstd all depend on libc from crates.io (which they actually do) we
+        // want to make sure they all get distinct versions. Things get really
+        // weird if we try to unify all these dependencies right now, namely
+        // around how many times the library is linked in dynamic libraries and
+        // such. If rustc were a static executable or if we didn't ship dylibs
+        // this wouldn't be a problem, but we do, so it is. This is in general
+        // just here to make sure things build right. If you can remove this and
+        // things still build right, please do!
+        match mode {
+            Mode::Std => metadata.push_str("std"),
+            // When we're building rustc tools, they're built with a search path
+            // that contains things built during the rustc build. For example,
+            // bitflags is built during the rustc build, and is a dependency of
+            // rustdoc as well. We're building rustdoc in a different target
+            // directory, though, which means that Cargo will rebuild the
+            // dependency. When we go on to build rustdoc, we'll look for
+            // bitflags, and find two different copies: one built during the
+            // rustc step and one that we just built. This isn't always a
+            // problem, somehow -- not really clear why -- but we know that this
+            // fixes things.
+            Mode::ToolRustc => metadata.push_str("tool-rustc"),
+            // Same for codegen backends.
+            Mode::Codegen => metadata.push_str("codegen"),
+            _ => {}
+        }
+        cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata);
+
+        if cmd_kind == Kind::Clippy {
+            rustflags.arg("-Zforce-unstable-if-unmarked");
+        }
+
+        rustflags.arg("-Zmacro-backtrace");
+
+        let want_rustdoc = self.doc_tests != DocTests::No;
+
+        // Clear the output directory if the real rustc we're using has changed;
+        // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc.
+        //
+        // Avoid doing this during dry run as that usually means the relevant
+        // compiler is not yet linked/copied properly.
+        //
+        // Only clear out the directory if we're compiling std; otherwise, we
+        // should let Cargo take care of things for us (via depdep info)
+        if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build {
+            self.clear_if_dirty(&out_dir, &self.rustc(compiler));
+        }
+
+        let rustdoc_path = match cmd_kind {
+            Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler),
+            _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"),
+        };
+
+        // Customize the compiler we're running. Specify the compiler to cargo
+        // as our shim and then pass it some various options used to configure
+        // how the actual compiler itself is called.
+        //
+        // These variables are primarily all read by
+        // src/bootstrap/bin/{rustc.rs,rustdoc.rs}
+        cargo
+            .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
+            .env("RUSTC_REAL", self.rustc(compiler))
+            .env("RUSTC_STAGE", stage.to_string())
+            .env("RUSTC_SYSROOT", sysroot)
+            .env("RUSTC_LIBDIR", libdir)
+            .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
+            .env("RUSTDOC_REAL", rustdoc_path)
+            .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
+            .env("RUSTC_BREAK_ON_ICE", "1");
+
+        // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree
+        // sysroot depending on whether we're building build scripts.
+        // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not
+        // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs.
+        cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc"));
+        // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:(
+        cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
+
+        // Someone might have set some previous rustc wrapper (e.g.
+        // sccache) before bootstrap overrode it. Respect that variable.
+        if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") {
+            cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper);
+        }
+
+        // If this is for `miri-test`, prepare the sysroots.
+        if cmd_kind == Kind::MiriTest {
+            self.ensure(compile::Std::new(compiler, compiler.host));
+            let host_sysroot = self.sysroot(compiler);
+            let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target);
+            cargo.env("MIRI_SYSROOT", &miri_sysroot);
+            cargo.env("MIRI_HOST_SYSROOT", &host_sysroot);
+        }
+
+        cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string());
+
+        if let Some(stack_protector) = &self.config.rust_stack_protector {
+            rustflags.arg(&format!("-Zstack-protector={stack_protector}"));
+        }
+
+        if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc
+        {
+            cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
+        }
+
+        let debuginfo_level = match mode {
+            Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
+            Mode::Std => self.config.rust_debuginfo_level_std,
+            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => {
+                self.config.rust_debuginfo_level_tools
+            }
+        };
+        cargo.env(profile_var("DEBUG"), debuginfo_level.to_string());
+        if let Some(opt_level) = &self.config.rust_optimize.get_opt_level() {
+            cargo.env(profile_var("OPT_LEVEL"), opt_level);
+        }
+        cargo.env(
+            profile_var("DEBUG_ASSERTIONS"),
+            if mode == Mode::Std {
+                self.config.rust_debug_assertions_std.to_string()
+            } else {
+                self.config.rust_debug_assertions.to_string()
+            },
+        );
+        cargo.env(
+            profile_var("OVERFLOW_CHECKS"),
+            if mode == Mode::Std {
+                self.config.rust_overflow_checks_std.to_string()
+            } else {
+                self.config.rust_overflow_checks.to_string()
+            },
+        );
+
+        match self.config.split_debuginfo(target) {
+            SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"),
+            SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"),
+            SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"),
+        };
+
+        if self.config.cmd.bless() {
+            // Bless `expect!` tests.
+            cargo.env("UPDATE_EXPECT", "1");
+        }
+
+        if !mode.is_tool() {
+            cargo.env("RUSTC_FORCE_UNSTABLE", "1");
+        }
+
+        if let Some(x) = self.crt_static(target) {
+            if x {
+                rustflags.arg("-Ctarget-feature=+crt-static");
+            } else {
+                rustflags.arg("-Ctarget-feature=-crt-static");
+            }
+        }
+
+        if let Some(x) = self.crt_static(compiler.host) {
+            let sign = if x { "+" } else { "-" };
+            hostflags.arg(format!("-Ctarget-feature={sign}crt-static"));
+        }
+
+        if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) {
+            let map = format!("{}={}", self.build.src.display(), map_to);
+            cargo.env("RUSTC_DEBUGINFO_MAP", map);
+
+            // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to,
+            // in order to opportunistically reverse it later.
+            cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to);
+        }
+
+        if self.config.rust_remap_debuginfo {
+            let mut env_var = OsString::new();
+            if self.config.vendor {
+                let vendor = self.build.src.join("vendor");
+                env_var.push(vendor);
+                env_var.push("=/rust/deps");
+            } else {
+                let registry_src = t!(home::cargo_home()).join("registry").join("src");
+                for entry in t!(std::fs::read_dir(registry_src)) {
+                    if !env_var.is_empty() {
+                        env_var.push("\t");
+                    }
+                    env_var.push(t!(entry).path());
+                    env_var.push("=/rust/deps");
+                }
+            }
+            cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var);
+        }
+
+        // Enable usage of unstable features
+        cargo.env("RUSTC_BOOTSTRAP", "1");
+
+        if self.config.dump_bootstrap_shims {
+            prepare_behaviour_dump_dir(self.build);
+
+            cargo
+                .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump"))
+                .env("BUILD_OUT", &self.build.out)
+                .env("CARGO_HOME", t!(home::cargo_home()));
+        };
+
+        self.add_rust_test_threads(&mut cargo);
+
+        // Almost all of the crates that we compile as part of the bootstrap may
+        // have a build script, including the standard library. To compile a
+        // build script, however, it itself needs a standard library! This
+        // introduces a bit of a pickle when we're compiling the standard
+        // library itself.
+        //
+        // To work around this we actually end up using the snapshot compiler
+        // (stage0) for compiling build scripts of the standard library itself.
+        // The stage0 compiler is guaranteed to have a libstd available for use.
+        //
+        // For other crates, however, we know that we've already got a standard
+        // library up and running, so we can use the normal compiler to compile
+        // build scripts in that situation.
+        if mode == Mode::Std {
+            cargo
+                .env("RUSTC_SNAPSHOT", &self.initial_rustc)
+                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
+        } else {
+            cargo
+                .env("RUSTC_SNAPSHOT", self.rustc(compiler))
+                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler));
+        }
+
+        // Tools that use compiler libraries may inherit the `-lLLVM` link
+        // requirement, but the `-L` library path is not propagated across
+        // separate Cargo projects. We can add LLVM's library path to the
+        // platform-specific environment variable as a workaround.
+        if mode == Mode::ToolRustc || mode == Mode::Codegen {
+            if let Some(llvm_config) = self.llvm_config(target) {
+                let llvm_libdir =
+                    command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout();
+                add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo);
+            }
+        }
+
+        // Compile everything except libraries and proc macros with the more
+        // efficient initial-exec TLS model. This doesn't work with `dlopen`,
+        // so we can't use it by default in general, but we can use it for tools
+        // and our own internal libraries.
+        if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") {
+            cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1");
+        }
+
+        // Ignore incremental modes except for stage0, since we're
+        // not guaranteeing correctness across builds if the compiler
+        // is changing under your feet.
+        if self.config.incremental && compiler.stage == 0 {
+            cargo.env("CARGO_INCREMENTAL", "1");
+        } else {
+            // Don't rely on any default setting for incr. comp. in Cargo
+            cargo.env("CARGO_INCREMENTAL", "0");
+        }
+
+        if let Some(ref on_fail) = self.config.on_fail {
+            cargo.env("RUSTC_ON_FAIL", on_fail);
+        }
+
+        if self.config.print_step_timings {
+            cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1");
+        }
+
+        if self.config.print_step_rusage {
+            cargo.env("RUSTC_PRINT_STEP_RUSAGE", "1");
+        }
+
+        if self.config.backtrace_on_ice {
+            cargo.env("RUSTC_BACKTRACE_ON_ICE", "1");
+        }
+
+        if self.is_verbose() {
+            // This provides very useful logs especially when debugging build cache-related stuff.
+            cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info");
+        }
+
+        cargo.env("RUSTC_VERBOSE", self.verbosity.to_string());
+
+        // Downstream forks of the Rust compiler might want to use a custom libc to add support for
+        // targets that are not yet available upstream. Adding a patch to replace libc with a
+        // custom one would cause compilation errors though, because Cargo would interpret the
+        // custom libc as part of the workspace, and apply the check-cfg lints on it.
+        //
+        // The libc build script emits check-cfg flags only when this environment variable is set,
+        // so this line allows the use of custom libcs.
+        cargo.env("LIBC_CHECK_CFG", "1");
+
+        if source_type == SourceType::InTree {
+            let mut lint_flags = Vec::new();
+            // When extending this list, add the new lints to the RUSTFLAGS of the
+            // build_bootstrap function of src/bootstrap/bootstrap.py as well as
+            // some code doesn't go through this `rustc` wrapper.
+            lint_flags.push("-Wrust_2018_idioms");
+            lint_flags.push("-Wunused_lifetimes");
+
+            if self.config.deny_warnings {
+                lint_flags.push("-Dwarnings");
+                rustdocflags.arg("-Dwarnings");
+            }
+
+            // This does not use RUSTFLAGS due to caching issues with Cargo.
+            // Clippy is treated as an "in tree" tool, but shares the same
+            // cache as other "submodule" tools. With these options set in
+            // RUSTFLAGS, that causes *every* shared dependency to be rebuilt.
+            // By injecting this into the rustc wrapper, this circumvents
+            // Cargo's fingerprint detection. This is fine because lint flags
+            // are always ignored in dependencies. Eventually this should be
+            // fixed via better support from Cargo.
+            cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" "));
+
+            rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes");
+        }
+
+        if mode == Mode::Rustc {
+            rustflags.arg("-Wrustc::internal");
+            // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all
+            // of the individual lints are satisfied.
+            rustflags.arg("-Wkeyword_idents_2024");
+            rustflags.arg("-Wunsafe_op_in_unsafe_fn");
+        }
+
+        if self.config.rust_frame_pointers {
+            rustflags.arg("-Cforce-frame-pointers=true");
+        }
+
+        // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc
+        // when compiling the standard library, since this might be linked into the final outputs
+        // produced by rustc. Since this mitigation is only available on Windows, only enable it
+        // for the standard library in case the compiler is run on a non-Windows platform.
+        // This is not needed for stage 0 artifacts because these will only be used for building
+        // the stage 1 compiler.
+        if cfg!(windows)
+            && mode == Mode::Std
+            && self.config.control_flow_guard
+            && compiler.stage >= 1
+        {
+            rustflags.arg("-Ccontrol-flow-guard");
+        }
+
+        // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the
+        // standard library, since this might be linked into the final outputs produced by rustc.
+        // Since this mitigation is only available on Windows, only enable it for the standard
+        // library in case the compiler is run on a non-Windows platform.
+        // This is not needed for stage 0 artifacts because these will only be used for building
+        // the stage 1 compiler.
+        if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 {
+            rustflags.arg("-Zehcont-guard");
+        }
+
+        // For `cargo doc` invocations, make rustdoc print the Rust version into the docs
+        // This replaces spaces with tabs because RUSTDOCFLAGS does not
+        // support arguments with regular spaces. Hopefully someday Cargo will
+        // have space support.
+        let rust_version = self.rust_version().replace(' ', "\t");
+        rustdocflags.arg("--crate-version").arg(&rust_version);
+
+        // Environment variables *required* throughout the build
+        //
+        // FIXME: should update code to not require this env var
+
+        // The host this new compiler will *run* on.
+        cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple);
+        // The host this new compiler is being *built* on.
+        cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple);
+
+        // Set this for all builds to make sure doc builds also get it.
+        cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel);
+
+        // This one's a bit tricky. As of the time of this writing the compiler
+        // links to the `winapi` crate on crates.io. This crate provides raw
+        // bindings to Windows system functions, sort of like libc does for
+        // Unix. This crate also, however, provides "import libraries" for the
+        // MinGW targets. There's an import library per dll in the windows
+        // distribution which is what's linked to. These custom import libraries
+        // are used because the winapi crate can reference Windows functions not
+        // present in the MinGW import libraries.
+        //
+        // For example MinGW may ship libdbghelp.a, but it may not have
+        // references to all the functions in the dbghelp dll. Instead the
+        // custom import library for dbghelp in the winapi crates has all this
+        // information.
+        //
+        // Unfortunately for us though the import libraries are linked by
+        // default via `-ldylib=winapi_foo`. That is, they're linked with the
+        // `dylib` type with a `winapi_` prefix (so the winapi ones don't
+        // conflict with the system MinGW ones). This consequently means that
+        // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm
+        // DLL) when linked against *again*, for example with procedural macros
+        // or plugins, will trigger the propagation logic of `-ldylib`, passing
+        // `-lwinapi_foo` to the linker again. This isn't actually available in
+        // our distribution, however, so the link fails.
+        //
+        // To solve this problem we tell winapi to not use its bundled import
+        // libraries. This means that it will link to the system MinGW import
+        // libraries by default, and the `-ldylib=foo` directives will still get
+        // passed to the final linker, but they'll look like `-lfoo` which can
+        // be resolved because MinGW has the import library. The downside is we
+        // don't get newer functions from Windows, but we don't use any of them
+        // anyway.
+        if !mode.is_tool() {
+            cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1");
+        }
+
+        for _ in 0..self.verbosity {
+            cargo.arg("-v");
+        }
+
+        match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) {
+            (Mode::Std, Some(n), _) | (_, _, Some(n)) => {
+                cargo.env(profile_var("CODEGEN_UNITS"), n.to_string());
+            }
+            _ => {
+                // Don't set anything
+            }
+        }
+
+        if self.config.locked_deps {
+            cargo.arg("--locked");
+        }
+        if self.config.vendor || self.is_sudo {
+            cargo.arg("--frozen");
+        }
+
+        // Try to use a sysroot-relative bindir, in case it was configured absolutely.
+        cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative());
+
+        cargo.force_coloring_in_ci();
+
+        // When we build Rust dylibs they're all intended for intermediate
+        // usage, so make sure we pass the -Cprefer-dynamic flag instead of
+        // linking all deps statically into the dylib.
+        if matches!(mode, Mode::Std) {
+            rustflags.arg("-Cprefer-dynamic");
+        }
+        if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) {
+            rustflags.arg("-Cprefer-dynamic");
+        }
+
+        cargo.env(
+            "RUSTC_LINK_STD_INTO_RUSTC_DRIVER",
+            if self.link_std_into_rustc_driver(target) { "1" } else { "0" },
+        );
+
+        // When building incrementally we default to a lower ThinLTO import limit
+        // (unless explicitly specified otherwise). This will produce a somewhat
+        // slower code but give way better compile times.
+        {
+            let limit = match self.config.rust_thin_lto_import_instr_limit {
+                Some(limit) => Some(limit),
+                None if self.config.incremental => Some(10),
+                _ => None,
+            };
+
+            if let Some(limit) = limit {
+                if stage == 0
+                    || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm"
+                {
+                    rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}"));
+                }
+            }
+        }
+
+        if matches!(mode, Mode::Std) {
+            if let Some(mir_opt_level) = self.config.rust_validate_mir_opts {
+                rustflags.arg("-Zvalidate-mir");
+                rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}"));
+            }
+            if self.config.rust_randomize_layout {
+                rustflags.arg("--cfg=randomized_layouts");
+            }
+            // Always enable inlining MIR when building the standard library.
+            // Without this flag, MIR inlining is disabled when incremental compilation is enabled.
+            // That causes some mir-opt tests which inline functions from the standard library to
+            // break when incremental compilation is enabled. So this overrides the "no inlining
+            // during incremental builds" heuristic for the standard library.
+            rustflags.arg("-Zinline-mir");
+
+            // Similarly, we need to keep debug info for functions inlined into other std functions,
+            // even if we're not going to output debuginfo for the crate we're currently building,
+            // so that it'll be available when downstream consumers of std try to use it.
+            rustflags.arg("-Zinline-mir-preserve-debug");
+        }
+
+        if self.config.rustc_parallel
+            && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen)
+        {
+            // keep in sync with `bootstrap/lib.rs:Build::rustc_features`
+            // `cfg` option for rustc, `features` option for cargo, for conditional compilation
+            rustflags.arg("--cfg=parallel_compiler");
+            rustdocflags.arg("--cfg=parallel_compiler");
+        }
+
+        Cargo {
+            command: cargo,
+            compiler,
+            target,
+            rustflags,
+            rustdocflags,
+            hostflags,
+            allow_features,
+        }
+    }
+}
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder/mod.rs
index 8cea01434fe..8f8ab112857 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -1,7 +1,8 @@
+mod cargo;
+
 use std::any::{Any, type_name};
 use std::cell::{Cell, RefCell};
 use std::collections::BTreeSet;
-use std::ffi::{OsStr, OsString};
 use std::fmt::{Debug, Write};
 use std::hash::Hash;
 use std::ops::Deref;
@@ -12,22 +13,17 @@ use std::{env, fs};
 
 use clap::ValueEnum;
 
+pub use self::cargo::Cargo;
 pub use crate::Compiler;
-use crate::core::build_steps::tool::{self, SourceType};
 use crate::core::build_steps::{
-    check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, vendor,
+    check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor,
 };
-use crate::core::config::flags::{Color, Subcommand};
-use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection};
+use crate::core::config::flags::Subcommand;
+use crate::core::config::{DryRun, TargetSelection};
 use crate::utils::cache::Cache;
 use crate::utils::exec::{BootstrapCommand, command};
-use crate::utils::helpers::{
-    self, LldThreads, add_dylib_path, add_link_lib_path, check_cfg_arg, exe, libdir, linker_args,
-    linker_flags, t,
-};
-use crate::{
-    Build, CLang, Crate, DocTests, EXTRA_CHECK_CFGS, GitRepo, Mode, prepare_behaviour_dump_dir,
-};
+use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t};
+use crate::{Build, Crate};
 
 #[cfg(test)]
 mod tests;
@@ -1410,870 +1406,6 @@ impl<'a> Builder<'a> {
         None
     }
 
-    /// Like `cargo`, but only passes flags that are valid for all commands.
-    pub fn bare_cargo(
-        &self,
-        compiler: Compiler,
-        mode: Mode,
-        target: TargetSelection,
-        cmd_kind: Kind,
-    ) -> BootstrapCommand {
-        let mut cargo = match cmd_kind {
-            Kind::Clippy => {
-                let mut cargo = self.cargo_clippy_cmd(compiler);
-                cargo.arg(cmd_kind.as_str());
-                cargo
-            }
-            Kind::MiriSetup => {
-                let mut cargo = self.cargo_miri_cmd(compiler);
-                cargo.arg("miri").arg("setup");
-                cargo
-            }
-            Kind::MiriTest => {
-                let mut cargo = self.cargo_miri_cmd(compiler);
-                cargo.arg("miri").arg("test");
-                cargo
-            }
-            _ => {
-                let mut cargo = command(&self.initial_cargo);
-                cargo.arg(cmd_kind.as_str());
-                cargo
-            }
-        };
-
-        // Run cargo from the source root so it can find .cargo/config.
-        // This matters when using vendoring and the working directory is outside the repository.
-        cargo.current_dir(&self.src);
-
-        let out_dir = self.stage_out(compiler, mode);
-        cargo.env("CARGO_TARGET_DIR", &out_dir);
-
-        // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger`
-        // from out of tree it shouldn't matter, since x.py is only used for
-        // building in-tree.
-        let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"];
-        match self.build.config.color {
-            Color::Always => {
-                cargo.arg("--color=always");
-                for log in &color_logs {
-                    cargo.env(log, "always");
-                }
-            }
-            Color::Never => {
-                cargo.arg("--color=never");
-                for log in &color_logs {
-                    cargo.env(log, "never");
-                }
-            }
-            Color::Auto => {} // nothing to do
-        }
-
-        if cmd_kind != Kind::Install {
-            cargo.arg("--target").arg(target.rustc_target_arg());
-        } else {
-            assert_eq!(target, compiler.host);
-        }
-
-        if self.config.rust_optimize.is_release() &&
-        // cargo bench/install do not accept `--release` and miri doesn't want it
-        !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest)
-        {
-            cargo.arg("--release");
-        }
-
-        // Remove make-related flags to ensure Cargo can correctly set things up
-        cargo.env_remove("MAKEFLAGS");
-        cargo.env_remove("MFLAGS");
-
-        cargo
-    }
-
-    /// This will create a `Command` that represents a pending execution of
-    /// Cargo. This cargo will be configured to use `compiler` as the actual
-    /// rustc compiler, its output will be scoped by `mode`'s output directory,
-    /// it will pass the `--target` flag for the specified `target`, and will be
-    /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands
-    /// to be run with Miri.
-    fn cargo(
-        &self,
-        compiler: Compiler,
-        mode: Mode,
-        source_type: SourceType,
-        target: TargetSelection,
-        cmd_kind: Kind,
-    ) -> Cargo {
-        let mut cargo = self.bare_cargo(compiler, mode, target, cmd_kind);
-        let out_dir = self.stage_out(compiler, mode);
-
-        let mut hostflags = HostFlags::default();
-
-        // Codegen backends are not yet tracked by -Zbinary-dep-depinfo,
-        // so we need to explicitly clear out if they've been updated.
-        for backend in self.codegen_backends(compiler) {
-            self.clear_if_dirty(&out_dir, &backend);
-        }
-
-        if cmd_kind == Kind::Doc {
-            let my_out = match mode {
-                // This is the intended out directory for compiler documentation.
-                Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target),
-                Mode::Std => {
-                    if self.config.cmd.json() {
-                        out_dir.join(target).join("json-doc")
-                    } else {
-                        out_dir.join(target).join("doc")
-                    }
-                }
-                _ => panic!("doc mode {mode:?} not expected"),
-            };
-            let rustdoc = self.rustdoc(compiler);
-            self.clear_if_dirty(&my_out, &rustdoc);
-        }
-
-        let profile_var = |name: &str| {
-            let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" };
-            format!("CARGO_PROFILE_{}_{}", profile, name)
-        };
-
-        // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config
-        // needs to not accidentally link to libLLVM in stage0/lib.
-        cargo.env("REAL_LIBRARY_PATH_VAR", helpers::dylib_path_var());
-        if let Some(e) = env::var_os(helpers::dylib_path_var()) {
-            cargo.env("REAL_LIBRARY_PATH", e);
-        }
-
-        // Set a flag for `check`/`clippy`/`fix`, so that certain build
-        // scripts can do less work (i.e. not building/requiring LLVM).
-        if matches!(cmd_kind, Kind::Check | Kind::Clippy | Kind::Fix) {
-            // If we've not yet built LLVM, or it's stale, then bust
-            // the rustc_llvm cache. That will always work, even though it
-            // may mean that on the next non-check build we'll need to rebuild
-            // rustc_llvm. But if LLVM is stale, that'll be a tiny amount
-            // of work comparatively, and we'd likely need to rebuild it anyway,
-            // so that's okay.
-            if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target, false)
-                .should_build()
-            {
-                cargo.env("RUST_CHECK", "1");
-            }
-        }
-
-        let stage = if compiler.stage == 0 && self.local_rebuild {
-            // Assume the local-rebuild rustc already has stage1 features.
-            1
-        } else {
-            compiler.stage
-        };
-
-        // We synthetically interpret a stage0 compiler used to build tools as a
-        // "raw" compiler in that it's the exact snapshot we download. Normally
-        // the stage0 build means it uses libraries build by the stage0
-        // compiler, but for tools we just use the precompiled libraries that
-        // we've downloaded
-        let use_snapshot = mode == Mode::ToolBootstrap;
-        assert!(!use_snapshot || stage == 0 || self.local_rebuild);
-
-        let maybe_sysroot = self.sysroot(compiler);
-        let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
-        let libdir = self.rustc_libdir(compiler);
-
-        let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8");
-        if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) {
-            println!("using sysroot {sysroot_str}");
-        }
-
-        let mut rustflags = Rustflags::new(target);
-        if stage != 0 {
-            if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
-                cargo.args(s.split_whitespace());
-            }
-            rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP");
-        } else {
-            if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") {
-                cargo.args(s.split_whitespace());
-            }
-            rustflags.env("RUSTFLAGS_BOOTSTRAP");
-            rustflags.arg("--cfg=bootstrap");
-        }
-
-        if cmd_kind == Kind::Clippy {
-            // clippy overwrites sysroot if we pass it to cargo.
-            // Pass it directly to clippy instead.
-            // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
-            // so it has no way of knowing the sysroot.
-            rustflags.arg("--sysroot");
-            rustflags.arg(sysroot_str);
-        }
-
-        let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling {
-            Some(setting) => {
-                // If an explicit setting is given, use that
-                setting
-            }
-            None => {
-                if mode == Mode::Std {
-                    // The standard library defaults to the legacy scheme
-                    false
-                } else {
-                    // The compiler and tools default to the new scheme
-                    true
-                }
-            }
-        };
-
-        // By default, windows-rs depends on a native library that doesn't get copied into the
-        // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native
-        // library unnecessary. This can be removed when windows-rs enables raw-dylib
-        // unconditionally.
-        if let Mode::Rustc | Mode::ToolRustc = mode {
-            rustflags.arg("--cfg=windows_raw_dylib");
-        }
-
-        if use_new_symbol_mangling {
-            rustflags.arg("-Csymbol-mangling-version=v0");
-        } else {
-            rustflags.arg("-Csymbol-mangling-version=legacy");
-        }
-
-        // FIXME: the following components don't build with `-Zrandomize-layout` yet:
-        // - wasm-component-ld, due to the `wast`crate
-        // - rust-analyzer, due to the rowan crate
-        // so we exclude entire categories of steps here due to lack of fine-grained control over
-        // rustflags.
-        if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc {
-            rustflags.arg("-Zrandomize-layout");
-        }
-
-        // Enable compile-time checking of `cfg` names, values and Cargo `features`.
-        //
-        // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like
-        // backtrace, core_simd, std_float, ...), those dependencies have their own
-        // features but cargo isn't involved in the #[path] process and so cannot pass the
-        // complete list of features, so for that reason we don't enable checking of
-        // features for std crates.
-        if mode == Mode::Std {
-            rustflags.arg("--check-cfg=cfg(feature,values(any()))");
-        }
-
-        // Add extra cfg not defined in/by rustc
-        //
-        // Note: Although it would seems that "-Zunstable-options" to `rustflags` is useless as
-        // cargo would implicitly add it, it was discover that sometimes bootstrap only use
-        // `rustflags` without `cargo` making it required.
-        rustflags.arg("-Zunstable-options");
-        for (restricted_mode, name, values) in EXTRA_CHECK_CFGS {
-            if restricted_mode.is_none() || *restricted_mode == Some(mode) {
-                rustflags.arg(&check_cfg_arg(name, *values));
-            }
-        }
-
-        // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments
-        // to the host invocation here, but rather Cargo should know what flags to pass rustc
-        // itself.
-        if stage == 0 {
-            hostflags.arg("--cfg=bootstrap");
-        }
-        // Cargo doesn't pass RUSTFLAGS to proc_macros:
-        // https://github.com/rust-lang/cargo/issues/4423
-        // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
-        // We also declare that the flag is expected, which we need to do to not
-        // get warnings about it being unexpected.
-        hostflags.arg("-Zunstable-options");
-        hostflags.arg("--check-cfg=cfg(bootstrap)");
-
-        // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`,
-        // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See
-        // #71458.
-        let mut rustdocflags = rustflags.clone();
-        rustdocflags.propagate_cargo_env("RUSTDOCFLAGS");
-        if stage == 0 {
-            rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP");
-        } else {
-            rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP");
-        }
-
-        if let Ok(s) = env::var("CARGOFLAGS") {
-            cargo.args(s.split_whitespace());
-        }
-
-        match mode {
-            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}
-            Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
-                // Build proc macros both for the host and the target unless proc-macros are not
-                // supported by the target.
-                if target != compiler.host && cmd_kind != Kind::Check {
-                    let error = command(self.rustc(compiler))
-                        .arg("--target")
-                        .arg(target.rustc_target_arg())
-                        .arg("--print=file-names")
-                        .arg("--crate-type=proc-macro")
-                        .arg("-")
-                        .run_capture(self)
-                        .stderr();
-                    let not_supported = error
-                        .lines()
-                        .any(|line| line.contains("unsupported crate type `proc-macro`"));
-                    if !not_supported {
-                        cargo.arg("-Zdual-proc-macros");
-                        rustflags.arg("-Zdual-proc-macros");
-                    }
-                }
-            }
-        }
-
-        // This tells Cargo (and in turn, rustc) to output more complete
-        // dependency information.  Most importantly for bootstrap, this
-        // includes sysroot artifacts, like libstd, which means that we don't
-        // need to track those in bootstrap (an error prone process!). This
-        // feature is currently unstable as there may be some bugs and such, but
-        // it represents a big improvement in bootstrap's reliability on
-        // rebuilds, so we're using it here.
-        //
-        // For some additional context, see #63470 (the PR originally adding
-        // this), as well as #63012 which is the tracking issue for this
-        // feature on the rustc side.
-        cargo.arg("-Zbinary-dep-depinfo");
-        let allow_features = match mode {
-            Mode::ToolBootstrap | Mode::ToolStd => {
-                // Restrict the allowed features so we don't depend on nightly
-                // accidentally.
-                //
-                // binary-dep-depinfo is used by bootstrap itself for all
-                // compilations.
-                //
-                // Lots of tools depend on proc_macro2 and proc-macro-error.
-                // Those have build scripts which assume nightly features are
-                // available if the `rustc` version is "nighty" or "dev". See
-                // bin/rustc.rs for why that is a problem. Instead of labeling
-                // those features for each individual tool that needs them,
-                // just blanket allow them here.
-                //
-                // If this is ever removed, be sure to add something else in
-                // its place to keep the restrictions in place (or make a way
-                // to unset RUSTC_BOOTSTRAP).
-                "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic"
-                    .to_string()
-            }
-            Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(),
-        };
-
-        cargo.arg("-j").arg(self.jobs().to_string());
-
-        // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
-        // Force cargo to output binaries with disambiguating hashes in the name
-        let mut metadata = if compiler.stage == 0 {
-            // Treat stage0 like a special channel, whether it's a normal prior-
-            // release rustc or a local rebuild with the same version, so we
-            // never mix these libraries by accident.
-            "bootstrap".to_string()
-        } else {
-            self.config.channel.to_string()
-        };
-        // We want to make sure that none of the dependencies between
-        // std/test/rustc unify with one another. This is done for weird linkage
-        // reasons but the gist of the problem is that if librustc, libtest, and
-        // libstd all depend on libc from crates.io (which they actually do) we
-        // want to make sure they all get distinct versions. Things get really
-        // weird if we try to unify all these dependencies right now, namely
-        // around how many times the library is linked in dynamic libraries and
-        // such. If rustc were a static executable or if we didn't ship dylibs
-        // this wouldn't be a problem, but we do, so it is. This is in general
-        // just here to make sure things build right. If you can remove this and
-        // things still build right, please do!
-        match mode {
-            Mode::Std => metadata.push_str("std"),
-            // When we're building rustc tools, they're built with a search path
-            // that contains things built during the rustc build. For example,
-            // bitflags is built during the rustc build, and is a dependency of
-            // rustdoc as well. We're building rustdoc in a different target
-            // directory, though, which means that Cargo will rebuild the
-            // dependency. When we go on to build rustdoc, we'll look for
-            // bitflags, and find two different copies: one built during the
-            // rustc step and one that we just built. This isn't always a
-            // problem, somehow -- not really clear why -- but we know that this
-            // fixes things.
-            Mode::ToolRustc => metadata.push_str("tool-rustc"),
-            // Same for codegen backends.
-            Mode::Codegen => metadata.push_str("codegen"),
-            _ => {}
-        }
-        cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata);
-
-        if cmd_kind == Kind::Clippy {
-            rustflags.arg("-Zforce-unstable-if-unmarked");
-        }
-
-        rustflags.arg("-Zmacro-backtrace");
-
-        let want_rustdoc = self.doc_tests != DocTests::No;
-
-        // Clear the output directory if the real rustc we're using has changed;
-        // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc.
-        //
-        // Avoid doing this during dry run as that usually means the relevant
-        // compiler is not yet linked/copied properly.
-        //
-        // Only clear out the directory if we're compiling std; otherwise, we
-        // should let Cargo take care of things for us (via depdep info)
-        if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build {
-            self.clear_if_dirty(&out_dir, &self.rustc(compiler));
-        }
-
-        let rustdoc_path = match cmd_kind {
-            Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler),
-            _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"),
-        };
-
-        // Customize the compiler we're running. Specify the compiler to cargo
-        // as our shim and then pass it some various options used to configure
-        // how the actual compiler itself is called.
-        //
-        // These variables are primarily all read by
-        // src/bootstrap/bin/{rustc.rs,rustdoc.rs}
-        cargo
-            .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
-            .env("RUSTC_REAL", self.rustc(compiler))
-            .env("RUSTC_STAGE", stage.to_string())
-            .env("RUSTC_SYSROOT", sysroot)
-            .env("RUSTC_LIBDIR", libdir)
-            .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
-            .env("RUSTDOC_REAL", rustdoc_path)
-            .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
-            .env("RUSTC_BREAK_ON_ICE", "1");
-
-        // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree
-        // sysroot depending on whether we're building build scripts.
-        // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not
-        // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs.
-        cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc"));
-        // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:(
-        cargo.env("RUSTC", self.bootstrap_out.join("rustc"));
-
-        // Someone might have set some previous rustc wrapper (e.g.
-        // sccache) before bootstrap overrode it. Respect that variable.
-        if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") {
-            cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper);
-        }
-
-        // If this is for `miri-test`, prepare the sysroots.
-        if cmd_kind == Kind::MiriTest {
-            self.ensure(compile::Std::new(compiler, compiler.host));
-            let host_sysroot = self.sysroot(compiler);
-            let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target);
-            cargo.env("MIRI_SYSROOT", &miri_sysroot);
-            cargo.env("MIRI_HOST_SYSROOT", &host_sysroot);
-        }
-
-        cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string());
-
-        if let Some(stack_protector) = &self.config.rust_stack_protector {
-            rustflags.arg(&format!("-Zstack-protector={stack_protector}"));
-        }
-
-        if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc
-        {
-            cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
-        }
-
-        let debuginfo_level = match mode {
-            Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
-            Mode::Std => self.config.rust_debuginfo_level_std,
-            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => {
-                self.config.rust_debuginfo_level_tools
-            }
-        };
-        cargo.env(profile_var("DEBUG"), debuginfo_level.to_string());
-        if let Some(opt_level) = &self.config.rust_optimize.get_opt_level() {
-            cargo.env(profile_var("OPT_LEVEL"), opt_level);
-        }
-        cargo.env(
-            profile_var("DEBUG_ASSERTIONS"),
-            if mode == Mode::Std {
-                self.config.rust_debug_assertions_std.to_string()
-            } else {
-                self.config.rust_debug_assertions.to_string()
-            },
-        );
-        cargo.env(
-            profile_var("OVERFLOW_CHECKS"),
-            if mode == Mode::Std {
-                self.config.rust_overflow_checks_std.to_string()
-            } else {
-                self.config.rust_overflow_checks.to_string()
-            },
-        );
-
-        match self.config.split_debuginfo(target) {
-            SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"),
-            SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"),
-            SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"),
-        };
-
-        if self.config.cmd.bless() {
-            // Bless `expect!` tests.
-            cargo.env("UPDATE_EXPECT", "1");
-        }
-
-        if !mode.is_tool() {
-            cargo.env("RUSTC_FORCE_UNSTABLE", "1");
-        }
-
-        if let Some(x) = self.crt_static(target) {
-            if x {
-                rustflags.arg("-Ctarget-feature=+crt-static");
-            } else {
-                rustflags.arg("-Ctarget-feature=-crt-static");
-            }
-        }
-
-        if let Some(x) = self.crt_static(compiler.host) {
-            let sign = if x { "+" } else { "-" };
-            hostflags.arg(format!("-Ctarget-feature={sign}crt-static"));
-        }
-
-        if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) {
-            let map = format!("{}={}", self.build.src.display(), map_to);
-            cargo.env("RUSTC_DEBUGINFO_MAP", map);
-
-            // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to,
-            // in order to opportunistically reverse it later.
-            cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to);
-        }
-
-        if self.config.rust_remap_debuginfo {
-            let mut env_var = OsString::new();
-            if self.config.vendor {
-                let vendor = self.build.src.join("vendor");
-                env_var.push(vendor);
-                env_var.push("=/rust/deps");
-            } else {
-                let registry_src = t!(home::cargo_home()).join("registry").join("src");
-                for entry in t!(std::fs::read_dir(registry_src)) {
-                    if !env_var.is_empty() {
-                        env_var.push("\t");
-                    }
-                    env_var.push(t!(entry).path());
-                    env_var.push("=/rust/deps");
-                }
-            }
-            cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var);
-        }
-
-        // Enable usage of unstable features
-        cargo.env("RUSTC_BOOTSTRAP", "1");
-
-        if self.config.dump_bootstrap_shims {
-            prepare_behaviour_dump_dir(self.build);
-
-            cargo
-                .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump"))
-                .env("BUILD_OUT", &self.build.out)
-                .env("CARGO_HOME", t!(home::cargo_home()));
-        };
-
-        self.add_rust_test_threads(&mut cargo);
-
-        // Almost all of the crates that we compile as part of the bootstrap may
-        // have a build script, including the standard library. To compile a
-        // build script, however, it itself needs a standard library! This
-        // introduces a bit of a pickle when we're compiling the standard
-        // library itself.
-        //
-        // To work around this we actually end up using the snapshot compiler
-        // (stage0) for compiling build scripts of the standard library itself.
-        // The stage0 compiler is guaranteed to have a libstd available for use.
-        //
-        // For other crates, however, we know that we've already got a standard
-        // library up and running, so we can use the normal compiler to compile
-        // build scripts in that situation.
-        if mode == Mode::Std {
-            cargo
-                .env("RUSTC_SNAPSHOT", &self.initial_rustc)
-                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
-        } else {
-            cargo
-                .env("RUSTC_SNAPSHOT", self.rustc(compiler))
-                .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler));
-        }
-
-        // Tools that use compiler libraries may inherit the `-lLLVM` link
-        // requirement, but the `-L` library path is not propagated across
-        // separate Cargo projects. We can add LLVM's library path to the
-        // platform-specific environment variable as a workaround.
-        if mode == Mode::ToolRustc || mode == Mode::Codegen {
-            if let Some(llvm_config) = self.llvm_config(target) {
-                let llvm_libdir =
-                    command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout();
-                add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo);
-            }
-        }
-
-        // Compile everything except libraries and proc macros with the more
-        // efficient initial-exec TLS model. This doesn't work with `dlopen`,
-        // so we can't use it by default in general, but we can use it for tools
-        // and our own internal libraries.
-        if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") {
-            cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1");
-        }
-
-        // Ignore incremental modes except for stage0, since we're
-        // not guaranteeing correctness across builds if the compiler
-        // is changing under your feet.
-        if self.config.incremental && compiler.stage == 0 {
-            cargo.env("CARGO_INCREMENTAL", "1");
-        } else {
-            // Don't rely on any default setting for incr. comp. in Cargo
-            cargo.env("CARGO_INCREMENTAL", "0");
-        }
-
-        if let Some(ref on_fail) = self.config.on_fail {
-            cargo.env("RUSTC_ON_FAIL", on_fail);
-        }
-
-        if self.config.print_step_timings {
-            cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1");
-        }
-
-        if self.config.print_step_rusage {
-            cargo.env("RUSTC_PRINT_STEP_RUSAGE", "1");
-        }
-
-        if self.config.backtrace_on_ice {
-            cargo.env("RUSTC_BACKTRACE_ON_ICE", "1");
-        }
-
-        if self.is_verbose() {
-            // This provides very useful logs especially when debugging build cache-related stuff.
-            cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info");
-        }
-
-        cargo.env("RUSTC_VERBOSE", self.verbosity.to_string());
-
-        // Downstream forks of the Rust compiler might want to use a custom libc to add support for
-        // targets that are not yet available upstream. Adding a patch to replace libc with a
-        // custom one would cause compilation errors though, because Cargo would interpret the
-        // custom libc as part of the workspace, and apply the check-cfg lints on it.
-        //
-        // The libc build script emits check-cfg flags only when this environment variable is set,
-        // so this line allows the use of custom libcs.
-        cargo.env("LIBC_CHECK_CFG", "1");
-
-        if source_type == SourceType::InTree {
-            let mut lint_flags = Vec::new();
-            // When extending this list, add the new lints to the RUSTFLAGS of the
-            // build_bootstrap function of src/bootstrap/bootstrap.py as well as
-            // some code doesn't go through this `rustc` wrapper.
-            lint_flags.push("-Wrust_2018_idioms");
-            lint_flags.push("-Wunused_lifetimes");
-
-            if self.config.deny_warnings {
-                lint_flags.push("-Dwarnings");
-                rustdocflags.arg("-Dwarnings");
-            }
-
-            // This does not use RUSTFLAGS due to caching issues with Cargo.
-            // Clippy is treated as an "in tree" tool, but shares the same
-            // cache as other "submodule" tools. With these options set in
-            // RUSTFLAGS, that causes *every* shared dependency to be rebuilt.
-            // By injecting this into the rustc wrapper, this circumvents
-            // Cargo's fingerprint detection. This is fine because lint flags
-            // are always ignored in dependencies. Eventually this should be
-            // fixed via better support from Cargo.
-            cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" "));
-
-            rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes");
-        }
-
-        if mode == Mode::Rustc {
-            rustflags.arg("-Wrustc::internal");
-            // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all
-            // of the individual lints are satisfied.
-            rustflags.arg("-Wkeyword_idents_2024");
-            rustflags.arg("-Wunsafe_op_in_unsafe_fn");
-        }
-
-        if self.config.rust_frame_pointers {
-            rustflags.arg("-Cforce-frame-pointers=true");
-        }
-
-        // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc
-        // when compiling the standard library, since this might be linked into the final outputs
-        // produced by rustc. Since this mitigation is only available on Windows, only enable it
-        // for the standard library in case the compiler is run on a non-Windows platform.
-        // This is not needed for stage 0 artifacts because these will only be used for building
-        // the stage 1 compiler.
-        if cfg!(windows)
-            && mode == Mode::Std
-            && self.config.control_flow_guard
-            && compiler.stage >= 1
-        {
-            rustflags.arg("-Ccontrol-flow-guard");
-        }
-
-        // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the
-        // standard library, since this might be linked into the final outputs produced by rustc.
-        // Since this mitigation is only available on Windows, only enable it for the standard
-        // library in case the compiler is run on a non-Windows platform.
-        // This is not needed for stage 0 artifacts because these will only be used for building
-        // the stage 1 compiler.
-        if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 {
-            rustflags.arg("-Zehcont-guard");
-        }
-
-        // For `cargo doc` invocations, make rustdoc print the Rust version into the docs
-        // This replaces spaces with tabs because RUSTDOCFLAGS does not
-        // support arguments with regular spaces. Hopefully someday Cargo will
-        // have space support.
-        let rust_version = self.rust_version().replace(' ', "\t");
-        rustdocflags.arg("--crate-version").arg(&rust_version);
-
-        // Environment variables *required* throughout the build
-        //
-        // FIXME: should update code to not require this env var
-
-        // The host this new compiler will *run* on.
-        cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple);
-        // The host this new compiler is being *built* on.
-        cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple);
-
-        // Set this for all builds to make sure doc builds also get it.
-        cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel);
-
-        // This one's a bit tricky. As of the time of this writing the compiler
-        // links to the `winapi` crate on crates.io. This crate provides raw
-        // bindings to Windows system functions, sort of like libc does for
-        // Unix. This crate also, however, provides "import libraries" for the
-        // MinGW targets. There's an import library per dll in the windows
-        // distribution which is what's linked to. These custom import libraries
-        // are used because the winapi crate can reference Windows functions not
-        // present in the MinGW import libraries.
-        //
-        // For example MinGW may ship libdbghelp.a, but it may not have
-        // references to all the functions in the dbghelp dll. Instead the
-        // custom import library for dbghelp in the winapi crates has all this
-        // information.
-        //
-        // Unfortunately for us though the import libraries are linked by
-        // default via `-ldylib=winapi_foo`. That is, they're linked with the
-        // `dylib` type with a `winapi_` prefix (so the winapi ones don't
-        // conflict with the system MinGW ones). This consequently means that
-        // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm
-        // DLL) when linked against *again*, for example with procedural macros
-        // or plugins, will trigger the propagation logic of `-ldylib`, passing
-        // `-lwinapi_foo` to the linker again. This isn't actually available in
-        // our distribution, however, so the link fails.
-        //
-        // To solve this problem we tell winapi to not use its bundled import
-        // libraries. This means that it will link to the system MinGW import
-        // libraries by default, and the `-ldylib=foo` directives will still get
-        // passed to the final linker, but they'll look like `-lfoo` which can
-        // be resolved because MinGW has the import library. The downside is we
-        // don't get newer functions from Windows, but we don't use any of them
-        // anyway.
-        if !mode.is_tool() {
-            cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1");
-        }
-
-        for _ in 0..self.verbosity {
-            cargo.arg("-v");
-        }
-
-        match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) {
-            (Mode::Std, Some(n), _) | (_, _, Some(n)) => {
-                cargo.env(profile_var("CODEGEN_UNITS"), n.to_string());
-            }
-            _ => {
-                // Don't set anything
-            }
-        }
-
-        if self.config.locked_deps {
-            cargo.arg("--locked");
-        }
-        if self.config.vendor || self.is_sudo {
-            cargo.arg("--frozen");
-        }
-
-        // Try to use a sysroot-relative bindir, in case it was configured absolutely.
-        cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative());
-
-        cargo.force_coloring_in_ci();
-
-        // When we build Rust dylibs they're all intended for intermediate
-        // usage, so make sure we pass the -Cprefer-dynamic flag instead of
-        // linking all deps statically into the dylib.
-        if matches!(mode, Mode::Std) {
-            rustflags.arg("-Cprefer-dynamic");
-        }
-        if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) {
-            rustflags.arg("-Cprefer-dynamic");
-        }
-
-        cargo.env(
-            "RUSTC_LINK_STD_INTO_RUSTC_DRIVER",
-            if self.link_std_into_rustc_driver(target) { "1" } else { "0" },
-        );
-
-        // When building incrementally we default to a lower ThinLTO import limit
-        // (unless explicitly specified otherwise). This will produce a somewhat
-        // slower code but give way better compile times.
-        {
-            let limit = match self.config.rust_thin_lto_import_instr_limit {
-                Some(limit) => Some(limit),
-                None if self.config.incremental => Some(10),
-                _ => None,
-            };
-
-            if let Some(limit) = limit {
-                if stage == 0
-                    || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm"
-                {
-                    rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}"));
-                }
-            }
-        }
-
-        if matches!(mode, Mode::Std) {
-            if let Some(mir_opt_level) = self.config.rust_validate_mir_opts {
-                rustflags.arg("-Zvalidate-mir");
-                rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}"));
-            }
-            if self.config.rust_randomize_layout {
-                rustflags.arg("--cfg=randomized_layouts");
-            }
-            // Always enable inlining MIR when building the standard library.
-            // Without this flag, MIR inlining is disabled when incremental compilation is enabled.
-            // That causes some mir-opt tests which inline functions from the standard library to
-            // break when incremental compilation is enabled. So this overrides the "no inlining
-            // during incremental builds" heuristic for the standard library.
-            rustflags.arg("-Zinline-mir");
-
-            // Similarly, we need to keep debug info for functions inlined into other std functions,
-            // even if we're not going to output debuginfo for the crate we're currently building,
-            // so that it'll be available when downstream consumers of std try to use it.
-            rustflags.arg("-Zinline-mir-preserve-debug");
-        }
-
-        if self.config.rustc_parallel
-            && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen)
-        {
-            // keep in sync with `bootstrap/lib.rs:Build::rustc_features`
-            // `cfg` option for rustc, `features` option for cargo, for conditional compilation
-            rustflags.arg("--cfg=parallel_compiler");
-            rustdocflags.arg("--cfg=parallel_compiler");
-        }
-
-        Cargo {
-            command: cargo,
-            compiler,
-            target,
-            rustflags,
-            rustdocflags,
-            hostflags,
-            allow_features,
-        }
-    }
-
     /// Ensure that a given step is built, returning its output. This will
     /// cache the step, so it is safe (and good!) to call this as often as
     /// needed to ensure that all dependencies are built.
@@ -2399,337 +1531,3 @@ impl<'a> Builder<'a> {
         }
     }
 }
-
-/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler later.
-///
-/// `-Z crate-attr` flags will be applied recursively on the target code using the `rustc_parse::parser::Parser`.
-/// See `rustc_builtin_macros::cmdline_attrs::inject` for more information.
-#[derive(Debug, Clone)]
-struct Rustflags(String, TargetSelection);
-
-impl Rustflags {
-    fn new(target: TargetSelection) -> Rustflags {
-        let mut ret = Rustflags(String::new(), target);
-        ret.propagate_cargo_env("RUSTFLAGS");
-        ret
-    }
-
-    /// By default, cargo will pick up on various variables in the environment. However, bootstrap
-    /// reuses those variables to pass additional flags to rustdoc, so by default they get overridden.
-    /// Explicitly add back any previous value in the environment.
-    ///
-    /// `prefix` is usually `RUSTFLAGS` or `RUSTDOCFLAGS`.
-    fn propagate_cargo_env(&mut self, prefix: &str) {
-        // Inherit `RUSTFLAGS` by default ...
-        self.env(prefix);
-
-        // ... and also handle target-specific env RUSTFLAGS if they're configured.
-        let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix);
-        self.env(&target_specific);
-    }
-
-    fn env(&mut self, env: &str) {
-        if let Ok(s) = env::var(env) {
-            for part in s.split(' ') {
-                self.arg(part);
-            }
-        }
-    }
-
-    fn arg(&mut self, arg: &str) -> &mut Self {
-        assert_eq!(arg.split(' ').count(), 1);
-        if !self.0.is_empty() {
-            self.0.push(' ');
-        }
-        self.0.push_str(arg);
-        self
-    }
-}
-
-/// Flags that are passed to the `rustc` shim binary.
-/// These flags will only be applied when compiling host code, i.e. when
-/// `--target` is unset.
-#[derive(Debug, Default)]
-pub struct HostFlags {
-    rustc: Vec<String>,
-}
-
-impl HostFlags {
-    const SEPARATOR: &'static str = " ";
-
-    /// Adds a host rustc flag.
-    fn arg<S: Into<String>>(&mut self, flag: S) {
-        let value = flag.into().trim().to_string();
-        assert!(!value.contains(Self::SEPARATOR));
-        self.rustc.push(value);
-    }
-
-    /// Encodes all the flags into a single string.
-    fn encode(self) -> String {
-        self.rustc.join(Self::SEPARATOR)
-    }
-}
-
-#[derive(Debug)]
-pub struct Cargo {
-    command: BootstrapCommand,
-    compiler: Compiler,
-    target: TargetSelection,
-    rustflags: Rustflags,
-    rustdocflags: Rustflags,
-    hostflags: HostFlags,
-    allow_features: String,
-}
-
-impl Cargo {
-    /// Calls `Builder::cargo` and `Cargo::configure_linker` to prepare an invocation of `cargo` to be run.
-    pub fn new(
-        builder: &Builder<'_>,
-        compiler: Compiler,
-        mode: Mode,
-        source_type: SourceType,
-        target: TargetSelection,
-        cmd_kind: Kind,
-    ) -> Cargo {
-        let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind);
-
-        match cmd_kind {
-            // No need to configure the target linker for these command types,
-            // as they don't invoke rustc at all.
-            Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {}
-            _ => {
-                cargo.configure_linker(builder);
-            }
-        }
-
-        cargo
-    }
-
-    pub fn into_cmd(self) -> BootstrapCommand {
-        self.into()
-    }
-
-    /// Same as `Cargo::new` except this one doesn't configure the linker with `Cargo::configure_linker`
-    pub fn new_for_mir_opt_tests(
-        builder: &Builder<'_>,
-        compiler: Compiler,
-        mode: Mode,
-        source_type: SourceType,
-        target: TargetSelection,
-        cmd_kind: Kind,
-    ) -> Cargo {
-        builder.cargo(compiler, mode, source_type, target, cmd_kind)
-    }
-
-    pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo {
-        self.rustdocflags.arg(arg);
-        self
-    }
-
-    pub fn rustflag(&mut self, arg: &str) -> &mut Cargo {
-        self.rustflags.arg(arg);
-        self
-    }
-
-    pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Cargo {
-        self.command.arg(arg.as_ref());
-        self
-    }
-
-    pub fn args<I, S>(&mut self, args: I) -> &mut Cargo
-    where
-        I: IntoIterator<Item = S>,
-        S: AsRef<OsStr>,
-    {
-        for arg in args {
-            self.arg(arg.as_ref());
-        }
-        self
-    }
-
-    pub fn env(&mut self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) -> &mut Cargo {
-        // These are managed through rustflag/rustdocflag interfaces.
-        assert_ne!(key.as_ref(), "RUSTFLAGS");
-        assert_ne!(key.as_ref(), "RUSTDOCFLAGS");
-        self.command.env(key.as_ref(), value.as_ref());
-        self
-    }
-
-    pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) {
-        builder.add_rustc_lib_path(self.compiler, &mut self.command);
-    }
-
-    pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo {
-        self.command.current_dir(dir);
-        self
-    }
-
-    /// Adds nightly-only features that this invocation is allowed to use.
-    ///
-    /// By default, all nightly features are allowed. Once this is called, it
-    /// will be restricted to the given set.
-    pub fn allow_features(&mut self, features: &str) -> &mut Cargo {
-        if !self.allow_features.is_empty() {
-            self.allow_features.push(',');
-        }
-        self.allow_features.push_str(features);
-        self
-    }
-
-    fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo {
-        let target = self.target;
-        let compiler = self.compiler;
-
-        // Dealing with rpath here is a little special, so let's go into some
-        // detail. First off, `-rpath` is a linker option on Unix platforms
-        // which adds to the runtime dynamic loader path when looking for
-        // dynamic libraries. We use this by default on Unix platforms to ensure
-        // that our nightlies behave the same on Windows, that is they work out
-        // of the box. This can be disabled by setting `rpath = false` in `[rust]`
-        // table of `config.toml`
-        //
-        // Ok, so the astute might be wondering "why isn't `-C rpath` used
-        // here?" and that is indeed a good question to ask. This codegen
-        // option is the compiler's current interface to generating an rpath.
-        // Unfortunately it doesn't quite suffice for us. The flag currently
-        // takes no value as an argument, so the compiler calculates what it
-        // should pass to the linker as `-rpath`. This unfortunately is based on
-        // the **compile time** directory structure which when building with
-        // Cargo will be very different than the runtime directory structure.
-        //
-        // All that's a really long winded way of saying that if we use
-        // `-Crpath` then the executables generated have the wrong rpath of
-        // something like `$ORIGIN/deps` when in fact the way we distribute
-        // rustc requires the rpath to be `$ORIGIN/../lib`.
-        //
-        // So, all in all, to set up the correct rpath we pass the linker
-        // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it
-        // fun to pass a flag to a tool to pass a flag to pass a flag to a tool
-        // to change a flag in a binary?
-        if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) {
-            let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap();
-            let rpath = if target.contains("apple") {
-                // Note that we need to take one extra step on macOS to also pass
-                // `-Wl,-instal_name,@rpath/...` to get things to work right. To
-                // do that we pass a weird flag to the compiler to get it to do
-                // so. Note that this is definitely a hack, and we should likely
-                // flesh out rpath support more fully in the future.
-                self.rustflags.arg("-Zosx-rpath-install-name");
-                Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
-            } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") {
-                self.rustflags.arg("-Clink-args=-Wl,-z,origin");
-                Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
-            } else {
-                None
-            };
-            if let Some(rpath) = rpath {
-                self.rustflags.arg(&format!("-Clink-args={rpath}"));
-            }
-        }
-
-        for arg in linker_args(builder, compiler.host, LldThreads::Yes) {
-            self.hostflags.arg(&arg);
-        }
-
-        if let Some(target_linker) = builder.linker(target) {
-            let target = crate::envify(&target.triple);
-            self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker);
-        }
-        // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not
-        // `linker_args` here.
-        for flag in linker_flags(builder, target, LldThreads::Yes) {
-            self.rustflags.arg(&flag);
-        }
-        for arg in linker_args(builder, target, LldThreads::Yes) {
-            self.rustdocflags.arg(&arg);
-        }
-
-        if !builder.config.dry_run()
-            && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz")
-        {
-            self.rustflags.arg("-Clink-arg=-gz");
-        }
-
-        // Throughout the build Cargo can execute a number of build scripts
-        // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
-        // obtained previously to those build scripts.
-        // Build scripts use either the `cc` crate or `configure/make` so we pass
-        // the options through environment variables that are fetched and understood by both.
-        //
-        // FIXME: the guard against msvc shouldn't need to be here
-        if target.is_msvc() {
-            if let Some(ref cl) = builder.config.llvm_clang_cl {
-                // FIXME: There is a bug in Clang 18 when building for ARM64:
-                // https://github.com/llvm/llvm-project/pull/81849. This is
-                // fixed in LLVM 19, but can't be backported.
-                if !target.starts_with("aarch64") && !target.starts_with("arm64ec") {
-                    self.command.env("CC", cl).env("CXX", cl);
-                }
-            }
-        } else {
-            let ccache = builder.config.ccache.as_ref();
-            let ccacheify = |s: &Path| {
-                let ccache = match ccache {
-                    Some(ref s) => s,
-                    None => return s.display().to_string(),
-                };
-                // FIXME: the cc-rs crate only recognizes the literal strings
-                // `ccache` and `sccache` when doing caching compilations, so we
-                // mirror that here. It should probably be fixed upstream to
-                // accept a new env var or otherwise work with custom ccache
-                // vars.
-                match &ccache[..] {
-                    "ccache" | "sccache" => format!("{} {}", ccache, s.display()),
-                    _ => s.display().to_string(),
-                }
-            };
-            let triple_underscored = target.triple.replace('-', "_");
-            let cc = ccacheify(&builder.cc(target));
-            self.command.env(format!("CC_{triple_underscored}"), &cc);
-
-            let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" ");
-            self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags);
-
-            if let Some(ar) = builder.ar(target) {
-                let ranlib = format!("{} s", ar.display());
-                self.command
-                    .env(format!("AR_{triple_underscored}"), ar)
-                    .env(format!("RANLIB_{triple_underscored}"), ranlib);
-            }
-
-            if let Ok(cxx) = builder.cxx(target) {
-                let cxx = ccacheify(&cxx);
-                let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ");
-                self.command
-                    .env(format!("CXX_{triple_underscored}"), &cxx)
-                    .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags);
-            }
-        }
-
-        self
-    }
-}
-
-impl From<Cargo> for BootstrapCommand {
-    fn from(mut cargo: Cargo) -> BootstrapCommand {
-        let rustflags = &cargo.rustflags.0;
-        if !rustflags.is_empty() {
-            cargo.command.env("RUSTFLAGS", rustflags);
-        }
-
-        let rustdocflags = &cargo.rustdocflags.0;
-        if !rustdocflags.is_empty() {
-            cargo.command.env("RUSTDOCFLAGS", rustdocflags);
-        }
-
-        let encoded_hostflags = cargo.hostflags.encode();
-        if !encoded_hostflags.is_empty() {
-            cargo.command.env("RUSTC_HOST_FLAGS", encoded_hostflags);
-        }
-
-        if !cargo.allow_features.is_empty() {
-            cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
-        }
-        cargo.command
-    }
-}
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 695a66834d4..21c5f7232a1 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -637,7 +637,7 @@ mod dist {
         assert_eq!(first(builder.cache.all::<test::Crate>()), &[test::Crate {
             compiler: Compiler { host, stage: 0 },
             target: host,
-            mode: Mode::Std,
+            mode: crate::Mode::Std,
             crates: vec!["std".to_owned()],
         },]);
     }
diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
index 1b98d541693..410f0f92e60 100644
--- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
@@ -56,9 +56,9 @@ ENV \
     CFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \
     CXX_x86_64_fortanix_unknown_sgx=clang++-11 \
     CXXFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \
-    AR_i686_unknown_freebsd=i686-unknown-freebsd12-ar \
-    CC_i686_unknown_freebsd=i686-unknown-freebsd12-clang \
-    CXX_i686_unknown_freebsd=i686-unknown-freebsd12-clang++ \
+    AR_i686_unknown_freebsd=i686-unknown-freebsd13-ar \
+    CC_i686_unknown_freebsd=i686-unknown-freebsd13-clang \
+    CXX_i686_unknown_freebsd=i686-unknown-freebsd13-clang++ \
     CC_aarch64_unknown_uefi=clang-11 \
     CXX_aarch64_unknown_uefi=clang++-11 \
     CC_i686_unknown_uefi=clang-11 \
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
index b3c5f41bdd7..fd0f5da8c49 100644
--- a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:18.04
+FROM ubuntu:22.04
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
   clang \
@@ -29,9 +29,9 @@ COPY scripts/cmake.sh /scripts/
 RUN /scripts/cmake.sh
 
 ENV \
-    AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-ar \
-    CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang \
-    CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang++
+    AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-ar \
+    CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang \
+    CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang++
 
 ENV HOSTS=x86_64-unknown-freebsd
 
diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh
index 0d02636db91..4826b81d56c 100755
--- a/src/ci/docker/scripts/freebsd-toolchain.sh
+++ b/src/ci/docker/scripts/freebsd-toolchain.sh
@@ -5,8 +5,8 @@ set -eux
 
 arch=$1
 binutils_version=2.40
-freebsd_version=12.3
-triple=$arch-unknown-freebsd12
+freebsd_version=13.2
+triple=$arch-unknown-freebsd13
 sysroot=/usr/local/$triple
 
 hide_output() {
@@ -59,7 +59,7 @@ done
 
 # Originally downloaded from:
 # URL=https://download.freebsd.org/ftp/releases/${freebsd_arch}/${freebsd_version}-RELEASE/base.txz
-URL=https://ci-mirrors.rust-lang.org/rustc/2022-05-06-freebsd-${freebsd_version}-${freebsd_arch}-base.txz
+URL=https://ci-mirrors.rust-lang.org/rustc/2024-02-18-freebsd-${freebsd_version}-${freebsd_arch}-base.txz
 curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}"
 
 # Clang can do cross-builds out of the box, if we give it the right
@@ -68,7 +68,7 @@ curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}"
 # there might be other problems.)
 #
 # The --target option is last because the cross-build of LLVM uses
-# --target without an OS version ("-freebsd" vs. "-freebsd12").  This
+# --target without an OS version ("-freebsd" vs. "-freebsd13").  This
 # makes Clang default to libstdc++ (which no longer exists), and also
 # controls other features, like GNU-style symbol table hashing and
 # anything predicated on the version number in the __FreeBSD__
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index d84b904442c..2b636604049 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -51,7 +51,7 @@ envs:
     RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
     MACOSX_DEPLOYMENT_TARGET: 10.12
     MACOSX_STD_DEPLOYMENT_TARGET: 10.12
-    SELECT_XCODE: /Applications/Xcode_14.3.1.app
+    SELECT_XCODE: /Applications/Xcode_15.2.app
     NO_LLVM_ASSERTIONS: 1
     NO_DEBUG_ASSERTIONS: 1
     NO_OVERFLOW_CHECKS: 1
@@ -287,7 +287,7 @@ auto:
       RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
       MACOSX_DEPLOYMENT_TARGET: 10.12
-      SELECT_XCODE: /Applications/Xcode_14.3.1.app
+      SELECT_XCODE: /Applications/Xcode_15.2.app
       NO_LLVM_ASSERTIONS: 1
       NO_DEBUG_ASSERTIONS: 1
       NO_OVERFLOW_CHECKS: 1
@@ -303,7 +303,7 @@ auto:
       RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc --set target.aarch64-apple-ios-macabi.sanitizers=false --set target.x86_64-apple-ios-macabi.sanitizers=false
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
       MACOSX_DEPLOYMENT_TARGET: 10.12
-      SELECT_XCODE: /Applications/Xcode_14.3.1.app
+      SELECT_XCODE: /Applications/Xcode_15.2.app
       NO_LLVM_ASSERTIONS: 1
       NO_DEBUG_ASSERTIONS: 1
       NO_OVERFLOW_CHECKS: 1
@@ -332,7 +332,7 @@ auto:
         --set llvm.ninja=false
         --set rust.lto=thin
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
-      SELECT_XCODE: /Applications/Xcode_14.3.1.app
+      SELECT_XCODE: /Applications/Xcode_15.4.app
       USE_XCODE_CLANG: 1
       MACOSX_DEPLOYMENT_TARGET: 11.0
       MACOSX_STD_DEPLOYMENT_TARGET: 11.0
@@ -352,7 +352,7 @@ auto:
         --enable-profiler
         --set rust.jemalloc
       RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
-      SELECT_XCODE: /Applications/Xcode_14.3.1.app
+      SELECT_XCODE: /Applications/Xcode_15.4.app
       USE_XCODE_CLANG: 1
       MACOSX_DEPLOYMENT_TARGET: 11.0
       MACOSX_STD_DEPLOYMENT_TARGET: 11.0
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 022fc9da7e0..e9c73ef1c2d 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -100,7 +100,7 @@ target | notes
 [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29)
 [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3)
 `s390x-unknown-linux-gnu` | S390x Linux (kernel 3.2, glibc 2.17)
-`x86_64-unknown-freebsd` | 64-bit FreeBSD
+`x86_64-unknown-freebsd` | 64-bit FreeBSD (version 13.2)
 `x86_64-unknown-illumos` | illumos
 `x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3
 [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64
@@ -166,7 +166,7 @@ target | std | notes
 `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87]
 [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI]
 [`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI]
-`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI]
+`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD (version 13.2) [^x86_32-floats-return-ABI]
 `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI]
 [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI
 [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI)
@@ -257,7 +257,7 @@ target | std | host | notes
 [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? |  | ARM64 TEEOS |
 [`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? |  | ARM64 QNX Neutrino 7.0 RTOS |
 [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ |  | ARM64 QNX Neutrino 7.1 RTOS |
-`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
+`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD (version 13.2)
 [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ |  | ARM64 Hermit
 `aarch64-unknown-illumos` | ✓ | ✓ | ARM64 illumos
 `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI)
@@ -276,14 +276,14 @@ target | std | host | notes
 `armv4t-unknown-linux-gnueabi` | ? |  | Armv4T Linux
 [`armv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * |  | Bare Armv5TE
 `armv5te-unknown-linux-uclibceabi` | ? |  | Armv5TE Linux with uClibc
-`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD
+`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD (version 13.2)
 [`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv6 NetBSD w/hard-float
 [`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? |  | Armv6k Nintendo 3DS, Horizon (Requires devkitARM toolchain)
 [`armv7-rtems-eabihf`](platform-support/armv7-rtems-eabihf.md) | ? |  | RTEMS OS for ARM BSPs
 [`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ |  | Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain)
 [`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | Armv7-A Linux with uClibc, softfloat
 [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat
-`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD
+`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD (version 13.2)
 [`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float
 [`armv7-unknown-trusty`](platform-support/trusty.md) | ? |  |
 [`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ |  | Armv7-A for VxWorks
@@ -342,9 +342,9 @@ target | std | host | notes
 [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * |  |
 [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ |  |
 [`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  |
-`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2)
-`powerpc64le-unknown-freebsd` |   |   | PPC64LE FreeBSD
-`powerpc-unknown-freebsd` |   |   | PowerPC FreeBSD
+`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2, version 13.2)
+`powerpc64le-unknown-freebsd` |   |   | PPC64LE FreeBSD (version 13.2)
+`powerpc-unknown-freebsd` |   |   | PowerPC FreeBSD (version 13.2)
 `powerpc64-unknown-linux-musl` | ? |  | 64-bit PowerPC Linux with musl 1.2.3
 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  |
 `powerpc64le-unknown-linux-musl` | ? |  | 64-bit PowerPC Linux with musl 1.2.3, Little Endian
@@ -360,7 +360,7 @@ target | std | host | notes
 [`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ |  | RISC-V ESP-IDF
 [`riscv32-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  |
 [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ |   | RISC-V Hermit
-`riscv64gc-unknown-freebsd` |   |   | RISC-V FreeBSD
+`riscv64gc-unknown-freebsd` |   |   | RISC-V FreeBSD (version 13.2)
 `riscv64gc-unknown-fuchsia` |   |   | RISC-V Fuchsia
 [`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD
 [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64
diff --git a/src/doc/rustc/src/platform-support/arm-none-eabi.md b/src/doc/rustc/src/platform-support/arm-none-eabi.md
index de0ef322fa6..9732df4be7f 100644
--- a/src/doc/rustc/src/platform-support/arm-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/arm-none-eabi.md
@@ -42,16 +42,15 @@ their own document.
 There are two 32-bit instruction set architectures (ISAs) defined by Arm:
 
 - The [*A32 ISA*][a32-isa], with fixed-width 32-bit instructions. Previously
-  known as the *Arm* ISA, this originated with the original Arm1 of 1985 and has
+  known as the *Arm* ISA, this originated with the original ARM1 of 1985 and has
   been updated by various revisions to the architecture specifications ever
   since.
 - The [*T32 ISA*][t32-isa], with a mix of 16-bit and 32-bit width instructions.
   Note that this term includes both the original 16-bit width *Thumb* ISA
   introduced with the Armv4T architecture in 1994, and the later 16/32-bit sized
-  *Thumb-2* ISA introduced with the Armv6T2 architecture in 2003.
-
-Again, these ISAs have been revised by subsequent revisions to the relevant Arm
-architecture specifications.
+  *Thumb-2* ISA introduced with the Armv6T2 architecture in 2003. Again, these
+  ISAs have been revised by subsequent revisions to the relevant Arm
+  architecture specifications.
 
 There is also a 64-bit ISA with fixed-width 32-bit instructions called the *A64
 ISA*, but targets which implement that instruction set generally start with
@@ -106,10 +105,14 @@ features you do not have available, leaving you with the optimized instruction
 scheduling and support for the features you do have. More details are available
 in the detailed target-specific documentation.
 
-**Note:** Many target-features are currently unstable and subject to change, and
+<div class="warning">
+
+Many target-features are currently unstable and subject to change, and
 if you use them you should disassemble the compiler output and manually inspect
 it to ensure only appropriate instructions for your CPU have been generated.
 
+</div>
+
 If you wish to use the *target-cpu* and *target-feature* options, you can add
 them to your `.cargo/config.toml` file alongside any other flags your project
 uses (likely linker related ones):
diff --git a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
index f25ef0383b1..11c9486cb76 100644
--- a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
@@ -35,9 +35,9 @@ to use these flags.
 | CPU        | FPU | DSP | Target CPU  | Target Features |
 | ---------- | --- | --- | ----------- | --------------- |
 | Any        | No  | Yes | None        | None            |
-| Cortex-M4  | No  | Yes | `cortex-m4` | `+soft-float`   |
+| Cortex-M4  | No  | Yes | `cortex-m4` | `-fpregs`       |
 | Cortex-M4F | SP  | Yes | `cortex-m4` | None            |
-| Cortex-M7  | No  | Yes | `cortex-m7` | `+soft-float`   |
+| Cortex-M7  | No  | Yes | `cortex-m7` | `-fpregs`       |
 | Cortex-M7F | SP  | Yes | `cortex-m7` | `-fp64`         |
 | Cortex-M7F | DP  | Yes | `cortex-m7` | None            |
 
@@ -50,6 +50,13 @@ to use these flags.
 | Cortex-M7F | SP  | Yes | `cortex-m7` | `-fp64`         |
 | Cortex-M7F | DP  | Yes | `cortex-m7` | None            |
 
+<div class="warning">
+
+Never use the `-fpregs` *target-feature* with the `thumbv7em-none-eabihf` target
+as it will cause compilation units to have different ABIs, which is unsound.
+
+</div>
+
 ### Arm Cortex-M4 and Arm Cortex-M4F
 
 The target CPU is `cortex-m4`.
@@ -59,7 +66,7 @@ The target CPU is `cortex-m4`.
   * enabled by default with this *target*
 * Cortex-M4F has a single precision FPU
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 
 ### Arm Cortex-M7 and Arm Cortex-M7F
 
@@ -71,4 +78,4 @@ The target CPU is `cortex-m7`.
 * Cortex-M7F have either a single-precision or double-precision FPU
   * double-precision support is enabled by default with this *target-cpu*
     * opt-out by using the `-f64` *target-feature*
-  * disable support entirely using the `+soft-float` feature (`eabi` only)
+  * disable support entirely using the `-fpregs` *target-feature* (`eabi` only)
diff --git a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
index 4e696f9c304..82fdc5b21cf 100644
--- a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
@@ -39,22 +39,22 @@ to use these flags.
 | CPU         | FPU | DSP | MVE       | Target CPU    | Target Features       |
 | ----------- | --- | --- | --------- | ------------- | --------------------- |
 | Unspecified | No  | No  | No        | None          | None                  |
-| Cortex-M33  | No  | No  | No        | `cortex-m33`  | `+soft-float,-dsp`    |
-| Cortex-M33  | No  | Yes | No        | `cortex-m33`  | `+soft-float`         |
+| Cortex-M33  | No  | No  | No        | `cortex-m33`  | `-fpregs,-dsp`        |
+| Cortex-M33  | No  | Yes | No        | `cortex-m33`  | `-fpregs`             |
 | Cortex-M33  | SP  | No  | No        | `cortex-m33`  | `-dsp`                |
 | Cortex-M33  | SP  | Yes | No        | `cortex-m33`  | None                  |
-| Cortex-M35P | No  | No  | No        | `cortex-m35p` | `+soft-float,-dsp`    |
-| Cortex-M35P | No  | Yes | No        | `cortex-m35p` | `+soft-float`         |
+| Cortex-M35P | No  | No  | No        | `cortex-m35p` | `-fpregs,-dsp`        |
+| Cortex-M35P | No  | Yes | No        | `cortex-m35p` | `-fpregs`             |
 | Cortex-M35P | SP  | No  | No        | `cortex-m35p` | `-dsp`                |
 | Cortex-M35P | SP  | Yes | No        | `cortex-m35p` | None                  |
-| Cortex-M55  | No  | Yes | No        | `cortex-m55`  | `+soft-float,-mve`    |
+| Cortex-M55  | No  | Yes | No        | `cortex-m55`  | `-fpregs,-mve`        |
 | Cortex-M55  | DP  | Yes | No        | `cortex-m55`  | `-mve`                |
-| Cortex-M55  | No  | Yes | Int       | `cortex-m55`  | `+soft-float,-mve.fp` |
+| Cortex-M55  | No  | Yes | Int       | `cortex-m55`  | `-fpregs,-mve.fp,+mve`|
 | Cortex-M55  | DP  | Yes | Int       | `cortex-m55`  | `-mve.fp`             |
 | Cortex-M55  | DP  | Yes | Int+Float | `cortex-m55`  | None                  |
-| Cortex-M85  | No  | Yes | No        | `cortex-m85`  | `+soft-float,-mve`    |
+| Cortex-M85  | No  | Yes | No        | `cortex-m85`  | `-fpregs,-mve`        |
 | Cortex-M85  | DP  | Yes | No        | `cortex-m85`  | `-mve`                |
-| Cortex-M85  | No  | Yes | Int       | `cortex-m85`  | `+soft-float,-mve.fp` |
+| Cortex-M85  | No  | Yes | Int       | `cortex-m85`  | `-fpregs,-mve.fp,+mve`|
 | Cortex-M85  | DP  | Yes | Int       | `cortex-m85`  | `-mve.fp`             |
 | Cortex-M85  | DP  | Yes | Int+Float | `cortex-m85`  | None                  |
 
@@ -74,6 +74,19 @@ to use these flags.
 | Cortex-M85  | DP  | Yes | Int       | `cortex-m85`  | `-mve.fp`             |
 | Cortex-M85  | DP  | Yes | Int+Float | `cortex-m85`  | None                  |
 
+*Technically* you can use this hard-float ABI on a CPU which has no FPU but does
+have Integer MVE, because MVE provides the same set of registers as the FPU
+(including `s0` and `d0`). The particular set of flags that might enable this
+unusual scenario are currently not recorded here.
+
+<div class="warning">
+
+Never use the `-fpregs` *target-feature* with the `thumbv8m.main-none-eabihf`
+target as it will cause compilation units to have different ABIs, which is
+unsound.
+
+</div>
+
 ### Arm Cortex-M33
 
 The target CPU is `cortex-m33`.
@@ -83,7 +96,7 @@ The target CPU is `cortex-m33`.
   * enabled by default with this *target-cpu*
 * Has an optional single precision FPU
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 
 ### Arm Cortex-M35P
 
@@ -94,7 +107,7 @@ The target CPU is `cortex-m35p`.
   * enabled by default with this *target-cpu*
 * Has an optional single precision FPU
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 
 ### Arm Cortex-M55
 
@@ -106,7 +119,7 @@ The target CPU is `cortex-m55`.
 * Has an optional double-precision FPU that also supports half-precision FP16
   values
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 * Has optional support for M-Profile Vector Extensions
   * Also known as *Helium Technology*
   * Available with only integer support, or both integer/float support
@@ -125,7 +138,7 @@ The target CPU is `cortex-m85`.
 * Has an optional double-precision FPU that also supports half-precision FP16
   values
   * support is enabled by default with this *target-cpu*
-  * disable support using the `+soft-float` feature (`eabi` only)
+  * disable support using the `-fpregs` *target-feature* (`eabi` only)
 * Has optional support for M-Profile Vector Extensions
   * Also known as *Helium Technology*
   * Available with only integer support, or both integer/float support
diff --git a/src/doc/unstable-book/src/language-features/result-ffi-guarantees.md b/src/doc/unstable-book/src/language-features/result-ffi-guarantees.md
deleted file mode 100644
index dc9c196524e..00000000000
--- a/src/doc/unstable-book/src/language-features/result-ffi-guarantees.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# `result_ffi_guarantees`
-
-The tracking issue for this feature is: [#110503]
-
-[#110503]: https://github.com/rust-lang/rust/issues/110503
-
-------------------------
-
-This feature adds the possibility of using `Result<T, E>` in FFI if T's niche
-value can be used to describe E or vise-versa.
-
-See [RFC 3391] for more information.
-
-[RFC 3391]: https://github.com/rust-lang/rfcs/blob/master/text/3391-result_ffi_guarantees.md
diff --git a/src/doc/unstable-book/src/language-features/strict-provenance.md b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md
index dc60f3f375d..81bdf07a86a 100644
--- a/src/doc/unstable-book/src/language-features/strict-provenance.md
+++ b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md
@@ -1,18 +1,17 @@
-# `strict_provenance`
+# `strict_provenance_lints`
 
 The tracking issue for this feature is: [#95228]
 
 [#95228]: https://github.com/rust-lang/rust/issues/95228
 -----
 
-The `strict_provenance` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints.
+The `strict_provenance_lints` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints.
 These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model.
-The same feature gate is also used for the experimental strict provenance API in `std` (actually `core`).
 
 ## Example
 
 ```rust
-#![feature(strict_provenance)]
+#![feature(strict_provenance_lints)]
 #![warn(fuzzy_provenance_casts)]
 
 fn main() {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 1ddad917b78..7d4d8d8941d 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1817,7 +1817,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
                         // Only anon consts can implicitly capture params.
                         // FIXME: is this correct behavior?
                         let param_env = cx.tcx.param_env(*def_id);
-                        ct.normalize(cx.tcx, param_env)
+                        cx.tcx.normalize_erasing_regions(param_env, ct)
                     } else {
                         ct
                     };
@@ -2033,8 +2033,8 @@ pub(crate) fn clean_middle_ty<'tcx>(
             Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)),
             format!("{pat:?}").into_boxed_str(),
         ),
-        ty::Array(ty, mut n) => {
-            n = n.normalize(cx.tcx, ty::ParamEnv::reveal_all());
+        ty::Array(ty, n) => {
+            let n = cx.tcx.normalize_erasing_regions(cx.param_env, n);
             let n = print_const(cx, n);
             Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into())
         }
diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml
index 7f7cd3672b7..14ff1d08816 100644
--- a/src/rustdoc-json-types/Cargo.toml
+++ b/src/rustdoc-json-types/Cargo.toml
@@ -11,7 +11,7 @@ default = ["rustc-hash"]
 
 [dependencies]
 serde = { version = "1.0", features = ["derive"] }
-rustc-hash = { version = "1.1.0", optional = true }
+rustc-hash = { version = "2.0", optional = true }
 
 [dev-dependencies]
 serde_json = "1.0"
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index c4e142342a8..f553a78d766 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -2,6 +2,18 @@
 //!
 //! These types are the public API exposed through the `--output-format json` flag. The [`Crate`]
 //! struct is the root of the JSON blob and all other items are contained within.
+//!
+//! We expose a `rustc-hash` feature that is disabled by default. This feature switches the
+//! [`std::collections::HashMap`] for [`rustc_hash::FxHashMap`] to improve the performance of said
+//! `HashMap` in specific situations.
+//!
+//! `cargo-semver-checks` for example, saw a [-3% improvement][1] when benchmarking using the
+//! `aws_sdk_ec2` JSON output (~500MB of JSON). As always, we recommend measuring the impact before
+//! turning this feature on, as [`FxHashMap`][2] only concerns itself with hash speed, and may
+//! increase the number of collisions.
+//!
+//! [1]: https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/rustc-hash.20and.20performance.20of.20rustdoc-types/near/474855731
+//! [2]: https://crates.io/crates/rustc-hash
 
 #[cfg(not(feature = "rustc-hash"))]
 use std::collections::HashMap;
@@ -305,10 +317,10 @@ pub enum AssocItemConstraintKind {
 // FIXME(aDotInTheVoid): Consider making this non-public in rustdoc-types.
 pub struct Id(pub u32);
 
-/// The fundamental kind of an item. Unlike [`ItemEnum`], this does not carry any aditional info.
+/// The fundamental kind of an item. Unlike [`ItemEnum`], this does not carry any additional info.
 ///
 /// Part of [`ItemSummary`].
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum ItemKind {
     /// A module declaration, e.g. `mod foo;` or `mod foo {}`
@@ -698,7 +710,7 @@ pub enum Abi {
     Aapcs { unwind: bool },
     /// Can be specified as `extern "win64"`.
     Win64 { unwind: bool },
-    /// Can be specifed as `extern "sysv64"`.
+    /// Can be specified as `extern "sysv64"`.
     SysV64 { unwind: bool },
     /// Can be specified as `extern "system"`.
     System { unwind: bool },
@@ -892,7 +904,7 @@ pub enum GenericBound {
 }
 
 /// A set of modifiers applied to a trait.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum TraitBoundModifier {
     /// Marks the absence of a modifier.
@@ -996,7 +1008,7 @@ pub enum Type {
     QualifiedPath {
         /// The name of the associated type in the parent type.
         ///
-        /// ```ignore (incomplete expresssion)
+        /// ```ignore (incomplete expression)
         /// <core::array::IntoIter<u32, 42> as Iterator>::Item
         /// //                                            ^^^^
         /// ```
@@ -1083,7 +1095,7 @@ pub struct FunctionSignature {
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Trait {
     /// Whether the trait is marked `auto` and is thus implemented automatically
-    /// for all aplicable types.
+    /// for all applicable types.
     pub is_auto: bool,
     /// Whether the trait is marked as `unsafe`.
     pub is_unsafe: bool,
@@ -1193,7 +1205,7 @@ pub struct ProcMacro {
 }
 
 /// The way a [`ProcMacro`] is declared to be used.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum MacroKind {
     /// A bang macro `foo!()`.
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 f4c00d8287d..00f83237224 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
@@ -190,15 +190,6 @@ fn is_same_generics<'tcx>(
         .enumerate()
         .skip(1) // skip `Self` implicit arg
         .all(|(arg_index, arg)| {
-            if [
-                implied_by_generics.host_effect_index,
-                implied_generics.host_effect_index,
-            ]
-            .contains(&Some(arg_index))
-            {
-                // skip host effect params in determining whether generics are same
-                return true;
-            }
             if let Some(ty) = arg.as_type() {
                 if let &ty::Param(ty::ParamTy { index, .. }) = ty.kind()
                     // `index == 0` means that it's referring to `Self`,
diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
index 22e9674714f..ae2c3e0491f 100644
--- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
@@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
             if let Some(range) = higher::Range::hir(index) {
                 // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
                 if let ty::Array(_, s) = ty.kind() {
-                    let size: u128 = if let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env) {
+                    let size: u128 = if let Some(size) = s.try_to_target_usize(cx.tcx) {
                         size.into()
                     } else {
                         return;
@@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
                             && let ty::Uint(utype) = cx.typeck_results().expr_ty(index).kind()
                             && *utype == ty::UintTy::Usize
                             && let ty::Array(_, s) = ty.kind()
-                            && let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env)
+                            && let Some(size) = s.try_to_target_usize(cx.tcx)
                         {
                             // get constant offset and check whether it is in bounds
                             let off = usize::try_from(off).unwrap();
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
index b134af500f5..022c6bcc70b 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -30,7 +30,7 @@ pub(super) fn check(
                 return;
             }
         } else if count
-            .try_eval_target_usize(cx.tcx, cx.param_env)
+            .try_to_target_usize(cx.tcx)
             .map_or(true, |x| x > 32)
             && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN)
         {
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
index 68d063ad5e5..af089451759 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
@@ -472,7 +472,7 @@ fn is_array_length_equal_to_range(cx: &LateContext<'_>, start: &Expr<'_>, end: &
     let arr_ty = cx.typeck_results().expr_ty(arr).peel_refs();
 
     if let ty::Array(_, s) = arr_ty.kind() {
-        let size: u128 = if let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env) {
+        let size: u128 = if let Some(size) = s.try_to_target_usize(cx.tcx) {
             size.into()
         } else {
             return false;
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index 214b8b0f379..2e6442156ef 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -207,7 +207,7 @@ fn is_end_eq_array_len<'tcx>(
     if let ExprKind::Lit(lit) = end.kind
         && let ast::LitKind::Int(end_int, _) = lit.node
         && let ty::Array(_, arr_len_const) = indexed_ty.kind()
-        && let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env)
+        && let Some(arr_len) = arr_len_const.try_to_target_usize(cx.tcx)
     {
         return match limits {
             ast::RangeLimits::Closed => end_int.get() + 1 >= arr_len.into(),
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index 2eda238ae8c..047d070a131 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -11,7 +11,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{Visitor, walk_pat};
 use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef};
+use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef};
 use rustc_span::{Span, sym};
 
 use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
@@ -67,7 +67,6 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tc
             if v.has_enum {
                 let cx = PatCtxt {
                     tcx: cx.tcx,
-                    param_env: cx.param_env,
                     typeck,
                     arena: DroplessArena::default(),
                 };
@@ -185,7 +184,6 @@ impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> {
 /// The context needed to manipulate a `PatState`.
 struct PatCtxt<'tcx> {
     tcx: TyCtxt<'tcx>,
-    param_env: ParamEnv<'tcx>,
     typeck: &'tcx TypeckResults<'tcx>,
     arena: DroplessArena,
 }
@@ -334,7 +332,7 @@ impl<'a> PatState<'a> {
                 if match *cx.typeck.pat_ty(pat).peel_refs().kind() {
                     ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()),
                     ty::Tuple(tys) => !tys.is_empty(),
-                    ty::Array(_, len) => len.try_eval_target_usize(cx.tcx, cx.param_env) != Some(1),
+                    ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1),
                     ty::Slice(..) => true,
                     _ => false,
                 } =>
@@ -353,7 +351,7 @@ impl<'a> PatState<'a> {
             },
             PatKind::Slice([sub_pat], _, []) | PatKind::Slice([], _, [sub_pat])
                 if let ty::Array(_, len) = *cx.typeck.pat_ty(pat).kind()
-                    && len.try_eval_target_usize(cx.tcx, cx.param_env) == Some(1) =>
+                    && len.try_to_target_usize(cx.tcx) == Some(1) =>
             {
                 self.add_pat(cx, sub_pat)
             },
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
index f198849c5c0..9d462bd1845 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
@@ -31,14 +31,14 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) ->
         // parameter.
         substs
             .const_at(1)
-            .try_eval_target_usize(cx.tcx, cx.param_env)
+            .try_to_target_usize(cx.tcx)
             .map(u128::from)
     } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did)
         && let ExprKind::MethodCall(_, recv, ..) = iter.kind
     {
         if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() {
             // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..)
-            len.try_eval_target_usize(cx.tcx, cx.param_env).map(u128::from)
+            len.try_to_target_usize(cx.tcx).map(u128::from)
         } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) {
             match args {
                 VecArgs::Vec(vec) => vec.len().try_into().ok(),
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index cf0ee569f13..6e39e7be2c4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -18,7 +18,7 @@ pub(super) fn derefs_to_slice<'tcx>(
             ty::Slice(_) => true,
             ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => may_slice(cx, boxed),
             ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec),
-            ty::Array(_, size) => size.try_eval_target_usize(cx.tcx, cx.param_env).is_some(),
+            ty::Array(_, size) => size.try_to_target_usize(cx.tcx).is_some(),
             ty::Ref(_, inner, _) => may_slice(cx, *inner),
             _ => false,
         }
diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
index db8c63892b8..52bb7c4bd68 100644
--- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
+++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::has_repr_attr;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{Const, FeedConstTy};
+use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
@@ -55,16 +55,14 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray {
 
 fn is_struct_with_trailing_zero_sized_array<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool {
     if let ItemKind::Struct(data, _) = &item.kind
-        // First check if last field is an array
         && let Some(last_field) = data.fields().last()
-        && let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind
-
-        // Then check if that array is zero-sized
-        && let length = Const::from_const_arg(cx.tcx, length, FeedConstTy::No)
-        && let length = length.try_eval_target_usize(cx.tcx, cx.param_env)
-        && let Some(length) = length
+        && let field_ty = cx
+            .tcx
+            .normalize_erasing_regions(cx.param_env, cx.tcx.type_of(last_field.def_id).instantiate_identity())
+        && let ty::Array(_, array_len) = *field_ty.kind()
+        && let Some(0) = array_len.try_to_target_usize(cx.tcx)
     {
-        length == 0
+        true
     } else {
         false
     }
diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
index 3da8a449a7c..07d0f59b91c 100644
--- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
@@ -190,7 +190,7 @@ fn all_bindings_are_for_conv<'tcx>(
                     tys.len() == elements.len() && tys.iter().chain(final_tys.iter().copied()).all_equal()
                 },
                 (ToType::Tuple, ty::Array(ty, len)) => {
-                    let Some(len) = len.try_eval_target_usize(cx.tcx, cx.param_env) else { return false };
+                    let Some(len) = len.try_to_target_usize(cx.tcx) else { return false };
                     len as usize == elements.len() && final_tys.iter().chain(once(ty)).all_equal()
                 },
                 _ => false,
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index a1cfb7be647..67c31abbdda 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -472,7 +472,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
             ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
             ExprKind::Repeat(value, _) => {
                 let n = match self.typeck.expr_ty(e).kind() {
-                    ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?,
+                    ty::Array(_, n) => n.try_to_target_usize(self.tcx)?,
                     _ => span_bug!(e.span, "typeck error"),
                 };
                 self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
@@ -553,7 +553,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
             ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()),
             ExprKind::Repeat(..) => {
                 if let ty::Array(_, n) = self.typeck.expr_ty(e).kind() {
-                    Some(n.try_eval_target_usize(self.tcx, self.param_env)? == 0)
+                    Some(n.try_to_target_usize(self.tcx)? == 0)
                 } else {
                     span_bug!(e.span, "typeck error");
                 }
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index b7a3569ccf0..07c3d0eada0 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -274,23 +274,10 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
         .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into()))
         .collect::<Vec<_>>();
 
-    // If an effect arg was not specified, we need to specify it.
-    let effect_arg = if tcx
-        .generics_of(trait_id)
-        .host_effect_index
-        .is_some_and(|x| args.get(x - 1).is_none())
-    {
-        Some(GenericArg::from(callee_id.map_or(tcx.consts.true_, |def_id| {
-            tcx.expected_host_effect_param_for_body(def_id)
-        })))
-    } else {
-        None
-    };
-
     let trait_ref = TraitRef::new(
         tcx,
         trait_id,
-        [GenericArg::from(ty)].into_iter().chain(args).chain(effect_arg),
+        [GenericArg::from(ty)].into_iter().chain(args),
     );
 
     debug_assert_matches!(
@@ -989,7 +976,7 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
         (Ok(size), _) => size,
         (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(),
         (Err(_), ty::Array(t, n)) => {
-            n.try_eval_target_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t)
+            n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t)
         },
         (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
             .variants()
@@ -1207,7 +1194,7 @@ impl<'tcx> InteriorMut<'tcx> {
         let chain = match *ty.kind() {
             ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain(cx, inner_ty),
             ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain(cx, inner_ty),
-            ty::Array(inner_ty, size) if size.try_eval_target_usize(cx.tcx, cx.param_env) != Some(0) => {
+            ty::Array(inner_ty, size) if size.try_to_target_usize(cx.tcx) != Some(0) => {
                 self.interior_mut_ty_chain(cx, inner_ty)
             },
             ty::Tuple(fields) => fields.iter().find_map(|ty| self.interior_mut_ty_chain(cx, ty)),
diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml
index 1318a1f4476..cc8ecefd530 100644
--- a/src/tools/jsondoclint/Cargo.toml
+++ b/src/tools/jsondoclint/Cargo.toml
@@ -9,7 +9,7 @@ edition = "2021"
 anyhow = "1.0.62"
 clap = { version = "4.0.15", features = ["derive"] }
 fs-err = "2.8.1"
-rustc-hash = "1.1.0"
+rustc-hash = "2.0.0"
 rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" }
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0.85"
diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs
index fa77ab1e011..833e41df537 100644
--- a/src/tools/linkchecker/main.rs
+++ b/src/tools/linkchecker/main.rs
@@ -100,6 +100,7 @@ fn main() {
         links_ignored_external: 0,
         links_ignored_exception: 0,
         intra_doc_exceptions: 0,
+        has_broken_urls: false,
     };
     checker.walk(&docs, &mut report);
     report.report();
@@ -116,6 +117,8 @@ struct Checker {
 
 struct Report {
     errors: u32,
+    // Used to provide help message to remind the user to register a page in `SUMMARY.md`.
+    has_broken_urls: bool,
     start: Instant,
     html_files: u32,
     html_redirects: u32,
@@ -274,6 +277,7 @@ impl Checker {
                     report.links_ignored_exception += 1;
                 } else {
                     report.errors += 1;
+                    report.has_broken_urls = true;
                     println!("{}:{}: broken link - `{}`", pretty_path, i, target_pretty_path);
                 }
                 return;
@@ -438,6 +442,13 @@ impl Report {
         println!("number of links ignored due to exceptions: {}", self.links_ignored_exception);
         println!("number of intra doc links ignored: {}", self.intra_doc_exceptions);
         println!("errors found: {}", self.errors);
+
+        if self.has_broken_urls {
+            eprintln!(
+                "NOTE: if you are adding or renaming a markdown file in a mdBook, don't forget to \
+                register the page in SUMMARY.md"
+            );
+        }
     }
 }
 
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index 59776a484b5..0799b93dbb0 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -1,8 +1,7 @@
 use either::Either;
 use rustc_apfloat::{Float, Round};
-use rustc_middle::ty::FloatTy;
-use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
-use rustc_middle::{mir, ty};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::{mir, ty, ty::FloatTy};
 use rustc_span::{Symbol, sym};
 use rustc_target::abi::{Endian, HasDataLayout};
 
@@ -633,9 +632,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
                 let index = generic_args[2]
                     .expect_const()
-                    .eval(*this.tcx, this.param_env(), this.tcx.span)
+                    .try_to_valtree()
                     .unwrap()
-                    .1
+                    .0
                     .unwrap_branch();
                 let index_len = index.len();
 
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 9814858beaa..660f2e493bc 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -11,8 +11,6 @@
 #![feature(let_chains)]
 #![feature(trait_upcasting)]
 #![feature(strict_overflow_ops)]
-#![feature(strict_provenance)]
-#![feature(exposed_provenance)]
 #![feature(pointer_is_aligned_to)]
 #![feature(unqualified_local_imports)]
 // Configure clippy and other lints
diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs
index 3e20b8da622..c63e926376d 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs
+++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs
@@ -1,4 +1,3 @@
-#![feature(strict_provenance)]
 use std::ptr;
 
 fn direct_raw(x: *const (i32, i32)) -> *const i32 {
diff --git a/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs b/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs
index fa40f942b8f..b22c1b4c5e8 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs
+++ b/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_box.rs
@@ -1,6 +1,5 @@
 // Should be caught even without retagging
 //@compile-flags: -Zmiri-disable-stacked-borrows
-#![feature(strict_provenance)]
 use std::ptr::{self, addr_of_mut};
 
 // Deref'ing a dangling raw pointer is fine, but for a dangling box it is not.
diff --git a/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs b/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs
index 036ef2580a8..eeab7c333e5 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs
+++ b/src/tools/miri/tests/fail/dangling_pointers/deref_dangling_ref.rs
@@ -1,6 +1,5 @@
 // Should be caught even without retagging
 //@compile-flags: -Zmiri-disable-stacked-borrows
-#![feature(strict_provenance)]
 use std::ptr::{self, addr_of_mut};
 
 // Deref'ing a dangling raw pointer is fine, but for a dangling reference it is not.
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs
index c307dfddb6c..0acda559d3a 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs
@@ -1,4 +1,3 @@
-#![feature(strict_provenance)]
 use core::ptr;
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs b/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs
index d6bbfd7d94c..da381596284 100644
--- a/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs
+++ b/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs
@@ -1,4 +1,3 @@
-#![feature(strict_provenance)]
 use std::mem;
 
 #[repr(C, usize)]
diff --git a/src/tools/miri/tests/fail/provenance/provenance_transmute.rs b/src/tools/miri/tests/fail/provenance/provenance_transmute.rs
index bc5dd53dcf5..d72f10530d7 100644
--- a/src/tools/miri/tests/fail/provenance/provenance_transmute.rs
+++ b/src/tools/miri/tests/fail/provenance/provenance_transmute.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(strict_provenance)]
 
 use std::mem;
 
diff --git a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs
index f89378fcb3c..a43ba13e13d 100644
--- a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs
+++ b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(strict_provenance, exposed_provenance)]
 
 fn main() {
     let x: i32 = 3;
diff --git a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
index 512473cd894..d4479f32e56 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 @@
-#![feature(strict_provenance, exposed_provenance)]
 
 // Ensure that a `ptr::without_provenance` ptr is truly invalid.
 fn main() {
diff --git a/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs b/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs
index c8be521ef82..53a16ce5d1b 100644
--- a/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs
+++ b/src/tools/miri/tests/fail/provenance/ptr_invalid_offset.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-strict-provenance
-#![feature(strict_provenance)]
 
 fn main() {
     let x = 22;
diff --git a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs
index d7b54f640f6..4674ba841b4 100644
--- a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs
+++ b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-strict-provenance
-#![feature(exposed_provenance)]
 
 fn main() {
     let addr = &0 as *const i32 as usize;
diff --git a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs
index 608ab718919..dfd7d4bb0be 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(exposed_provenance)]
 
 // If we have only exposed read-only pointers, doing a write through a wildcard ptr should fail.
 
diff --git a/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs
index e075db66039..009ce1eb882 100644
--- a/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs
+++ b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs
@@ -1,6 +1,5 @@
 //@compile-flags: -Zmiri-symbolic-alignment-check
 //@revisions: call_unaligned_ptr read_unaligned_ptr
-#![feature(strict_provenance)]
 
 #[path = "../../utils/mod.rs"]
 mod utils;
diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs
index 954571f4a22..1cf70d9d9a3 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs
+++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs
@@ -1,7 +1,6 @@
 //@compile-flags: -Zmiri-disable-validation
 //@error-in-other-file: memory is uninitialized at [0x4..0x8]
 //@normalize-stderr-test: "a[0-9]+" -> "ALLOC"
-#![feature(strict_provenance)]
 #![allow(dropping_copy_types)]
 
 // Test printing allocations that contain single-byte provenance.
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs
index 8aa8c7dcb8e..3c4311efc4c 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs
@@ -3,7 +3,6 @@
 //@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4
 #![feature(io_error_more)]
 #![feature(pointer_is_aligned_to)]
-#![feature(strict_provenance)]
 
 use std::mem::{size_of, size_of_val};
 
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs
index 3ea34513376..288c1d41f30 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs
@@ -1,6 +1,5 @@
 //@only-target: linux
 
-#![feature(strict_provenance)]
 use std::convert::TryInto;
 
 fn main() {
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
index c399616b9ae..10be78798a6 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
@@ -1,4 +1,4 @@
-#![feature(strict_provenance, pointer_is_aligned_to)]
+#![feature(pointer_is_aligned_to)]
 use std::{mem, ptr, slice};
 
 fn test_memcpy() {
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
index f3261eaa43c..f07007fa705 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
@@ -2,7 +2,6 @@
 //@compile-flags: -Zmiri-disable-isolation
 #![feature(io_error_more)]
 #![feature(pointer_is_aligned_to)]
-#![feature(strict_provenance)]
 
 use std::mem::transmute;
 
diff --git a/src/tools/miri/tests/pass-dep/libc/mmap.rs b/src/tools/miri/tests/pass-dep/libc/mmap.rs
index db305acbf4a..bfd840d2fb8 100644
--- a/src/tools/miri/tests/pass-dep/libc/mmap.rs
+++ b/src/tools/miri/tests/pass-dep/libc/mmap.rs
@@ -1,6 +1,5 @@
 //@ignore-target: windows # No mmap on Windows
 //@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance
-#![feature(strict_provenance)]
 
 use std::io::Error;
 use std::{ptr, slice};
diff --git a/src/tools/miri/tests/pass/align_offset_symbolic.rs b/src/tools/miri/tests/pass/align_offset_symbolic.rs
index 9647277821f..c836a3b07cb 100644
--- a/src/tools/miri/tests/pass/align_offset_symbolic.rs
+++ b/src/tools/miri/tests/pass/align_offset_symbolic.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-symbolic-alignment-check
-#![feature(strict_provenance)]
 
 use std::mem;
 
diff --git a/src/tools/miri/tests/pass/atomic.rs b/src/tools/miri/tests/pass/atomic.rs
index f84fe825d01..2b2e89e6d70 100644
--- a/src/tools/miri/tests/pass/atomic.rs
+++ b/src/tools/miri/tests/pass/atomic.rs
@@ -2,7 +2,7 @@
 //@[tree]compile-flags: -Zmiri-tree-borrows
 //@compile-flags: -Zmiri-strict-provenance
 
-#![feature(strict_provenance, strict_provenance_atomic_ptr)]
+#![feature(strict_provenance_atomic_ptr)]
 // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
 #![allow(static_mut_refs)]
 
diff --git a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs
index 0a61db960c1..63329118283 100644
--- a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs
+++ b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs
@@ -5,7 +5,6 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
 #![feature(allocator_api)]
-#![feature(strict_provenance)]
 
 use std::alloc::{AllocError, Allocator, Layout};
 use std::cell::{Cell, UnsafeCell};
diff --git a/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs
index 255f4061abc..a293dd0bef1 100644
--- a/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs
+++ b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs
@@ -1,7 +1,6 @@
 //! Regression test for <https://github.com/rust-lang/miri/issues/3450>:
 //! When the address gets reused, there should be a happens-before relation.
 //@compile-flags: -Zmiri-address-reuse-cross-thread-rate=1.0
-#![feature(strict_provenance)]
 #![feature(sync_unsafe_cell)]
 
 use std::cell::SyncUnsafeCell;
diff --git a/src/tools/miri/tests/pass/const-addrs.rs b/src/tools/miri/tests/pass/const-addrs.rs
index 727c67ebfb5..af68b28b2b8 100644
--- a/src/tools/miri/tests/pass/const-addrs.rs
+++ b/src/tools/miri/tests/pass/const-addrs.rs
@@ -7,7 +7,6 @@
 // MIR inlining will put every evaluation of the const we're repeatedly evaluating into the same
 // stack frame, breaking this test.
 //@compile-flags: -Zinline-mir=no
-#![feature(strict_provenance)]
 
 const EVALS: usize = 256;
 
diff --git a/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs
index 43ddc8a4d8b..2f8665e8d62 100644
--- a/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs
+++ b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs
@@ -1,4 +1,4 @@
-#![feature(custom_mir, core_intrinsics, strict_provenance)]
+#![feature(custom_mir, core_intrinsics)]
 use std::intrinsics::mir::*;
 
 // The `Drop` terminator on a type with no drop glue should be a NOP.
diff --git a/src/tools/miri/tests/pass/extern_types.rs b/src/tools/miri/tests/pass/extern_types.rs
index eade5c6ac08..08e9caf4f1c 100644
--- a/src/tools/miri/tests/pass/extern_types.rs
+++ b/src/tools/miri/tests/pass/extern_types.rs
@@ -1,6 +1,6 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
-#![feature(extern_types, strict_provenance)]
+#![feature(extern_types)]
 
 use std::ptr;
 
diff --git a/src/tools/miri/tests/pass/provenance.rs b/src/tools/miri/tests/pass/provenance.rs
index 2e4d240cc48..8c52b703190 100644
--- a/src/tools/miri/tests/pass/provenance.rs
+++ b/src/tools/miri/tests/pass/provenance.rs
@@ -1,6 +1,5 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
-#![feature(strict_provenance)]
 use std::{mem, ptr};
 
 const PTR_SIZE: usize = mem::size_of::<&i32>();
diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs
index ade5f537785..9a76c8dd349 100644
--- a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs
+++ b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs
@@ -2,7 +2,6 @@
 // Tree Borrows doesn't support int2ptr casts, but let's make sure we don't immediately crash either.
 //@[tree]compile-flags: -Zmiri-tree-borrows
 //@[stack]compile-flags: -Zmiri-permissive-provenance
-#![feature(strict_provenance, exposed_provenance)]
 
 use std::ptr;
 
diff --git a/src/tools/miri/tests/pass/ptr_raw.rs b/src/tools/miri/tests/pass/ptr_raw.rs
index dcf13d97ce3..c958dfddf69 100644
--- a/src/tools/miri/tests/pass/ptr_raw.rs
+++ b/src/tools/miri/tests/pass/ptr_raw.rs
@@ -1,4 +1,3 @@
-#![feature(strict_provenance)]
 use std::mem;
 use std::ptr::{self, addr_of};
 
diff --git a/src/tools/miri/tests/pass/shims/ptr_mask.rs b/src/tools/miri/tests/pass/shims/ptr_mask.rs
index fb8bb6b13db..6872ba577d2 100644
--- a/src/tools/miri/tests/pass/shims/ptr_mask.rs
+++ b/src/tools/miri/tests/pass/shims/ptr_mask.rs
@@ -1,5 +1,4 @@
 #![feature(ptr_mask)]
-#![feature(strict_provenance)]
 
 fn main() {
     let v: u32 = 0xABCDABCD;
diff --git a/src/tools/miri/tests/pass/slices.rs b/src/tools/miri/tests/pass/slices.rs
index 39e1d1cb97f..dd18a061cdd 100644
--- a/src/tools/miri/tests/pass/slices.rs
+++ b/src/tools/miri/tests/pass/slices.rs
@@ -4,7 +4,6 @@
 #![feature(slice_as_chunks)]
 #![feature(slice_partition_dedup)]
 #![feature(layout_for_ptr)]
-#![feature(strict_provenance)]
 
 use std::{ptr, slice};
 
diff --git a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs
index c89d79b42e3..8a05fca3f31 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(exposed_provenance)]
 use std::ptr;
 
 // Just to make sure that casting a ref to raw, to int and back to raw
diff --git a/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs b/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs
index 1478b21d6c1..e507f49b955 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs
@@ -2,7 +2,6 @@
 // printing, not how it interacts with the GC.
 //@compile-flags: -Zmiri-permissive-provenance -Zmiri-provenance-gc=0
 
-#![feature(strict_provenance)]
 use std::alloc::{self, Layout};
 use std::mem::ManuallyDrop;
 
diff --git a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs
index 55356814a1a..b0f53283cda 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs
@@ -1,5 +1,4 @@
 //@compile-flags: -Zmiri-permissive-provenance
-#![feature(exposed_provenance)]
 
 use std::ptr;
 
diff --git a/src/tools/miri/tests/pass/transmute_ptr.rs b/src/tools/miri/tests/pass/transmute_ptr.rs
index ce6d86b7068..0944781c6de 100644
--- a/src/tools/miri/tests/pass/transmute_ptr.rs
+++ b/src/tools/miri/tests/pass/transmute_ptr.rs
@@ -1,6 +1,5 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
-#![feature(strict_provenance)]
 use std::{mem, ptr};
 
 fn t1() {
diff --git a/src/tools/miri/tests/pass/underscore_pattern.rs b/src/tools/miri/tests/pass/underscore_pattern.rs
index f59bb9f5c82..2f203bdc01e 100644
--- a/src/tools/miri/tests/pass/underscore_pattern.rs
+++ b/src/tools/miri/tests/pass/underscore_pattern.rs
@@ -1,5 +1,4 @@
 // Various tests ensuring that underscore patterns really just construct the place, but don't check its contents.
-#![feature(strict_provenance)]
 #![feature(never_type)]
 
 use std::ptr;
diff --git a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
index a3356b682a6..6c0d9bc253a 100644
--- a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
+++ b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
@@ -1,5 +1,4 @@
 //! Tests specific for <https://github.com/rust-lang/rust/issues/117945>: zero-sized operations.
-#![feature(strict_provenance)]
 
 use std::ptr;
 
diff --git a/src/tools/miropt-test-tools/src/lib.rs b/src/tools/miropt-test-tools/src/lib.rs
index 4317f23a822..41b53d2ad0e 100644
--- a/src/tools/miropt-test-tools/src/lib.rs
+++ b/src/tools/miropt-test-tools/src/lib.rs
@@ -129,6 +129,9 @@ pub fn files_for_miropt_test(
 
             out.push(MiroptTestFile { expected_file, from_file, to_file });
         }
+        if !run_filecheck && l.trim_start().starts_with("// CHECK") {
+            panic!("error: test contains filecheck directive but is marked `skip-filecheck`");
+        }
     }
 
     MiroptTest { run_filecheck, suffix, files: out, passes }
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index 928867be64f..42e608ff5ce 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -13,7 +13,7 @@ walkdir = "2"
 ignore = "0.4.18"
 semver = "1.0"
 termcolor = "1.1.3"
-rustc-hash = "1.1.0"
+rustc-hash = "2.0.0"
 fluent-syntax = "0.11.1"
 similar = "2.5.0"
 
diff --git a/tests/codegen/atomicptr.rs b/tests/codegen/atomicptr.rs
index ea8b382c8fc..e8c5e6a6749 100644
--- a/tests/codegen/atomicptr.rs
+++ b/tests/codegen/atomicptr.rs
@@ -6,7 +6,6 @@
 
 //@ compile-flags: -O -Cno-prepopulate-passes
 #![crate_type = "lib"]
-#![feature(strict_provenance)]
 #![feature(strict_provenance_atomic_ptr)]
 
 use std::ptr::without_provenance_mut;
diff --git a/tests/codegen/issues/issue-103285-ptr-addr-overflow-check.rs b/tests/codegen/issues/issue-103285-ptr-addr-overflow-check.rs
index d4a74b3d782..122f02fbbc5 100644
--- a/tests/codegen/issues/issue-103285-ptr-addr-overflow-check.rs
+++ b/tests/codegen/issues/issue-103285-ptr-addr-overflow-check.rs
@@ -1,7 +1,6 @@
 //@ compile-flags: -O -C debug-assertions=yes
 
 #![crate_type = "lib"]
-#![feature(strict_provenance)]
 
 #[no_mangle]
 pub fn test(src: *const u8, dst: *const u8) -> usize {
diff --git a/tests/debuginfo/constant-ordering-prologue.rs b/tests/debuginfo/constant-ordering-prologue.rs
index f2d4fd37ce3..3136aff238a 100644
--- a/tests/debuginfo/constant-ordering-prologue.rs
+++ b/tests/debuginfo/constant-ordering-prologue.rs
@@ -20,11 +20,11 @@
 // lldb-command:run
 
 // lldb-command:print a
-// lldb-check: = 19
+// lldb-check: 19
 // lldb-command:print b
-// lldb-check: = 20
+// lldb-check: 20
 // lldb-command:print c
-// lldb-check: = 21.5
+// lldb-check: 21.5
 
 fn binding(a: i64, b: u64, c: f64) {
     let x = 0;
diff --git a/tests/mir-opt/dest-prop/union.rs b/tests/mir-opt/dest-prop/union.rs
index 4e6fb71bf75..85eded09980 100644
--- a/tests/mir-opt/dest-prop/union.rs
+++ b/tests/mir-opt/dest-prop/union.rs
@@ -1,4 +1,3 @@
-// skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 //! Tests that we can propagate into places that are projections into unions
 //@ compile-flags: -Zunsound-mir-opts -C debuginfo=full
@@ -8,7 +7,7 @@ fn val() -> u32 {
 
 // EMIT_MIR union.main.DestinationPropagation.diff
 fn main() {
-    // CHECK-LABEL: fn args(
+    // CHECK-LABEL: fn main(
     // CHECK: {{_.*}} = Un { us: const 1_u32 };
     union Un {
         us: u32,
diff --git a/tests/mir-opt/gvn_ptr_eq_with_constant.rs b/tests/mir-opt/gvn_ptr_eq_with_constant.rs
index d8025072ee3..05445208e07 100644
--- a/tests/mir-opt/gvn_ptr_eq_with_constant.rs
+++ b/tests/mir-opt/gvn_ptr_eq_with_constant.rs
@@ -5,8 +5,6 @@
 
 // Regression for <https://github.com/rust-lang/rust/issues/127089>
 
-#![feature(strict_provenance)]
-
 struct Foo<T>(std::marker::PhantomData<T>);
 
 impl<T> Foo<T> {
diff --git a/tests/mir-opt/issues/issue_59352.rs b/tests/mir-opt/issues/issue_59352.rs
index 5c06b7e56f7..9024dc976e4 100644
--- a/tests/mir-opt/issues/issue_59352.rs
+++ b/tests/mir-opt/issues/issue_59352.rs
@@ -1,4 +1,3 @@
-// skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 // This test is a mirror of codegen/issue-59352.rs.
 // The LLVM inliner doesn't inline `char::method::is_digit()` and so it doesn't recognize this case
diff --git a/tests/run-make/rustc-crates-on-stable/rmake.rs b/tests/run-make/rustc-crates-on-stable/rmake.rs
index 81cc775c919..9fbc675cc9a 100644
--- a/tests/run-make/rustc-crates-on-stable/rmake.rs
+++ b/tests/run-make/rustc-crates-on-stable/rmake.rs
@@ -31,6 +31,10 @@ fn main() {
             "rustc_pattern_analysis",
             "-p",
             "rustc_lexer",
+            "-p",
+            "rustc_abi",
+            "-p",
+            "rustc_parse_format",
         ])
         .run();
 }
diff --git a/tests/rustdoc/impossible-default.rs b/tests/rustdoc/impossible-default.rs
index fad64068010..c9444004d5c 100644
--- a/tests/rustdoc/impossible-default.rs
+++ b/tests/rustdoc/impossible-default.rs
@@ -18,3 +18,11 @@ pub trait Foo {
 pub struct Bar([u8]);
 
 impl Foo for Bar {}
+
+//@ has foo/struct.Generic.html '//*[@id="method.needs_sized"]//h4[@class="code-header"]' \
+// "fn needs_sized"
+//@ has foo/struct.Generic.html '//*[@id="method.no_needs_sized"]//h4[@class="code-header"]' \
+// "fn no_needs_sized"
+pub struct Generic<T: ?Sized>(T);
+
+impl<T: ?Sized> Foo for Generic<T> {}
diff --git a/tests/ui/argument-suggestions/issue-109425.fixed b/tests/ui/argument-suggestions/issue-109425.fixed
index 5d96f457c88..4b3aaa46d86 100644
--- a/tests/ui/argument-suggestions/issue-109425.fixed
+++ b/tests/ui/argument-suggestions/issue-109425.fixed
@@ -15,6 +15,10 @@ fn main() {
     //~^ error: this function takes 1 argument but 3 arguments were supplied
     is(0, ""); // is(0, "")
     //~^ error: this function takes 2 arguments but 4 arguments were supplied
+    is(1, "");
+    //~^ error: this function takes 2 arguments but 4 arguments were supplied
+    is(1, "");
+    //~^ error: this function takes 2 arguments but 4 arguments were supplied
     s("");     // s("")
     //~^ error: this function takes 1 argument but 3 arguments were supplied
 }
diff --git a/tests/ui/argument-suggestions/issue-109425.rs b/tests/ui/argument-suggestions/issue-109425.rs
index bb9d37ee0ff..56816681337 100644
--- a/tests/ui/argument-suggestions/issue-109425.rs
+++ b/tests/ui/argument-suggestions/issue-109425.rs
@@ -15,6 +15,10 @@ fn main() {
     //~^ error: this function takes 1 argument but 3 arguments were supplied
     is(0, 1, 2, ""); // is(0, "")
     //~^ error: this function takes 2 arguments but 4 arguments were supplied
+    is((), 1, "", ());
+    //~^ error: this function takes 2 arguments but 4 arguments were supplied
+    is(1, (), "", ());
+    //~^ error: this function takes 2 arguments but 4 arguments were supplied
     s(0, 1, "");     // s("")
     //~^ error: this function takes 1 argument but 3 arguments were supplied
 }
diff --git a/tests/ui/argument-suggestions/issue-109425.stderr b/tests/ui/argument-suggestions/issue-109425.stderr
index bfe007793d4..2cd53ed528e 100644
--- a/tests/ui/argument-suggestions/issue-109425.stderr
+++ b/tests/ui/argument-suggestions/issue-109425.stderr
@@ -74,9 +74,47 @@ LL -     is(0, 1, 2, ""); // is(0, "")
 LL +     is(0, ""); // is(0, "")
    |
 
-error[E0061]: this function takes 1 argument but 3 arguments were supplied
+error[E0061]: this function takes 2 arguments but 4 arguments were supplied
   --> $DIR/issue-109425.rs:18:5
    |
+LL |     is((), 1, "", ());
+   |     ^^ --         -- unexpected argument #4 of type `()`
+   |        |
+   |        unexpected argument #1 of type `()`
+   |
+note: function defined here
+  --> $DIR/issue-109425.rs:5:4
+   |
+LL | fn is(_: u32, _: &str) {}
+   |    ^^ ------  -------
+help: remove the extra arguments
+   |
+LL -     is((), 1, "", ());
+LL +     is(1, "");
+   |
+
+error[E0061]: this function takes 2 arguments but 4 arguments were supplied
+  --> $DIR/issue-109425.rs:20:5
+   |
+LL |     is(1, (), "", ());
+   |     ^^    --      -- unexpected argument #4 of type `()`
+   |           |
+   |           unexpected argument #2 of type `()`
+   |
+note: function defined here
+  --> $DIR/issue-109425.rs:5:4
+   |
+LL | fn is(_: u32, _: &str) {}
+   |    ^^ ------  -------
+help: remove the extra arguments
+   |
+LL -     is(1, (), "", ());
+LL +     is(1, "");
+   |
+
+error[E0061]: this function takes 1 argument but 3 arguments were supplied
+  --> $DIR/issue-109425.rs:22:5
+   |
 LL |     s(0, 1, "");     // s("")
    |     ^ -  - unexpected argument #2 of type `{integer}`
    |       |
@@ -93,6 +131,6 @@ LL -     s(0, 1, "");     // s("")
 LL +     s("");     // s("")
    |
 
-error: aborting due to 5 previous errors
+error: aborting due to 7 previous errors
 
 For more information about this error, try `rustc --explain E0061`.
diff --git a/tests/ui/argument-suggestions/issue-112507.stderr b/tests/ui/argument-suggestions/issue-112507.stderr
index 908ed35c669..e2ea9af7dc2 100644
--- a/tests/ui/argument-suggestions/issue-112507.stderr
+++ b/tests/ui/argument-suggestions/issue-112507.stderr
@@ -18,8 +18,9 @@ LL |     Float(Option<f64>),
    |     ^^^^^
 help: remove the extra arguments
    |
-LL ~         ,
-LL ~         None);
+LL -         0,
+LL -         None,
+LL +         None);
    |
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs
index 0c9df7ecd78..b2b4934aa5f 100644
--- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs
@@ -2,8 +2,6 @@
 //@ compile-flags: -Copt-level=2
 //@ run-pass
 
-#![feature(exposed_provenance)]
-
 use std::ptr;
 
 fn main() {
diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs
index b188b794d1f..bf130c9f759 100644
--- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs
@@ -4,8 +4,6 @@
 
 // Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1434203908
 
-#![feature(exposed_provenance)]
-
 use std::ptr;
 
 fn f() -> usize {
diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs
index 7f64e23b9c1..cdf07eade87 100644
--- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs
@@ -4,8 +4,6 @@
 
 // Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
 
-#![feature(exposed_provenance)]
-
 use std::ptr;
 
 #[inline(never)]
diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs
index 3417296ce12..f128e1bb084 100644
--- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs
@@ -4,8 +4,6 @@
 
 // Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
 
-#![feature(exposed_provenance)]
-
 use std::ptr;
 
 #[inline(never)]
diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs
index e1e9e3f46b8..0baf8886395 100644
--- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs
@@ -4,8 +4,6 @@
 
 // https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
 
-#![feature(exposed_provenance)]
-
 use std::ptr;
 
 fn main() {
diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs
index 8d581e8c9e9..c7f46318aae 100644
--- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs
@@ -4,8 +4,6 @@
 
 // https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
 
-#![feature(exposed_provenance)]
-
 use std::ptr;
 
 fn main() {
diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs
index 506f114cd2a..b163c282d93 100644
--- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs
@@ -4,8 +4,6 @@
 
 // https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
 
-#![feature(exposed_provenance)]
-
 use std::{
     cell::{Ref, RefCell},
     ptr,
diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs
index 603db5e08f4..7ccff8d0848 100644
--- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs
@@ -4,8 +4,6 @@
 
 // Derived from https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
 
-#![feature(exposed_provenance)]
-
 use std::ptr;
 
 fn main() {
diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs
index 0243c2bfe95..4602ec654d1 100644
--- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs
@@ -2,8 +2,6 @@
 //@ compile-flags: -Copt-level=2
 //@ run-pass
 
-#![feature(strict_provenance)]
-
 use std::ptr;
 
 fn main() {
diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs
index 29758036a21..789a78c15b4 100644
--- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs
@@ -4,8 +4,6 @@
 
 // Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1434203908
 
-#![feature(strict_provenance)]
-
 use std::ptr;
 
 fn f() -> usize {
diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs
index 11925261a65..5f4ee731f7d 100644
--- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs
@@ -4,8 +4,6 @@
 
 // Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
 
-#![feature(strict_provenance)]
-
 use std::ptr;
 
 #[inline(never)]
diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs
index e628bb90faa..0414879804a 100644
--- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs
@@ -4,8 +4,6 @@
 
 // Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
 
-#![feature(strict_provenance)]
-
 use std::ptr;
 
 #[inline(never)]
diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs
index 075e3475a32..b7165ce1e5c 100644
--- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs
@@ -4,8 +4,6 @@
 
 // https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
 
-#![feature(strict_provenance)]
-
 use std::ptr;
 
 fn main() {
diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs
index 6d7b6fa33e0..a02ff30918d 100644
--- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs
@@ -4,8 +4,6 @@
 
 // https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
 
-#![feature(strict_provenance)]
-
 use std::ptr;
 
 fn main() {
diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs
index 67660d285a4..fea41e03612 100644
--- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs
@@ -4,8 +4,6 @@
 
 // https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
 
-#![feature(strict_provenance)]
-
 use std::{
     cell::{Ref, RefCell},
     ptr,
diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs
index a89310f9930..d963e45e4cd 100644
--- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs
+++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs
@@ -4,8 +4,6 @@
 
 // Derived from https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
 
-#![feature(strict_provenance)]
-
 use std::ptr;
 
 fn main() {
diff --git a/tests/ui/feature-gates/feature-gate-result_ffi_guarantees.rs b/tests/ui/feature-gates/feature-gate-result_ffi_guarantees.rs
deleted file mode 100644
index dda317aecc3..00000000000
--- a/tests/ui/feature-gates/feature-gate-result_ffi_guarantees.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-#![allow(dead_code)]
-#![deny(improper_ctypes)]
-#![feature(ptr_internals)]
-
-use std::num;
-
-enum Z {}
-
-#[repr(transparent)]
-struct TransparentStruct<T>(T, std::marker::PhantomData<Z>);
-
-#[repr(transparent)]
-enum TransparentEnum<T> {
-    Variant(T, std::marker::PhantomData<Z>),
-}
-
-struct NoField;
-
-extern "C" {
-    fn result_ref_t(x: Result<&'static u8, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_fn_t(x: Result<extern "C" fn(), ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonnull_t(x: Result<std::ptr::NonNull<u8>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_unique_t(x: Result<std::ptr::Unique<u8>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_u8_t(x: Result<num::NonZero<u8>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_u16_t(x: Result<num::NonZero<u16>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_u32_t(x: Result<num::NonZero<u32>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_u64_t(x: Result<num::NonZero<u64>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_usize_t(x: Result<num::NonZero<usize>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_i8_t(x: Result<num::NonZero<i8>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_i16_t(x: Result<num::NonZero<i16>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_i32_t(x: Result<num::NonZero<i32>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_i64_t(x: Result<num::NonZero<i64>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_isize_t(x: Result<num::NonZero<isize>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_transparent_struct_t(x: Result<TransparentStruct<num::NonZero<u8>>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_transparent_enum_t(x: Result<TransparentEnum<num::NonZero<u8>>, ()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_phantom_t(x: Result<num::NonZero<u8>, std::marker::PhantomData<()>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_1zst_exhaustive_no_variant_t(x: Result<num::NonZero<u8>, Z>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_1zst_exhaustive_no_field_t(x: Result<num::NonZero<u8>, NoField>);
-    //~^ ERROR `extern` block uses type `Result
-
-    fn result_ref_e(x: Result<(), &'static u8>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_fn_e(x: Result<(), extern "C" fn()>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonnull_e(x: Result<(), std::ptr::NonNull<u8>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_unique_e(x: Result<(), std::ptr::Unique<u8>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_u8_e(x: Result<(), num::NonZero<u8>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_u16_e(x: Result<(), num::NonZero<u16>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_u32_e(x: Result<(), num::NonZero<u32>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_u64_e(x: Result<(), num::NonZero<u64>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_usize_e(x: Result<(), num::NonZero<usize>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_i8_e(x: Result<(), num::NonZero<i8>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_i16_e(x: Result<(), num::NonZero<i16>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_i32_e(x: Result<(), num::NonZero<i32>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_i64_e(x: Result<(), num::NonZero<i64>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_nonzero_isize_e(x: Result<(), num::NonZero<isize>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_transparent_struct_e(x: Result<(), TransparentStruct<num::NonZero<u8>>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_transparent_enum_e(x: Result<(), TransparentEnum<num::NonZero<u8>>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_phantom_e(x: Result<num::NonZero<u8>, std::marker::PhantomData<()>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_1zst_exhaustive_no_variant_e(x: Result<Z, num::NonZero<u8>>);
-    //~^ ERROR `extern` block uses type `Result
-    fn result_1zst_exhaustive_no_field_e(x: Result<NoField, num::NonZero<u8>>);
-    //~^ ERROR `extern` block uses type `Result
-}
-
-pub fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-result_ffi_guarantees.stderr b/tests/ui/feature-gates/feature-gate-result_ffi_guarantees.stderr
deleted file mode 100644
index 94416eb99c8..00000000000
--- a/tests/ui/feature-gates/feature-gate-result_ffi_guarantees.stderr
+++ /dev/null
@@ -1,349 +0,0 @@
-error: `extern` block uses type `Result<&u8, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:20:24
-   |
-LL |     fn result_ref_t(x: Result<&'static u8, ()>);
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-note: the lint level is defined here
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:2:9
-   |
-LL | #![deny(improper_ctypes)]
-   |         ^^^^^^^^^^^^^^^
-
-error: `extern` block uses type `Result<extern "C" fn(), ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:22:23
-   |
-LL |     fn result_fn_t(x: Result<extern "C" fn(), ()>);
-   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonNull<u8>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:24:28
-   |
-LL |     fn result_nonnull_t(x: Result<std::ptr::NonNull<u8>, ()>);
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<Unique<u8>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:26:27
-   |
-LL |     fn result_unique_t(x: Result<std::ptr::Unique<u8>, ()>);
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<u8>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:28:31
-   |
-LL |     fn result_nonzero_u8_t(x: Result<num::NonZero<u8>, ()>);
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<u16>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:30:32
-   |
-LL |     fn result_nonzero_u16_t(x: Result<num::NonZero<u16>, ()>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<u32>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:32:32
-   |
-LL |     fn result_nonzero_u32_t(x: Result<num::NonZero<u32>, ()>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<u64>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:34:32
-   |
-LL |     fn result_nonzero_u64_t(x: Result<num::NonZero<u64>, ()>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<usize>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:36:34
-   |
-LL |     fn result_nonzero_usize_t(x: Result<num::NonZero<usize>, ()>);
-   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<i8>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:38:31
-   |
-LL |     fn result_nonzero_i8_t(x: Result<num::NonZero<i8>, ()>);
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<i16>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:40:32
-   |
-LL |     fn result_nonzero_i16_t(x: Result<num::NonZero<i16>, ()>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<i32>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:42:32
-   |
-LL |     fn result_nonzero_i32_t(x: Result<num::NonZero<i32>, ()>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<i64>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:44:32
-   |
-LL |     fn result_nonzero_i64_t(x: Result<num::NonZero<i64>, ()>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<isize>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:46:34
-   |
-LL |     fn result_nonzero_isize_t(x: Result<num::NonZero<isize>, ()>);
-   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<TransparentStruct<NonZero<u8>>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:48:39
-   |
-LL |     fn result_transparent_struct_t(x: Result<TransparentStruct<num::NonZero<u8>>, ()>);
-   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<TransparentEnum<NonZero<u8>>, ()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:50:37
-   |
-LL |     fn result_transparent_enum_t(x: Result<TransparentEnum<num::NonZero<u8>>, ()>);
-   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<u8>, PhantomData<()>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:52:28
-   |
-LL |     fn result_phantom_t(x: Result<num::NonZero<u8>, std::marker::PhantomData<()>>);
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<u8>, Z>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:54:47
-   |
-LL |     fn result_1zst_exhaustive_no_variant_t(x: Result<num::NonZero<u8>, Z>);
-   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<u8>, NoField>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:56:45
-   |
-LL |     fn result_1zst_exhaustive_no_field_t(x: Result<num::NonZero<u8>, NoField>);
-   |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), &u8>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:59:24
-   |
-LL |     fn result_ref_e(x: Result<(), &'static u8>);
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), extern "C" fn()>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:61:23
-   |
-LL |     fn result_fn_e(x: Result<(), extern "C" fn()>);
-   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonNull<u8>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:63:28
-   |
-LL |     fn result_nonnull_e(x: Result<(), std::ptr::NonNull<u8>>);
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), Unique<u8>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:65:27
-   |
-LL |     fn result_unique_e(x: Result<(), std::ptr::Unique<u8>>);
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:67:31
-   |
-LL |     fn result_nonzero_u8_e(x: Result<(), num::NonZero<u8>>);
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<u16>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:69:32
-   |
-LL |     fn result_nonzero_u16_e(x: Result<(), num::NonZero<u16>>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<u32>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:71:32
-   |
-LL |     fn result_nonzero_u32_e(x: Result<(), num::NonZero<u32>>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<u64>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:73:32
-   |
-LL |     fn result_nonzero_u64_e(x: Result<(), num::NonZero<u64>>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<usize>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:75:34
-   |
-LL |     fn result_nonzero_usize_e(x: Result<(), num::NonZero<usize>>);
-   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<i8>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:77:31
-   |
-LL |     fn result_nonzero_i8_e(x: Result<(), num::NonZero<i8>>);
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<i16>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:79:32
-   |
-LL |     fn result_nonzero_i16_e(x: Result<(), num::NonZero<i16>>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<i32>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:81:32
-   |
-LL |     fn result_nonzero_i32_e(x: Result<(), num::NonZero<i32>>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<i64>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:83:32
-   |
-LL |     fn result_nonzero_i64_e(x: Result<(), num::NonZero<i64>>);
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), NonZero<isize>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:85:34
-   |
-LL |     fn result_nonzero_isize_e(x: Result<(), num::NonZero<isize>>);
-   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), TransparentStruct<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:87:39
-   |
-LL |     fn result_transparent_struct_e(x: Result<(), TransparentStruct<num::NonZero<u8>>>);
-   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<(), TransparentEnum<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:89:37
-   |
-LL |     fn result_transparent_enum_e(x: Result<(), TransparentEnum<num::NonZero<u8>>>);
-   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NonZero<u8>, PhantomData<()>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:91:28
-   |
-LL |     fn result_phantom_e(x: Result<num::NonZero<u8>, std::marker::PhantomData<()>>);
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<Z, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:93:47
-   |
-LL |     fn result_1zst_exhaustive_no_variant_e(x: Result<Z, num::NonZero<u8>>);
-   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: `extern` block uses type `Result<NoField, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/feature-gate-result_ffi_guarantees.rs:95:45
-   |
-LL |     fn result_1zst_exhaustive_no_field_e(x: Result<NoField, num::NonZero<u8>>);
-   |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
-   |
-   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
-   = note: enum has no representation hint
-
-error: aborting due to 38 previous errors
-
diff --git a/tests/ui/feature-gates/feature-gate-strict_provenance.rs b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs
index 738c8daa168..738c8daa168 100644
--- a/tests/ui/feature-gates/feature-gate-strict_provenance.rs
+++ b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs
diff --git a/tests/ui/feature-gates/feature-gate-strict_provenance.stderr b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr
index 82078d576ad..15428cbd4be 100644
--- a/tests/ui/feature-gates/feature-gate-strict_provenance.stderr
+++ b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr
@@ -1,24 +1,24 @@
 warning: unknown lint: `fuzzy_provenance_casts`
-  --> $DIR/feature-gate-strict_provenance.rs:3:1
+  --> $DIR/feature-gate-strict_provenance_lints.rs:3:1
    |
 LL | #![deny(fuzzy_provenance_casts)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the `fuzzy_provenance_casts` lint is unstable
-   = note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
-   = help: add `#![feature(strict_provenance)]` to the crate attributes to enable
+   = note: see issue #130351 <https://github.com/rust-lang/rust/issues/130351> for more information
+   = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
    = note: `#[warn(unknown_lints)]` on by default
 
 warning: unknown lint: `lossy_provenance_casts`
-  --> $DIR/feature-gate-strict_provenance.rs:5:1
+  --> $DIR/feature-gate-strict_provenance_lints.rs:5:1
    |
 LL | #![deny(lossy_provenance_casts)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the `lossy_provenance_casts` lint is unstable
-   = note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
-   = help: add `#![feature(strict_provenance)]` to the crate attributes to enable
+   = note: see issue #130351 <https://github.com/rust-lang/rust/issues/130351> for more information
+   = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 warning: 2 warnings emitted
diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs
index cb8e9e80675..19af1de9576 100644
--- a/tests/ui/lint/lint-ctypes-enum.rs
+++ b/tests/ui/lint/lint-ctypes-enum.rs
@@ -2,7 +2,6 @@
 #![deny(improper_ctypes)]
 #![feature(ptr_internals)]
 #![feature(transparent_unions)]
-#![feature(result_ffi_guarantees)]
 
 use std::num;
 
diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr
index bba5b09b69c..8e92e7e6946 100644
--- a/tests/ui/lint/lint-ctypes-enum.stderr
+++ b/tests/ui/lint/lint-ctypes-enum.stderr
@@ -1,5 +1,5 @@
 error: `extern` block uses type `U`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:69:14
+  --> $DIR/lint-ctypes-enum.rs:68:14
    |
 LL |     fn uf(x: U);
    |              ^ not FFI-safe
@@ -7,7 +7,7 @@ LL |     fn uf(x: U);
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
    = note: enum has no representation hint
 note: the type is defined here
-  --> $DIR/lint-ctypes-enum.rs:10:1
+  --> $DIR/lint-ctypes-enum.rs:9:1
    |
 LL | enum U {
    | ^^^^^^
@@ -18,7 +18,7 @@ LL | #![deny(improper_ctypes)]
    |         ^^^^^^^^^^^^^^^
 
 error: `extern` block uses type `B`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:70:14
+  --> $DIR/lint-ctypes-enum.rs:69:14
    |
 LL |     fn bf(x: B);
    |              ^ not FFI-safe
@@ -26,13 +26,13 @@ LL |     fn bf(x: B);
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
    = note: enum has no representation hint
 note: the type is defined here
-  --> $DIR/lint-ctypes-enum.rs:13:1
+  --> $DIR/lint-ctypes-enum.rs:12:1
    |
 LL | enum B {
    | ^^^^^^
 
 error: `extern` block uses type `T`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:71:14
+  --> $DIR/lint-ctypes-enum.rs:70:14
    |
 LL |     fn tf(x: T);
    |              ^ not FFI-safe
@@ -40,13 +40,13 @@ LL |     fn tf(x: T);
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
    = note: enum has no representation hint
 note: the type is defined here
-  --> $DIR/lint-ctypes-enum.rs:17:1
+  --> $DIR/lint-ctypes-enum.rs:16:1
    |
 LL | enum T {
    | ^^^^^^
 
 error: `extern` block uses type `u128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:83:31
+  --> $DIR/lint-ctypes-enum.rs:82:31
    |
 LL |     fn option_nonzero_u128(x: Option<num::NonZero<u128>>);
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -54,7 +54,7 @@ LL |     fn option_nonzero_u128(x: Option<num::NonZero<u128>>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `i128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:90:31
+  --> $DIR/lint-ctypes-enum.rs:89:31
    |
 LL |     fn option_nonzero_i128(x: Option<num::NonZero<i128>>);
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -62,7 +62,7 @@ LL |     fn option_nonzero_i128(x: Option<num::NonZero<i128>>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `Option<TransparentUnion<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:95:36
+  --> $DIR/lint-ctypes-enum.rs:94:36
    |
 LL |     fn option_transparent_union(x: Option<TransparentUnion<num::NonZero<u8>>>);
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -71,7 +71,7 @@ LL |     fn option_transparent_union(x: Option<TransparentUnion<num::NonZero<u8>
    = note: enum has no representation hint
 
 error: `extern` block uses type `Option<Rust<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:97:28
+  --> $DIR/lint-ctypes-enum.rs:96:28
    |
 LL |     fn option_repr_rust(x: Option<Rust<num::NonZero<u8>>>);
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -80,7 +80,7 @@ LL |     fn option_repr_rust(x: Option<Rust<num::NonZero<u8>>>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `u128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:107:33
+  --> $DIR/lint-ctypes-enum.rs:106:33
    |
 LL |     fn result_nonzero_u128_t(x: Result<num::NonZero<u128>, ()>);
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -88,7 +88,7 @@ LL |     fn result_nonzero_u128_t(x: Result<num::NonZero<u128>, ()>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `i128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:114:33
+  --> $DIR/lint-ctypes-enum.rs:113:33
    |
 LL |     fn result_nonzero_i128_t(x: Result<num::NonZero<i128>, ()>);
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -96,7 +96,7 @@ LL |     fn result_nonzero_i128_t(x: Result<num::NonZero<i128>, ()>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `Result<TransparentUnion<NonZero<u8>>, ()>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:119:38
+  --> $DIR/lint-ctypes-enum.rs:118:38
    |
 LL |     fn result_transparent_union_t(x: Result<TransparentUnion<num::NonZero<u8>>, ()>);
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -105,7 +105,7 @@ LL |     fn result_transparent_union_t(x: Result<TransparentUnion<num::NonZero<u
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<Rust<NonZero<u8>>, ()>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:121:30
+  --> $DIR/lint-ctypes-enum.rs:120:30
    |
 LL |     fn result_repr_rust_t(x: Result<Rust<num::NonZero<u8>>, ()>);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -114,7 +114,7 @@ LL |     fn result_repr_rust_t(x: Result<Rust<num::NonZero<u8>>, ()>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonZero<u8>, U>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:125:51
+  --> $DIR/lint-ctypes-enum.rs:124:51
    |
 LL |     fn result_1zst_exhaustive_single_variant_t(x: Result<num::NonZero<u8>, U>);
    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -123,7 +123,7 @@ LL |     fn result_1zst_exhaustive_single_variant_t(x: Result<num::NonZero<u8>,
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonZero<u8>, B>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:127:53
+  --> $DIR/lint-ctypes-enum.rs:126:53
    |
 LL |     fn result_1zst_exhaustive_multiple_variant_t(x: Result<num::NonZero<u8>, B>);
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -132,7 +132,7 @@ LL |     fn result_1zst_exhaustive_multiple_variant_t(x: Result<num::NonZero<u8>
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonZero<u8>, NonExhaustive>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:129:51
+  --> $DIR/lint-ctypes-enum.rs:128:51
    |
 LL |     fn result_1zst_non_exhaustive_no_variant_t(x: Result<num::NonZero<u8>, NonExhaustive>);
    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -141,7 +141,7 @@ LL |     fn result_1zst_non_exhaustive_no_variant_t(x: Result<num::NonZero<u8>,
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonZero<u8>, Field>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:132:49
+  --> $DIR/lint-ctypes-enum.rs:131:49
    |
 LL |     fn result_1zst_exhaustive_single_field_t(x: Result<num::NonZero<u8>, Field>);
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -150,7 +150,7 @@ LL |     fn result_1zst_exhaustive_single_field_t(x: Result<num::NonZero<u8>, Fi
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<Result<(), NonZero<u8>>, ()>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:134:30
+  --> $DIR/lint-ctypes-enum.rs:133:30
    |
 LL |     fn result_cascading_t(x: Result<Result<(), num::NonZero<u8>>, ()>);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -159,7 +159,7 @@ LL |     fn result_cascading_t(x: Result<Result<(), num::NonZero<u8>>, ()>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `u128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:145:33
+  --> $DIR/lint-ctypes-enum.rs:144:33
    |
 LL |     fn result_nonzero_u128_e(x: Result<(), num::NonZero<u128>>);
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -167,7 +167,7 @@ LL |     fn result_nonzero_u128_e(x: Result<(), num::NonZero<u128>>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `i128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:152:33
+  --> $DIR/lint-ctypes-enum.rs:151:33
    |
 LL |     fn result_nonzero_i128_e(x: Result<(), num::NonZero<i128>>);
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -175,7 +175,7 @@ LL |     fn result_nonzero_i128_e(x: Result<(), num::NonZero<i128>>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `Result<(), TransparentUnion<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:157:38
+  --> $DIR/lint-ctypes-enum.rs:156:38
    |
 LL |     fn result_transparent_union_e(x: Result<(), TransparentUnion<num::NonZero<u8>>>);
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -184,7 +184,7 @@ LL |     fn result_transparent_union_e(x: Result<(), TransparentUnion<num::NonZe
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<(), Rust<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:159:30
+  --> $DIR/lint-ctypes-enum.rs:158:30
    |
 LL |     fn result_repr_rust_e(x: Result<(), Rust<num::NonZero<u8>>>);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -193,7 +193,7 @@ LL |     fn result_repr_rust_e(x: Result<(), Rust<num::NonZero<u8>>>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<U, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:163:51
+  --> $DIR/lint-ctypes-enum.rs:162:51
    |
 LL |     fn result_1zst_exhaustive_single_variant_e(x: Result<U, num::NonZero<u8>>);
    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -202,7 +202,7 @@ LL |     fn result_1zst_exhaustive_single_variant_e(x: Result<U, num::NonZero<u8
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<B, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:165:53
+  --> $DIR/lint-ctypes-enum.rs:164:53
    |
 LL |     fn result_1zst_exhaustive_multiple_variant_e(x: Result<B, num::NonZero<u8>>);
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -211,7 +211,7 @@ LL |     fn result_1zst_exhaustive_multiple_variant_e(x: Result<B, num::NonZero<
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonExhaustive, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:167:51
+  --> $DIR/lint-ctypes-enum.rs:166:51
    |
 LL |     fn result_1zst_non_exhaustive_no_variant_e(x: Result<NonExhaustive, num::NonZero<u8>>);
    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -220,7 +220,7 @@ LL |     fn result_1zst_non_exhaustive_no_variant_e(x: Result<NonExhaustive, num
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<Field, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:170:49
+  --> $DIR/lint-ctypes-enum.rs:169:49
    |
 LL |     fn result_1zst_exhaustive_single_field_e(x: Result<Field, num::NonZero<u8>>);
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -229,7 +229,7 @@ LL |     fn result_1zst_exhaustive_single_field_e(x: Result<Field, num::NonZero<
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<(), Result<(), NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:172:30
+  --> $DIR/lint-ctypes-enum.rs:171:30
    |
 LL |     fn result_cascading_e(x: Result<(), Result<(), num::NonZero<u8>>>);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -238,7 +238,7 @@ LL |     fn result_cascading_e(x: Result<(), Result<(), num::NonZero<u8>>>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:174:27
+  --> $DIR/lint-ctypes-enum.rs:173:27
    |
 LL |     fn result_unit_t_e(x: Result<(), ()>);
    |                           ^^^^^^^^^^^^^^ not FFI-safe
diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs
index d2d72a68f13..187209d4e20 100644
--- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs
+++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs
@@ -1,4 +1,4 @@
-#![feature(strict_provenance)]
+#![feature(strict_provenance_lints)]
 #![deny(fuzzy_provenance_casts)]
 
 fn main() {
diff --git a/tests/ui/lint/lint-strict-provenance-lossy-casts.rs b/tests/ui/lint/lint-strict-provenance-lossy-casts.rs
index 9799a053756..395dc75f825 100644
--- a/tests/ui/lint/lint-strict-provenance-lossy-casts.rs
+++ b/tests/ui/lint/lint-strict-provenance-lossy-casts.rs
@@ -1,4 +1,4 @@
-#![feature(strict_provenance)]
+#![feature(strict_provenance_lints)]
 #![deny(lossy_provenance_casts)]
 
 fn main() {
diff --git a/tests/ui/lint/unit_bindings.deny_level.stderr b/tests/ui/lint/unit_bindings.deny_level.stderr
new file mode 100644
index 00000000000..9062f2e5c1f
--- /dev/null
+++ b/tests/ui/lint/unit_bindings.deny_level.stderr
@@ -0,0 +1,40 @@
+error: binding has unit type `()`
+  --> $DIR/unit_bindings.rs:50:5
+   |
+LL |     let _ = expr;
+   |     ^^^^-^^^^^^^^
+   |         |
+   |         this pattern is inferred to be the unit type `()`
+   |
+note: the lint level is defined here
+  --> $DIR/unit_bindings.rs:22:30
+   |
+LL | #![cfg_attr(deny_level, deny(unit_bindings))]
+   |                              ^^^^^^^^^^^^^
+
+error: binding has unit type `()`
+  --> $DIR/unit_bindings.rs:51:5
+   |
+LL |     let pat = expr;
+   |     ^^^^---^^^^^^^^
+   |         |
+   |         this pattern is inferred to be the unit type `()`
+
+error: binding has unit type `()`
+  --> $DIR/unit_bindings.rs:52:5
+   |
+LL |     let _pat = expr;
+   |     ^^^^----^^^^^^^^
+   |         |
+   |         this pattern is inferred to be the unit type `()`
+
+error: binding has unit type `()`
+  --> $DIR/unit_bindings.rs:55:5
+   |
+LL |     let list = v.sort();
+   |     ^^^^----^^^^^^^^^^^^
+   |         |
+   |         this pattern is inferred to be the unit type `()`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/lint/unit_bindings.rs b/tests/ui/lint/unit_bindings.rs
new file mode 100644
index 00000000000..9a682a2244c
--- /dev/null
+++ b/tests/ui/lint/unit_bindings.rs
@@ -0,0 +1,60 @@
+//! Basic checks for `unit_bindings` lint.
+//!
+//! The `unit_bindings` lint tries to detect cases like `let list = list.sort()`. The lint will
+//! trigger on bindings that have the unit `()` type **except** if:
+//!
+//! - The user wrote `()` on either side, i.e.
+//!     - `let () = <expr>;` or `let <expr> = ();`
+//!     - `let _ = ();`
+//! - The binding occurs within macro expansions, e.g. `foo!();`.
+//! - The user explicitly provided type annotations, e.g. `let x: () = <expr>`.
+//!
+//! Examples where the lint *should* fire on include:
+//!
+//! - `let _ = <expr>;`
+//! - `let pat = <expr>;`
+//! - `let _pat = <expr>;`
+
+//@ revisions: default_level deny_level
+//@[default_level] check-pass (`unit_bindings` is currently allow-by-default)
+
+#![allow(unused)]
+#![cfg_attr(deny_level, deny(unit_bindings))]
+
+// The `list` binding below should trigger the lint if it's not contained in a macro expansion.
+macro_rules! expands_to_sus {
+    () => {
+        let mut v = [1, 2, 3];
+        let list = v.sort();
+    }
+}
+
+// No warning for `y` and `z` because it is provided as type parameter.
+fn ty_param_check<T: Copy>(x: T) {
+    let y = x;
+    let z: T = x;
+}
+
+fn main() {
+    // No warning if user explicitly wrote `()` on either side.
+    let expr = ();
+    let () = expr;
+    let _ = ();
+    // No warning if user explicitly annotates the unit type on the binding.
+    let pat: () = expr;
+    // No warning for let bindings with unit type in macro expansions.
+    expands_to_sus!();
+    // No warning for unit bindings in generic fns.
+    ty_param_check(());
+
+    let _ = expr; //[deny_level]~ ERROR binding has unit type
+    let pat = expr; //[deny_level]~ ERROR binding has unit type
+    let _pat = expr; //[deny_level]~ ERROR binding has unit type
+
+    let mut v = [1, 2, 3];
+    let list = v.sort(); //[deny_level]~ ERROR binding has unit type
+
+    // Limitation: the lint currently does not fire on nested unit LHS bindings, i.e.
+    // this will not currently trigger the lint.
+    let (nested, _) = (expr, 0i32);
+}
diff --git a/tests/ui/lint/unused/unused-macro-rules-compile-error.stderr b/tests/ui/lint/unused/unused-macro-rules-compile-error.stderr
index 936428f6a1c..d91af762e68 100644
--- a/tests/ui/lint/unused/unused-macro-rules-compile-error.stderr
+++ b/tests/ui/lint/unused/unused-macro-rules-compile-error.stderr
@@ -10,17 +10,17 @@ note: the lint level is defined here
 LL | #![deny(unused_macro_rules)]
    |         ^^^^^^^^^^^^^^^^^^
 
-error: rule #3 of macro `num2` is never used
-  --> $DIR/unused-macro-rules-compile-error.rs:22:5
-   |
-LL |     (two_) => { compile_error! };
-   |     ^^^^^^
-
 error: rule #2 of macro `num2` is never used
   --> $DIR/unused-macro-rules-compile-error.rs:20:5
    |
 LL |     (two) => { fn compile_error() {} };
    |     ^^^^^
 
+error: rule #3 of macro `num2` is never used
+  --> $DIR/unused-macro-rules-compile-error.rs:22:5
+   |
+LL |     (two_) => { compile_error! };
+   |     ^^^^^^
+
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/lint/unused/unused-macro-rules-decl.stderr b/tests/ui/lint/unused/unused-macro-rules-decl.stderr
index 10ceb3921f3..98f146c5b5b 100644
--- a/tests/ui/lint/unused/unused-macro-rules-decl.stderr
+++ b/tests/ui/lint/unused/unused-macro-rules-decl.stderr
@@ -1,8 +1,8 @@
-error: rule #4 of macro `num` is never used
-  --> $DIR/unused-macro-rules-decl.rs:11:5
+error: rule #2 of macro `num` is never used
+  --> $DIR/unused-macro-rules-decl.rs:9:5
    |
-LL |     (four) => { 4 },
-   |     ^^^^^^
+LL |     (two) => { 2 },
+   |     ^^^^^
    |
 note: the lint level is defined here
   --> $DIR/unused-macro-rules-decl.rs:2:9
@@ -10,11 +10,11 @@ note: the lint level is defined here
 LL | #![deny(unused_macro_rules)]
    |         ^^^^^^^^^^^^^^^^^^
 
-error: rule #2 of macro `num` is never used
-  --> $DIR/unused-macro-rules-decl.rs:9:5
+error: rule #4 of macro `num` is never used
+  --> $DIR/unused-macro-rules-decl.rs:11:5
    |
-LL |     (two) => { 2 },
-   |     ^^^^^
+LL |     (four) => { 4 },
+   |     ^^^^^^
 
 error: rule #3 of macro `num_rec` is never used
   --> $DIR/unused-macro-rules-decl.rs:31:5
diff --git a/tests/ui/lint/unused/unused-macro-rules.stderr b/tests/ui/lint/unused/unused-macro-rules.stderr
index b9258e77805..e50a8a63cea 100644
--- a/tests/ui/lint/unused/unused-macro-rules.stderr
+++ b/tests/ui/lint/unused/unused-macro-rules.stderr
@@ -1,8 +1,8 @@
-error: rule #4 of macro `num` is never used
-  --> $DIR/unused-macro-rules.rs:10:5
+error: rule #2 of macro `num` is never used
+  --> $DIR/unused-macro-rules.rs:8:5
    |
-LL |     (four) => { 4 };
-   |     ^^^^^^
+LL |     (two) => { 2 };
+   |     ^^^^^
    |
 note: the lint level is defined here
   --> $DIR/unused-macro-rules.rs:1:9
@@ -10,11 +10,11 @@ note: the lint level is defined here
 LL | #![deny(unused_macro_rules)]
    |         ^^^^^^^^^^^^^^^^^^
 
-error: rule #2 of macro `num` is never used
-  --> $DIR/unused-macro-rules.rs:8:5
+error: rule #4 of macro `num` is never used
+  --> $DIR/unused-macro-rules.rs:10:5
    |
-LL |     (two) => { 2 };
-   |     ^^^^^
+LL |     (four) => { 4 };
+   |     ^^^^^^
 
 error: rule #3 of macro `num_rec` is never used
   --> $DIR/unused-macro-rules.rs:30:5
diff --git a/tests/ui/mir/alignment/i686-pc-windows-msvc.rs b/tests/ui/mir/alignment/i686-pc-windows-msvc.rs
index 379f61ae818..c6b47a6d679 100644
--- a/tests/ui/mir/alignment/i686-pc-windows-msvc.rs
+++ b/tests/ui/mir/alignment/i686-pc-windows-msvc.rs
@@ -7,8 +7,6 @@
 // that will fail on dereferencing of a pointer to u64 which is not 8-byte-aligned but is
 // 4-byte-aligned.
 
-#![feature(strict_provenance)]
-
 fn main() {
     let mut x = [0u64; 2];
     let ptr = x.as_mut_ptr();
diff --git a/tests/ui/mir/alignment/packed.rs b/tests/ui/mir/alignment/packed.rs
index 1a12425f11a..cf908365e1a 100644
--- a/tests/ui/mir/alignment/packed.rs
+++ b/tests/ui/mir/alignment/packed.rs
@@ -1,8 +1,6 @@
 //@ run-pass
 //@ compile-flags: -C debug-assertions
 
-#![feature(strict_provenance)]
-
 #[repr(packed)]
 struct Misaligner {
     _head: u8,
diff --git a/tests/ui/rfcs/rfc-3391-result-ffi-guarantees.rs b/tests/ui/rfcs/rfc-3391-result-ffi-guarantees.rs
new file mode 100644
index 00000000000..9b8092526b9
--- /dev/null
+++ b/tests/ui/rfcs/rfc-3391-result-ffi-guarantees.rs
@@ -0,0 +1,133 @@
+//@ build-pass
+/*!
+
+The guarantees in RFC 3391 were strengthened as a result of the 2024 Oct 09 T-lang meeting[^1]
+following the precedent of T-lang's guaranteeing[^2] ABI compatibility for "Option-like" enums[^2].
+We now guarantee ABI compatibility for enums that conform to these rules described by scottmcm:
+
+* The enum `E` has exactly two variants.
+* One variant has exactly one field, of type `T`.
+* `T` is a `rustc_nonnull_optimization_guaranteed` type.
+* All fields of the other variant are 1-ZSTs.
+
+Where "all" fields includes "there aren't any fields, so they're vacuously all 1-ZSTs".
+
+Note: "1-ZST" means a type of size 0 and alignment 1.
+
+The reason alignment of the zero-sized type matters is it can affect the alignment of the enum,
+which also will affect its size if the enum has a non-zero size.
+
+[^1]: <https://github.com/rust-lang/rust/pull/130628#issuecomment-2402761599>
+[^2]: <https://github.com/rust-lang/rust/pull/60300#issuecomment-487000474>
+
+*/
+
+#![allow(dead_code)]
+#![deny(improper_ctypes)]
+#![feature(ptr_internals)]
+
+use std::num;
+
+enum Z {}
+
+#[repr(transparent)]
+struct TransparentStruct<T>(T, std::marker::PhantomData<Z>);
+
+#[repr(transparent)]
+enum TransparentEnum<T> {
+    Variant(T, std::marker::PhantomData<Z>),
+}
+
+struct NoField;
+
+extern "C" {
+    fn result_ref_t(x: Result<&'static u8, ()>);
+    fn result_fn_t(x: Result<extern "C" fn(), ()>);
+    fn result_nonnull_t(x: Result<std::ptr::NonNull<u8>, ()>);
+    fn result_unique_t(x: Result<std::ptr::Unique<u8>, ()>);
+    fn result_nonzero_u8_t(x: Result<num::NonZero<u8>, ()>);
+    fn result_nonzero_u16_t(x: Result<num::NonZero<u16>, ()>);
+    fn result_nonzero_u32_t(x: Result<num::NonZero<u32>, ()>);
+    fn result_nonzero_u64_t(x: Result<num::NonZero<u64>, ()>);
+    fn result_nonzero_usize_t(x: Result<num::NonZero<usize>, ()>);
+    fn result_nonzero_i8_t(x: Result<num::NonZero<i8>, ()>);
+    fn result_nonzero_i16_t(x: Result<num::NonZero<i16>, ()>);
+    fn result_nonzero_i32_t(x: Result<num::NonZero<i32>, ()>);
+    fn result_nonzero_i64_t(x: Result<num::NonZero<i64>, ()>);
+    fn result_nonzero_isize_t(x: Result<num::NonZero<isize>, ()>);
+    fn result_transparent_struct_t(x: Result<TransparentStruct<num::NonZero<u8>>, ()>);
+    fn result_transparent_enum_t(x: Result<TransparentEnum<num::NonZero<u8>>, ()>);
+    fn result_phantom_t(x: Result<num::NonZero<u8>, std::marker::PhantomData<()>>);
+    fn result_1zst_exhaustive_no_variant_t(x: Result<num::NonZero<u8>, Z>);
+    fn result_1zst_exhaustive_no_field_t(x: Result<num::NonZero<u8>, NoField>);
+
+    fn result_ref_e(x: Result<(), &'static u8>);
+    fn result_fn_e(x: Result<(), extern "C" fn()>);
+    fn result_nonnull_e(x: Result<(), std::ptr::NonNull<u8>>);
+    fn result_unique_e(x: Result<(), std::ptr::Unique<u8>>);
+    fn result_nonzero_u8_e(x: Result<(), num::NonZero<u8>>);
+    fn result_nonzero_u16_e(x: Result<(), num::NonZero<u16>>);
+    fn result_nonzero_u32_e(x: Result<(), num::NonZero<u32>>);
+    fn result_nonzero_u64_e(x: Result<(), num::NonZero<u64>>);
+    fn result_nonzero_usize_e(x: Result<(), num::NonZero<usize>>);
+    fn result_nonzero_i8_e(x: Result<(), num::NonZero<i8>>);
+    fn result_nonzero_i16_e(x: Result<(), num::NonZero<i16>>);
+    fn result_nonzero_i32_e(x: Result<(), num::NonZero<i32>>);
+    fn result_nonzero_i64_e(x: Result<(), num::NonZero<i64>>);
+    fn result_nonzero_isize_e(x: Result<(), num::NonZero<isize>>);
+    fn result_transparent_struct_e(x: Result<(), TransparentStruct<num::NonZero<u8>>>);
+    fn result_transparent_enum_e(x: Result<(), TransparentEnum<num::NonZero<u8>>>);
+    fn result_phantom_e(x: Result<num::NonZero<u8>, std::marker::PhantomData<()>>);
+    fn result_1zst_exhaustive_no_variant_e(x: Result<Z, num::NonZero<u8>>);
+    fn result_1zst_exhaustive_no_field_e(x: Result<NoField, num::NonZero<u8>>);
+}
+
+// Custom "Result-like" enum for testing custom "Option-like" types are also accepted
+enum Either<L, R> {
+    Left(L),
+    Right(R),
+}
+
+extern "C" {
+    fn either_ref_t(x: Either<&'static u8, ()>);
+    fn either_fn_t(x: Either<extern "C" fn(), ()>);
+    fn either_nonnull_t(x: Either<std::ptr::NonNull<u8>, ()>);
+    fn either_unique_t(x: Either<std::ptr::Unique<u8>, ()>);
+    fn either_nonzero_u8_t(x: Either<num::NonZero<u8>, ()>);
+    fn either_nonzero_u16_t(x: Either<num::NonZero<u16>, ()>);
+    fn either_nonzero_u32_t(x: Either<num::NonZero<u32>, ()>);
+    fn either_nonzero_u64_t(x: Either<num::NonZero<u64>, ()>);
+    fn either_nonzero_usize_t(x: Either<num::NonZero<usize>, ()>);
+    fn either_nonzero_i8_t(x: Either<num::NonZero<i8>, ()>);
+    fn either_nonzero_i16_t(x: Either<num::NonZero<i16>, ()>);
+    fn either_nonzero_i32_t(x: Either<num::NonZero<i32>, ()>);
+    fn either_nonzero_i64_t(x: Either<num::NonZero<i64>, ()>);
+    fn either_nonzero_isize_t(x: Either<num::NonZero<isize>, ()>);
+    fn either_transparent_struct_t(x: Either<TransparentStruct<num::NonZero<u8>>, ()>);
+    fn either_transparent_enum_t(x: Either<TransparentEnum<num::NonZero<u8>>, ()>);
+    fn either_phantom_t(x: Either<num::NonZero<u8>, std::marker::PhantomData<()>>);
+    fn either_1zst_exhaustive_no_variant_t(x: Either<num::NonZero<u8>, Z>);
+    fn either_1zst_exhaustive_no_field_t(x: Either<num::NonZero<u8>, NoField>);
+
+    fn either_ref_e(x: Either<(), &'static u8>);
+    fn either_fn_e(x: Either<(), extern "C" fn()>);
+    fn either_nonnull_e(x: Either<(), std::ptr::NonNull<u8>>);
+    fn either_unique_e(x: Either<(), std::ptr::Unique<u8>>);
+    fn either_nonzero_u8_e(x: Either<(), num::NonZero<u8>>);
+    fn either_nonzero_u16_e(x: Either<(), num::NonZero<u16>>);
+    fn either_nonzero_u32_e(x: Either<(), num::NonZero<u32>>);
+    fn either_nonzero_u64_e(x: Either<(), num::NonZero<u64>>);
+    fn either_nonzero_usize_e(x: Either<(), num::NonZero<usize>>);
+    fn either_nonzero_i8_e(x: Either<(), num::NonZero<i8>>);
+    fn either_nonzero_i16_e(x: Either<(), num::NonZero<i16>>);
+    fn either_nonzero_i32_e(x: Either<(), num::NonZero<i32>>);
+    fn either_nonzero_i64_e(x: Either<(), num::NonZero<i64>>);
+    fn either_nonzero_isize_e(x: Either<(), num::NonZero<isize>>);
+    fn either_transparent_struct_e(x: Either<(), TransparentStruct<num::NonZero<u8>>>);
+    fn either_transparent_enum_e(x: Either<(), TransparentEnum<num::NonZero<u8>>>);
+    fn either_phantom_e(x: Either<num::NonZero<u8>, std::marker::PhantomData<()>>);
+    fn either_1zst_exhaustive_no_variant_e(x: Either<Z, num::NonZero<u8>>);
+    fn either_1zst_exhaustive_no_field_e(x: Either<NoField, num::NonZero<u8>>);
+}
+
+pub fn main() {}
diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr
index b27f769ba34..3a2d4df3bee 100644
--- a/tests/ui/stats/hir-stats.stderr
+++ b/tests/ui/stats/hir-stats.stderr
@@ -1,33 +1,33 @@
 ast-stats-1 PRE EXPANSION AST STATS
 ast-stats-1 Name                Accumulated Size         Count     Item Size
 ast-stats-1 ----------------------------------------------------------------
+ast-stats-1 Crate                     40 ( 0.6%)             1            40
 ast-stats-1 GenericArgs               40 ( 0.6%)             1            40
 ast-stats-1 - AngleBracketed            40 ( 0.6%)             1
-ast-stats-1 Crate                     40 ( 0.6%)             1            40
 ast-stats-1 ExprField                 48 ( 0.7%)             1            48
 ast-stats-1 WherePredicate            56 ( 0.8%)             1            56
 ast-stats-1 - BoundPredicate            56 ( 0.8%)             1
 ast-stats-1 Attribute                 64 ( 1.0%)             2            32
-ast-stats-1 - Normal                    32 ( 0.5%)             1
 ast-stats-1 - DocComment                32 ( 0.5%)             1
+ast-stats-1 - Normal                    32 ( 0.5%)             1
 ast-stats-1 Local                     80 ( 1.2%)             1            80
 ast-stats-1 ForeignItem               88 ( 1.3%)             1            88
 ast-stats-1 - Fn                        88 ( 1.3%)             1
 ast-stats-1 Arm                       96 ( 1.4%)             2            48
 ast-stats-1 FnDecl                   120 ( 1.8%)             5            24
 ast-stats-1 FieldDef                 160 ( 2.4%)             2            80
+ast-stats-1 Param                    160 ( 2.4%)             4            40
 ast-stats-1 Stmt                     160 ( 2.4%)             5            32
 ast-stats-1 - Let                       32 ( 0.5%)             1
 ast-stats-1 - MacCall                   32 ( 0.5%)             1
 ast-stats-1 - Expr                      96 ( 1.4%)             3
-ast-stats-1 Param                    160 ( 2.4%)             4            40
 ast-stats-1 Block                    192 ( 2.9%)             6            32
 ast-stats-1 Variant                  208 ( 3.1%)             2           104
-ast-stats-1 GenericBound             352 ( 5.3%)             4            88
-ast-stats-1 - Trait                    352 ( 5.3%)             4
 ast-stats-1 AssocItem                352 ( 5.3%)             4            88
 ast-stats-1 - Type                     176 ( 2.7%)             2
 ast-stats-1 - Fn                       176 ( 2.7%)             2
+ast-stats-1 GenericBound             352 ( 5.3%)             4            88
+ast-stats-1 - Trait                    352 ( 5.3%)             4
 ast-stats-1 GenericParam             480 ( 7.2%)             5            96
 ast-stats-1 Pat                      504 ( 7.6%)             7            72
 ast-stats-1 - Struct                    72 ( 1.1%)             1
@@ -41,15 +41,15 @@ ast-stats-1 - Lit                      144 ( 2.2%)             2
 ast-stats-1 - Block                    216 ( 3.3%)             3
 ast-stats-1 PathSegment              744 (11.2%)            31            24
 ast-stats-1 Ty                       896 (13.5%)            14            64
-ast-stats-1 - Ptr                       64 ( 1.0%)             1
 ast-stats-1 - Ref                       64 ( 1.0%)             1
+ast-stats-1 - Ptr                       64 ( 1.0%)             1
 ast-stats-1 - ImplicitSelf             128 ( 1.9%)             2
 ast-stats-1 - Path                     640 ( 9.6%)            10
 ast-stats-1 Item                   1_224 (18.4%)             9           136
-ast-stats-1 - Trait                    136 ( 2.0%)             1
-ast-stats-1 - Enum                     136 ( 2.0%)             1
 ast-stats-1 - ForeignMod               136 ( 2.0%)             1
+ast-stats-1 - Trait                    136 ( 2.0%)             1
 ast-stats-1 - Impl                     136 ( 2.0%)             1
+ast-stats-1 - Enum                     136 ( 2.0%)             1
 ast-stats-1 - Fn                       272 ( 4.1%)             2
 ast-stats-1 - Use                      408 ( 6.1%)             3
 ast-stats-1 ----------------------------------------------------------------
@@ -58,9 +58,9 @@ ast-stats-1
 ast-stats-2 POST EXPANSION AST STATS
 ast-stats-2 Name                Accumulated Size         Count     Item Size
 ast-stats-2 ----------------------------------------------------------------
+ast-stats-2 Crate                     40 ( 0.5%)             1            40
 ast-stats-2 GenericArgs               40 ( 0.5%)             1            40
 ast-stats-2 - AngleBracketed            40 ( 0.5%)             1
-ast-stats-2 Crate                     40 ( 0.5%)             1            40
 ast-stats-2 ExprField                 48 ( 0.7%)             1            48
 ast-stats-2 WherePredicate            56 ( 0.8%)             1            56
 ast-stats-2 - BoundPredicate            56 ( 0.8%)             1
@@ -68,24 +68,24 @@ ast-stats-2 Local                     80 ( 1.1%)             1            80
 ast-stats-2 ForeignItem               88 ( 1.2%)             1            88
 ast-stats-2 - Fn                        88 ( 1.2%)             1
 ast-stats-2 Arm                       96 ( 1.3%)             2            48
-ast-stats-2 InlineAsm                120 ( 1.6%)             1           120
 ast-stats-2 FnDecl                   120 ( 1.6%)             5            24
+ast-stats-2 InlineAsm                120 ( 1.6%)             1           120
 ast-stats-2 Attribute                128 ( 1.8%)             4            32
 ast-stats-2 - DocComment                32 ( 0.4%)             1
 ast-stats-2 - Normal                    96 ( 1.3%)             3
 ast-stats-2 FieldDef                 160 ( 2.2%)             2            80
+ast-stats-2 Param                    160 ( 2.2%)             4            40
 ast-stats-2 Stmt                     160 ( 2.2%)             5            32
 ast-stats-2 - Let                       32 ( 0.4%)             1
 ast-stats-2 - Semi                      32 ( 0.4%)             1
 ast-stats-2 - Expr                      96 ( 1.3%)             3
-ast-stats-2 Param                    160 ( 2.2%)             4            40
 ast-stats-2 Block                    192 ( 2.6%)             6            32
 ast-stats-2 Variant                  208 ( 2.9%)             2           104
-ast-stats-2 GenericBound             352 ( 4.8%)             4            88
-ast-stats-2 - Trait                    352 ( 4.8%)             4
 ast-stats-2 AssocItem                352 ( 4.8%)             4            88
 ast-stats-2 - Type                     176 ( 2.4%)             2
 ast-stats-2 - Fn                       176 ( 2.4%)             2
+ast-stats-2 GenericBound             352 ( 4.8%)             4            88
+ast-stats-2 - Trait                    352 ( 4.8%)             4
 ast-stats-2 GenericParam             480 ( 6.6%)             5            96
 ast-stats-2 Pat                      504 ( 6.9%)             7            72
 ast-stats-2 - Struct                    72 ( 1.0%)             1
@@ -100,16 +100,16 @@ ast-stats-2 - Lit                      144 ( 2.0%)             2
 ast-stats-2 - Block                    216 ( 3.0%)             3
 ast-stats-2 PathSegment              864 (11.9%)            36            24
 ast-stats-2 Ty                       896 (12.3%)            14            64
-ast-stats-2 - Ptr                       64 ( 0.9%)             1
 ast-stats-2 - Ref                       64 ( 0.9%)             1
+ast-stats-2 - Ptr                       64 ( 0.9%)             1
 ast-stats-2 - ImplicitSelf             128 ( 1.8%)             2
 ast-stats-2 - Path                     640 ( 8.8%)            10
 ast-stats-2 Item                   1_496 (20.5%)            11           136
-ast-stats-2 - Trait                    136 ( 1.9%)             1
 ast-stats-2 - Enum                     136 ( 1.9%)             1
+ast-stats-2 - Trait                    136 ( 1.9%)             1
+ast-stats-2 - Impl                     136 ( 1.9%)             1
 ast-stats-2 - ExternCrate              136 ( 1.9%)             1
 ast-stats-2 - ForeignMod               136 ( 1.9%)             1
-ast-stats-2 - Impl                     136 ( 1.9%)             1
 ast-stats-2 - Fn                       272 ( 3.7%)             2
 ast-stats-2 - Use                      544 ( 7.5%)             4
 ast-stats-2 ----------------------------------------------------------------
@@ -129,8 +129,8 @@ hir-stats - Lifetime                  48 ( 0.5%)             3
 hir-stats Local                     64 ( 0.7%)             1            64
 hir-stats Param                     64 ( 0.7%)             2            32
 hir-stats Body                      72 ( 0.8%)             3            24
-hir-stats InlineAsm                 72 ( 0.8%)             1            72
 hir-stats ImplItemRef               72 ( 0.8%)             2            36
+hir-stats InlineAsm                 72 ( 0.8%)             1            72
 hir-stats Arm                       80 ( 0.9%)             2            40
 hir-stats FieldDef                  96 ( 1.1%)             2            48
 hir-stats Stmt                      96 ( 1.1%)             3            32
@@ -139,8 +139,8 @@ hir-stats - Semi                      32 ( 0.4%)             1
 hir-stats - Expr                      32 ( 0.4%)             1
 hir-stats FnDecl                   120 ( 1.3%)             3            40
 hir-stats Attribute                128 ( 1.4%)             4            32
-hir-stats Variant                  144 ( 1.6%)             2            72
 hir-stats GenericArgs              144 ( 1.6%)             3            48
+hir-stats Variant                  144 ( 1.6%)             2            72
 hir-stats GenericBound             192 ( 2.1%)             4            48
 hir-stats - Trait                    192 ( 2.1%)             4
 hir-stats WherePredicate           192 ( 2.1%)             3            64
@@ -148,27 +148,27 @@ hir-stats - BoundPredicate           192 ( 2.1%)             3
 hir-stats Block                    288 ( 3.2%)             6            48
 hir-stats GenericParam             360 ( 4.0%)             5            72
 hir-stats Pat                      360 ( 4.0%)             5            72
-hir-stats - Wild                      72 ( 0.8%)             1
 hir-stats - Struct                    72 ( 0.8%)             1
+hir-stats - Wild                      72 ( 0.8%)             1
 hir-stats - Binding                  216 ( 2.4%)             3
 hir-stats Generics                 560 ( 6.2%)            10            56
 hir-stats Ty                       720 ( 8.0%)            15            48
-hir-stats - Ptr                       48 ( 0.5%)             1
 hir-stats - Ref                       48 ( 0.5%)             1
+hir-stats - Ptr                       48 ( 0.5%)             1
 hir-stats - Path                     624 ( 7.0%)            13
 hir-stats Expr                     768 ( 8.6%)            12            64
 hir-stats - Path                      64 ( 0.7%)             1
-hir-stats - Struct                    64 ( 0.7%)             1
 hir-stats - Match                     64 ( 0.7%)             1
+hir-stats - Struct                    64 ( 0.7%)             1
 hir-stats - InlineAsm                 64 ( 0.7%)             1
 hir-stats - Lit                      128 ( 1.4%)             2
 hir-stats - Block                    384 ( 4.3%)             6
 hir-stats Item                     968 (10.8%)            11            88
-hir-stats - Trait                     88 ( 1.0%)             1
 hir-stats - Enum                      88 ( 1.0%)             1
+hir-stats - Trait                     88 ( 1.0%)             1
+hir-stats - Impl                      88 ( 1.0%)             1
 hir-stats - ExternCrate               88 ( 1.0%)             1
 hir-stats - ForeignMod                88 ( 1.0%)             1
-hir-stats - Impl                      88 ( 1.0%)             1
 hir-stats - Fn                       176 ( 2.0%)             2
 hir-stats - Use                      352 ( 3.9%)             4
 hir-stats Path                   1_240 (13.8%)            31            40
diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs
index f49ce33841a..1961f10bd0a 100644
--- a/tests/ui/structs-enums/type-sizes.rs
+++ b/tests/ui/structs-enums/type-sizes.rs
@@ -5,7 +5,6 @@
 #![allow(dead_code)]
 #![feature(never_type)]
 #![feature(pointer_is_aligned_to)]
-#![feature(strict_provenance)]
 
 use std::mem::size_of;
 use std::num::NonZero;
diff --git a/tests/ui/traits/next-solver/canonical/do-not-bail-due-to-placeholders.rs b/tests/ui/traits/next-solver/canonical/do-not-bail-due-to-placeholders.rs
new file mode 100644
index 00000000000..3fc11f27d1f
--- /dev/null
+++ b/tests/ui/traits/next-solver/canonical/do-not-bail-due-to-placeholders.rs
@@ -0,0 +1,16 @@
+//@ compile-flags: -Znext-solver
+//@ check-pass
+
+// When canonicalizing responses, we bail if there are too many inference variables.
+// We previously also counted placeholders, which is incorrect.
+#![recursion_limit = "8"]
+
+fn foo<T>() {}
+
+fn bar<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>() {
+    // The query response will contain 10 placeholders, which previously
+    // caused us to bail here.
+    foo::<(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)>();
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs
new file mode 100644
index 00000000000..5284220ac38
--- /dev/null
+++ b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs
@@ -0,0 +1,52 @@
+//@ check-pass
+
+// When canonicalizing a response in the trait solver, we bail with overflow
+// if there are too many non-region inference variables. Doing so in normalizes-to
+// goals ends up hiding inference constraints in cases which we want to support,
+// see #131969. To prevent this issue we do not check for too many inference
+// variables in normalizes-to goals.
+#![recursion_limit = "8"]
+
+trait Bound {}
+trait Trait {
+    type Assoc;
+}
+
+
+impl<T0, T1, T2, T3, T4, T5, T6, T7> Trait for (T0, T1, T2, T3, T4, T5, T6, T7)
+where
+    T0: Trait,
+    T1: Trait,
+    T2: Trait,
+    T3: Trait,
+    T4: Trait,
+    T5: Trait,
+    T6: Trait,
+    T7: Trait,
+    (
+        T0::Assoc,
+        T1::Assoc,
+        T2::Assoc,
+        T3::Assoc,
+        T4::Assoc,
+        T5::Assoc,
+        T6::Assoc,
+        T7::Assoc,
+    ): Clone,
+{
+    type Assoc = (
+        T0::Assoc,
+        T1::Assoc,
+        T2::Assoc,
+        T3::Assoc,
+        T4::Assoc,
+        T5::Assoc,
+        T6::Assoc,
+        T7::Assoc,
+    );
+}
+
+trait Overlap {}
+impl<T: Trait<Assoc = ()>> Overlap for T {}
+impl<T0, T1, T2, T3, T4, T5, T6, T7> Overlap for (T0, T1, T2, T3, T4, T5, T6, T7) {}
+fn main() {}
diff --git a/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs
index 0d5f42231e4..f88f74680b9 100644
--- a/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs
+++ b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs
@@ -1,8 +1,5 @@
 //@ check-pass
-//@ revisions: ai_current ai_next ia_current ia_next ii_current ii_next
-//@[ai_next] compile-flags: -Znext-solver
-//@[ia_next] compile-flags: -Znext-solver
-//@[ii_next] compile-flags: -Znext-solver
+//@ revisions: ai ia ii
 
 // Regression test for nalgebra hang <https://github.com/rust-lang/rust/issues/130056>.
 
@@ -17,11 +14,11 @@ trait Trait {
     type Assoc: ?Sized;
 }
 impl<T: ?Sized + Trait> Trait for W<T, T> {
-    #[cfg(any(ai_current, ai_next))]
+    #[cfg(ai)]
     type Assoc = W<T::Assoc, Id<T::Assoc>>;
-    #[cfg(any(ia_current, ia_next))]
+    #[cfg(ia)]
     type Assoc = W<Id<T::Assoc>, T::Assoc>;
-    #[cfg(any(ii_current, ii_next))]
+    #[cfg(ii)]
     type Assoc = W<Id<T::Assoc>, Id<T::Assoc>>;
 }
 
diff --git a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs
index 86d428cc0f0..dd8ad3c2dfe 100644
--- a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs
+++ b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs
@@ -1,8 +1,8 @@
-//~ ERROR overflow evaluating the requirement `Self: Trait`
-//~^ ERROR overflow evaluating the requirement `Self well-formed`
-// This is a non-regression test for issue #115351, where a recursion limit of 0 caused an ICE.
+//@ check-pass
 //@ compile-flags: -Znext-solver --crate-type=lib
 
+// This is a non-regression test for issue #115351, where a recursion limit of 0 caused an ICE.
+
 #![recursion_limit = "0"]
 trait Trait {}
 impl Trait for u32 {}
diff --git a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr
deleted file mode 100644
index 2eed7e8f723..00000000000
--- a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error[E0275]: overflow evaluating the requirement `Self: Trait`
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`)
-
-error[E0275]: overflow evaluating the requirement `Self well-formed`
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`)
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/transmutability/dont-assume-err-is-yes-issue-126377.rs b/tests/ui/transmutability/dont-assume-err-is-yes-issue-126377.rs
new file mode 100644
index 00000000000..eeb0777c856
--- /dev/null
+++ b/tests/ui/transmutability/dont-assume-err-is-yes-issue-126377.rs
@@ -0,0 +1,19 @@
+#![feature(transmutability)]
+#![feature(generic_const_exprs)]
+//~^ WARN the feature `generic_const_exprs` is incomplete
+
+use std::mem::{Assume, TransmuteFrom};
+
+pub fn is_transmutable<const ASSUME_ALIGNMENT: bool>()
+where
+    (): TransmuteFrom<(), { Assume::SAFETY }>,
+{
+}
+
+fn foo<const N: usize>() {
+    is_transmutable::<{}>();
+    //~^ ERROR  the trait bound `(): TransmuteFrom<(), { Assume::SAFETY }>` is not satisfied
+    //~| ERROR mismatched types
+}
+
+fn main() {}
diff --git a/tests/ui/transmutability/dont-assume-err-is-yes-issue-126377.stderr b/tests/ui/transmutability/dont-assume-err-is-yes-issue-126377.stderr
new file mode 100644
index 00000000000..6cb6a85c78a
--- /dev/null
+++ b/tests/ui/transmutability/dont-assume-err-is-yes-issue-126377.stderr
@@ -0,0 +1,34 @@
+warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/dont-assume-err-is-yes-issue-126377.rs:2:12
+   |
+LL | #![feature(generic_const_exprs)]
+   |            ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+  --> $DIR/dont-assume-err-is-yes-issue-126377.rs:14:23
+   |
+LL |     is_transmutable::<{}>();
+   |                       ^^ expected `bool`, found `()`
+
+error[E0277]: the trait bound `(): TransmuteFrom<(), { Assume::SAFETY }>` is not satisfied
+  --> $DIR/dont-assume-err-is-yes-issue-126377.rs:14:23
+   |
+LL |     is_transmutable::<{}>();
+   |                       ^^ the trait `TransmuteFrom<(), { Assume::SAFETY }>` is not implemented for `()`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/dont-assume-err-is-yes-issue-126377.rs:9:9
+   |
+LL | pub fn is_transmutable<const ASSUME_ALIGNMENT: bool>()
+   |        --------------- required by a bound in this function
+LL | where
+LL |     (): TransmuteFrom<(), { Assume::SAFETY }>,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.