about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-07-03 04:16:20 +0000
committerbors <bors@rust-lang.org>2025-07-03 04:16:20 +0000
commit6268d0aa34b46981533b09827c1454b8cf27e032 (patch)
tree9e275fd8466532f354e5e3496cc74659f15d82ca
parent25face9808491588e59b6d7844f2185b09eef479 (diff)
parentbc0262d0f1bf9165e28c01b64fb0dd1a24534968 (diff)
downloadrust-6268d0aa34b46981533b09827c1454b8cf27e032.tar.gz
rust-6268d0aa34b46981533b09827c1454b8cf27e032.zip
Auto merge of #143350 - matthiaskrgr:rollup-zcuvkve, r=matthiaskrgr
Rollup of 9 pull requests

Successful merges:

 - rust-lang/rust#143192 (Improve CSS for source code block line numbers)
 - rust-lang/rust#143251 (bootstrap: add build.tidy-extra-checks option)
 - rust-lang/rust#143273 (Make the enum check work for negative discriminants)
 - rust-lang/rust#143292 (Explicitly handle all nodes in `generics_of` when computing parent)
 - rust-lang/rust#143316 (Add bootstrap check snapshot tests)
 - rust-lang/rust#143321 (byte-addresses memory -> byte-addressed memory)
 - rust-lang/rust#143324 (interpret: move the native call preparation logic into Miri)
 - rust-lang/rust#143325 (Use non-global interner in `test_string_interning` in bootstrap)
 - rust-lang/rust#143327 (miri: improve errors for type validity assertion failures)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--bootstrap.example.toml9
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs44
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs41
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs61
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs21
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs9
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs2
-rw-r--r--compiler/rustc_mir_transform/src/check_enums.rs30
-rw-r--r--library/core/src/ffi/c_char.md2
-rw-r--r--library/core/src/intrinsics/mod.rs11
-rw-r--r--library/core/src/mem/maybe_uninit.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs26
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs12
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs1
-rw-r--r--src/bootstrap/src/core/builder/mod.rs12
-rw-r--r--src/bootstrap/src/core/builder/tests.rs385
-rw-r--r--src/bootstrap/src/core/config/config.rs5
-rw-r--r--src/bootstrap/src/core/config/toml/build.rs1
-rw-r--r--src/bootstrap/src/utils/cache/tests.rs11
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css98
-rw-r--r--src/tools/miri/src/alloc_addresses/mod.rs15
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs4
-rw-r--r--src/tools/miri/src/shims/native_lib/mod.rs41
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs2
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr6
-rw-r--r--src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs7
-rw-r--r--src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr28
-rw-r--r--src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs8
-rw-r--r--src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr28
-rw-r--r--src/tools/miri/tests/fail/validity/uninit_float.stderr2
-rw-r--r--src/tools/miri/tests/fail/validity/uninit_integer.stderr2
-rw-r--r--src/tools/miri/tests/fail/validity/uninit_raw_ptr.stderr2
-rw-r--r--tests/rustdoc-gui/docblock-code-block-line-number.goml4
-rw-r--r--tests/rustdoc-gui/scrape-examples-button-focus.goml2
-rw-r--r--tests/rustdoc-gui/source-code-wrapping.goml29
-rw-r--r--tests/ui/mir/enum/negative_discr_break.rs14
-rw-r--r--tests/ui/mir/enum/negative_discr_ok.rs53
39 files changed, 716 insertions, 325 deletions
diff --git a/bootstrap.example.toml b/bootstrap.example.toml
index cc1ea796a02..b59f112bdfb 100644
--- a/bootstrap.example.toml
+++ b/bootstrap.example.toml
@@ -467,6 +467,15 @@
 # Whether to use the precompiled stage0 libtest with compiletest.
 #build.compiletest-use-stage0-libtest = true
 
+# Default value for the `--extra-checks` flag of tidy.
+#
+# See `./x test tidy --help` for details.
+#
+# Note that if any value is manually given to bootstrap such as
+# `./x test tidy --extra-checks=js`, this value is ignored.
+# Use `--extra-checks=''` to temporarily disable all extra checks.
+#build.tidy-extra-checks = ""
+
 # Indicates whether ccache is used when building certain artifacts (e.g. LLVM).
 # Set to `true` to use the first `ccache` in PATH, or set an absolute path to use
 # a specific version.
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 317b1229a90..76fa744361a 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -10,7 +10,7 @@ use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
 use rustc_middle::mir::AssertMessage;
 use rustc_middle::mir::interpret::ReportedErrorInfo;
 use rustc_middle::query::TyCtxtAt;
-use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
+use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, mir};
 use rustc_span::{Span, Symbol, sym};
@@ -23,8 +23,8 @@ use crate::fluent_generated as fluent;
 use crate::interpret::{
     self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
     GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
-    compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom,
-    throw_unsup, throw_unsup_format,
+    compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
+    throw_ub_custom, throw_unsup, throw_unsup_format,
 };
 
 /// When hitting this many interpreted terminators we emit a deny by default lint
@@ -462,6 +462,44 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
             // (We know the value here in the machine of course, but this is the runtime of that code,
             // not the optimization stage.)
             sym::is_val_statically_known => ecx.write_scalar(Scalar::from_bool(false), dest)?,
+
+            // We handle these here since Miri does not want to have them.
+            sym::assert_inhabited
+            | sym::assert_zero_valid
+            | sym::assert_mem_uninitialized_valid => {
+                let ty = instance.args.type_at(0);
+                let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap();
+
+                let should_panic = !ecx
+                    .tcx
+                    .check_validity_requirement((requirement, ecx.typing_env().as_query_input(ty)))
+                    .map_err(|_| err_inval!(TooGeneric))?;
+
+                if should_panic {
+                    let layout = ecx.layout_of(ty)?;
+
+                    let msg = match requirement {
+                        // For *all* intrinsics we first check `is_uninhabited` to give a more specific
+                        // error message.
+                        _ if layout.is_uninhabited() => format!(
+                            "aborted execution: attempted to instantiate uninhabited type `{ty}`"
+                        ),
+                        ValidityRequirement::Inhabited => bug!("handled earlier"),
+                        ValidityRequirement::Zero => format!(
+                            "aborted execution: attempted to zero-initialize type `{ty}`, which is invalid"
+                        ),
+                        ValidityRequirement::UninitMitigated0x01Fill => format!(
+                            "aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid"
+                        ),
+                        ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
+                    };
+
+                    Self::panic_nounwind(ecx, &msg)?;
+                    // Skip the `return_to_block` at the end (we panicked, we do not return).
+                    return interp_ok(None);
+                }
+            }
+
             _ => {
                 // We haven't handled the intrinsic, let's see if we can use a fallback body.
                 if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index d7cede71293..378ed6d0e10 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -7,7 +7,7 @@ use std::assert_matches::assert_matches;
 use rustc_abi::Size;
 use rustc_apfloat::ieee::{Double, Half, Quad, Single};
 use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
-use rustc_middle::ty::layout::{TyAndLayout, ValidityRequirement};
+use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_middle::{bug, ty};
 use rustc_span::{Symbol, sym};
@@ -17,8 +17,8 @@ use super::memory::MemoryKind;
 use super::util::ensure_monomorphic_enough;
 use super::{
     Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy,
-    PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom,
-    err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
+    PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format,
+    interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
 };
 use crate::fluent_generated as fluent;
 
@@ -372,41 +372,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 self.exact_div(&val, &size, dest)?;
             }
 
-            sym::assert_inhabited
-            | sym::assert_zero_valid
-            | sym::assert_mem_uninitialized_valid => {
-                let ty = instance.args.type_at(0);
-                let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap();
-
-                let should_panic = !self
-                    .tcx
-                    .check_validity_requirement((requirement, self.typing_env.as_query_input(ty)))
-                    .map_err(|_| err_inval!(TooGeneric))?;
-
-                if should_panic {
-                    let layout = self.layout_of(ty)?;
-
-                    let msg = match requirement {
-                        // For *all* intrinsics we first check `is_uninhabited` to give a more specific
-                        // error message.
-                        _ if layout.is_uninhabited() => format!(
-                            "aborted execution: attempted to instantiate uninhabited type `{ty}`"
-                        ),
-                        ValidityRequirement::Inhabited => bug!("handled earlier"),
-                        ValidityRequirement::Zero => format!(
-                            "aborted execution: attempted to zero-initialize type `{ty}`, which is invalid"
-                        ),
-                        ValidityRequirement::UninitMitigated0x01Fill => format!(
-                            "aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid"
-                        ),
-                        ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
-                    };
-
-                    M::panic_nounwind(self, &msg)?;
-                    // Skip the `return_to_block` at the end (we panicked, we do not return).
-                    return interp_ok(true);
-                }
-            }
             sym::simd_insert => {
                 let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
                 let elem = &args[2];
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 3b36bb85985..ff822b52a8d 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -655,7 +655,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// The caller is responsible for calling the access hooks!
     ///
     /// You almost certainly want to use `get_ptr_alloc`/`get_ptr_alloc_mut` instead.
-    fn get_alloc_raw(
+    pub fn get_alloc_raw(
         &self,
         id: AllocId,
     ) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra, M::Bytes>> {
@@ -757,7 +757,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     ///
     /// Also returns a ptr to `self.extra` so that the caller can use it in parallel with the
     /// allocation.
-    fn get_alloc_raw_mut(
+    ///
+    /// You almost certainly want to use `get_ptr_alloc`/`get_ptr_alloc_mut` instead.
+    pub fn get_alloc_raw_mut(
         &mut self,
         id: AllocId,
     ) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra, M::Bytes>, &mut M)> {
@@ -976,15 +978,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         interp_ok(())
     }
 
-    /// Handle the effect an FFI call might have on the state of allocations.
-    /// This overapproximates the modifications which external code might make to memory:
-    /// We set all reachable allocations as initialized, mark all reachable provenances as exposed
-    /// and overwrite them with `Provenance::WILDCARD`.
-    ///
-    /// The allocations in `ids` are assumed to be already exposed.
-    pub fn prepare_for_native_call(&mut self, ids: Vec<AllocId>) -> InterpResult<'tcx> {
+    /// Visit all allocations reachable from the given start set, by recursively traversing the
+    /// provenance information of those allocations.
+    pub fn visit_reachable_allocs(
+        &mut self,
+        start: Vec<AllocId>,
+        mut visit: impl FnMut(&mut Self, AllocId, &AllocInfo) -> InterpResult<'tcx>,
+    ) -> InterpResult<'tcx> {
         let mut done = FxHashSet::default();
-        let mut todo = ids;
+        let mut todo = start;
         while let Some(id) = todo.pop() {
             if !done.insert(id) {
                 // We already saw this allocation before, don't process it again.
@@ -992,31 +994,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             }
             let info = self.get_alloc_info(id);
 
-            // If there is no data behind this pointer, skip this.
-            if !matches!(info.kind, AllocKind::LiveData) {
-                continue;
-            }
-
-            // Expose all provenances in this allocation, and add them to `todo`.
-            let alloc = self.get_alloc_raw(id)?;
-            for prov in alloc.provenance().provenances() {
-                M::expose_provenance(self, prov)?;
-                if let Some(id) = prov.get_alloc_id() {
-                    todo.push(id);
+            // Recurse, if there is data here.
+            // Do this *before* invoking the callback, as the callback might mutate the
+            // allocation and e.g. replace all provenance by wildcards!
+            if matches!(info.kind, AllocKind::LiveData) {
+                let alloc = self.get_alloc_raw(id)?;
+                for prov in alloc.provenance().provenances() {
+                    if let Some(id) = prov.get_alloc_id() {
+                        todo.push(id);
+                    }
                 }
             }
-            // Also expose the provenance of the interpreter-level allocation, so it can
-            // be read by FFI. The `black_box` is defensive programming as LLVM likes
-            // to (incorrectly) optimize away ptr2int casts whose result is unused.
-            std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance());
-
-            // Prepare for possible write from native code if mutable.
-            if info.mutbl.is_mut() {
-                self.get_alloc_raw_mut(id)?
-                    .0
-                    .prepare_for_native_write()
-                    .map_err(|e| e.to_interp_error(id))?;
-            }
+
+            // Call the callback.
+            visit(self, id, &info)?;
         }
         interp_ok(())
     }
@@ -1073,7 +1064,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             todo.extend(static_roots(self));
             while let Some(id) = todo.pop() {
                 if reachable.insert(id) {
-                    // This is a new allocation, add the allocation it points to `todo`.
+                    // This is a new allocation, add the allocations it points to `todo`.
+                    // We only need to care about `alloc_map` memory here, as entirely unchanged
+                    // global memory cannot point to memory relevant for the leak check.
                     if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
                         todo.extend(
                             alloc.provenance().provenances().filter_map(|prov| prov.get_alloc_id()),
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 7eb896f0bf1..573af01a62d 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -1,12 +1,11 @@
 use std::assert_matches::assert_matches;
 use std::ops::ControlFlow;
 
-use hir::intravisit::{self, Visitor};
-use hir::{GenericParamKind, HirId, Node};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::VisitorExt;
-use rustc_hir::{self as hir, AmbigArg};
+use rustc_hir::intravisit::{self, Visitor, VisitorExt};
+use rustc_hir::{self as hir, AmbigArg, GenericParamKind, HirId, Node};
+use rustc_middle::span_bug;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::lint;
 use rustc_span::{Span, Symbol, kw};
@@ -212,7 +211,19 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
             // inherit the generics of the item.
             Some(parent.to_def_id())
         }
-        _ => None,
+
+        // All of these nodes have no parent from which to inherit generics.
+        Node::Item(_) | Node::ForeignItem(_) => None,
+
+        // Params don't really have generics, but we use it when instantiating their value paths.
+        Node::GenericParam(_) => None,
+
+        Node::Synthetic => span_bug!(
+            tcx.def_span(def_id),
+            "synthetic HIR should have its `generics_of` explicitly fed"
+        ),
+
+        _ => span_bug!(tcx.def_span(def_id), "unhandled node {node:?}"),
     };
 
     enum Defaults {
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index 4198b198ab1..d2cadc96b63 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -799,7 +799,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
     /// Initialize all previously uninitialized bytes in the entire allocation, and set
     /// provenance of everything to `Wildcard`. Before calling this, make sure all
     /// provenance in this allocation is exposed!
-    pub fn prepare_for_native_write(&mut self) -> AllocResult {
+    pub fn prepare_for_native_access(&mut self) {
         let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) };
         // Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
         for chunk in self.init_mask.range_as_init_chunks(full_range) {
@@ -814,13 +814,6 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
 
         // Set provenance of all bytes to wildcard.
         self.provenance.write_wildcards(self.len());
-
-        // Also expose the provenance of the interpreter-level allocation, so it can
-        // be written by FFI. The `black_box` is defensive programming as LLVM likes
-        // to (incorrectly) optimize away ptr2int casts whose result is unused.
-        std::hint::black_box(self.get_bytes_unchecked_raw_mut().expose_provenance());
-
-        Ok(())
     }
 
     /// Remove all provenance in the given memory range.
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs
index c9525df1f79..63608947eb3 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs
@@ -120,7 +120,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
         }
     }
 
-    /// Check if here is ptr-sized provenance at the given index.
+    /// Check if there is ptr-sized provenance at the given index.
     /// Does not mean anything for bytewise provenance! But can be useful as an optimization.
     pub fn get_ptr(&self, offset: Size) -> Option<Prov> {
         self.ptrs.get(&offset).copied()
diff --git a/compiler/rustc_mir_transform/src/check_enums.rs b/compiler/rustc_mir_transform/src/check_enums.rs
index 240da87ab27..fae984b4936 100644
--- a/compiler/rustc_mir_transform/src/check_enums.rs
+++ b/compiler/rustc_mir_transform/src/check_enums.rs
@@ -120,6 +120,7 @@ enum EnumCheckType<'tcx> {
     },
 }
 
+#[derive(Debug, Copy, Clone)]
 struct TyAndSize<'tcx> {
     pub ty: Ty<'tcx>,
     pub size: Size,
@@ -337,7 +338,7 @@ fn insert_direct_enum_check<'tcx>(
     let invalid_discr_block_data = BasicBlockData::new(None, false);
     let invalid_discr_block = basic_blocks.push(invalid_discr_block_data);
     let block_data = &mut basic_blocks[current_block];
-    let discr = insert_discr_cast_to_u128(
+    let discr_place = insert_discr_cast_to_u128(
         tcx,
         local_decls,
         block_data,
@@ -348,13 +349,34 @@ fn insert_direct_enum_check<'tcx>(
         source_info,
     );
 
+    // Mask out the bits of the discriminant type.
+    let mask = discr.size.unsigned_int_max();
+    let discr_masked =
+        local_decls.push(LocalDecl::with_source_info(tcx.types.u128, source_info)).into();
+    let rvalue = Rvalue::BinaryOp(
+        BinOp::BitAnd,
+        Box::new((
+            Operand::Copy(discr_place),
+            Operand::Constant(Box::new(ConstOperand {
+                span: source_info.span,
+                user_ty: None,
+                const_: Const::Val(ConstValue::from_u128(mask), tcx.types.u128),
+            })),
+        )),
+    );
+    block_data
+        .statements
+        .push(Statement::new(source_info, StatementKind::Assign(Box::new((discr_masked, rvalue)))));
+
     // Branch based on the discriminant value.
     block_data.terminator = Some(Terminator {
         source_info,
         kind: TerminatorKind::SwitchInt {
-            discr: Operand::Copy(discr),
+            discr: Operand::Copy(discr_masked),
             targets: SwitchTargets::new(
-                discriminants.into_iter().map(|discr| (discr, new_block)),
+                discriminants
+                    .into_iter()
+                    .map(|discr_val| (discr.size.truncate(discr_val), new_block)),
                 invalid_discr_block,
             ),
         },
@@ -371,7 +393,7 @@ fn insert_direct_enum_check<'tcx>(
             })),
             expected: true,
             target: new_block,
-            msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr))),
+            msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr_masked))),
             // This calls panic_invalid_enum_construction, which is #[rustc_nounwind].
             // We never want to insert an unwind into unsafe code, because unwinding could
             // make a failing UB check turn into much worse UB when we start unwinding.
diff --git a/library/core/src/ffi/c_char.md b/library/core/src/ffi/c_char.md
index b262a3663b3..119b739a39e 100644
--- a/library/core/src/ffi/c_char.md
+++ b/library/core/src/ffi/c_char.md
@@ -1,6 +1,6 @@
 Equivalent to C's `char` type.
 
-[C's `char` type] is completely unlike [Rust's `char` type]; while Rust's type represents a unicode scalar value, C's `char` type is just an ordinary integer. On modern architectures this type will always be either [`i8`] or [`u8`], as they use byte-addresses memory with 8-bit bytes.
+[C's `char` type] is completely unlike [Rust's `char` type]; while Rust's type represents a unicode scalar value, C's `char` type is just an ordinary integer. On modern architectures this type will always be either [`i8`] or [`u8`], as they use byte-addressed memory with 8-bit bytes.
 
 C chars are most commonly used to make C strings. Unlike Rust, where the length of a string is included alongside the string, C strings mark the end of a string with the character `'\0'`. See `CStr` for more information.
 
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index 4250de9fb2b..5827fa93bfb 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -472,7 +472,8 @@ pub fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
 }
 
 /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited:
-/// This will statically either panic, or do nothing.
+/// This will statically either panic, or do nothing. It does not *guarantee* to ever panic,
+/// and should only be called if an assertion failure will imply language UB in the following code.
 ///
 /// This intrinsic does not have a stable counterpart.
 #[rustc_intrinsic_const_stable_indirect]
@@ -481,7 +482,9 @@ pub fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
 pub const fn assert_inhabited<T>();
 
 /// A guard for unsafe functions that cannot ever be executed if `T` does not permit
-/// zero-initialization: This will statically either panic, or do nothing.
+/// zero-initialization: This will statically either panic, or do nothing. It does not *guarantee*
+/// to ever panic, and should only be called if an assertion failure will imply language UB in the
+/// following code.
 ///
 /// This intrinsic does not have a stable counterpart.
 #[rustc_intrinsic_const_stable_indirect]
@@ -489,7 +492,9 @@ pub const fn assert_inhabited<T>();
 #[rustc_intrinsic]
 pub const fn assert_zero_valid<T>();
 
-/// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing.
+/// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing. It does
+/// not *guarantee* to ever panic, and should only be called if an assertion failure will imply
+/// language UB in the following code.
 ///
 /// This intrinsic does not have a stable counterpart.
 #[rustc_intrinsic_const_stable_indirect]
diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs
index 63a479ed8dd..fc35e54bb0d 100644
--- a/library/core/src/mem/maybe_uninit.rs
+++ b/library/core/src/mem/maybe_uninit.rs
@@ -616,7 +616,9 @@ impl<T> MaybeUninit<T> {
         // This also means that `self` must be a `value` variant.
         unsafe {
             intrinsics::assert_inhabited::<T>();
-            ManuallyDrop::into_inner(self.value)
+            // We do this via a raw ptr read instead of `ManuallyDrop::into_inner` so that there's
+            // no trace of `ManuallyDrop` in Miri's error messages here.
+            (&raw const self.value).cast::<T>().read()
         }
     }
 
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 567416d079b..6c5f70b2f43 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -5,7 +5,7 @@ use crate::core::build_steps::compile::{
 };
 use crate::core::build_steps::tool::{COMPILETEST_ALLOW_FEATURES, SourceType, prepare_tool_cargo};
 use crate::core::builder::{
-    self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, crate_description,
+    self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
 };
 use crate::core::config::TargetSelection;
 use crate::utils::build_stamp::{self, BuildStamp};
@@ -167,6 +167,10 @@ impl Step for Std {
         let _guard = builder.msg_check("library test/bench/example targets", target, Some(stage));
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::check("std", self.target))
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -258,6 +262,10 @@ impl Step for Rustc {
         let hostdir = builder.sysroot_target_libdir(compiler, compiler.host);
         add_to_sysroot(builder, &libdir, &hostdir, &stamp);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::check("rustc", self.target))
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -315,6 +323,10 @@ impl Step for CodegenBackend {
 
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::check(self.backend, self.target))
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -373,6 +385,10 @@ impl Step for RustAnalyzer {
         let _guard = builder.msg_check("rust-analyzer artifacts", target, None);
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::check("rust-analyzer", self.target))
+    }
 }
 
 /// Compiletest is implicitly "checked" when it gets built in order to run tests,
@@ -432,6 +448,10 @@ impl Step for Compiletest {
         let _guard = builder.msg_check("compiletest artifacts", self.target, None);
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::check("compiletest", self.target))
+    }
 }
 
 macro_rules! tool_check_step {
@@ -467,6 +487,10 @@ macro_rules! tool_check_step {
                 let Self { target } = self;
                 run_tool_check_step(builder, target, stringify!($name), $path);
             }
+
+            fn metadata(&self) -> Option<StepMetadata> {
+                Some(StepMetadata::check(stringify!($name), self.target))
+            }
         }
     }
 }
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 84064150738..c3a3eddd161 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -306,11 +306,7 @@ impl Step for Std {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(
-            StepMetadata::build("std", self.target)
-                .built_by(self.compiler)
-                .stage(self.compiler.stage),
-        )
+        Some(StepMetadata::build("std", self.target).built_by(self.compiler))
     }
 }
 
@@ -1186,11 +1182,7 @@ impl Step for Rustc {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(
-            StepMetadata::build("rustc", self.target)
-                .built_by(self.build_compiler)
-                .stage(self.build_compiler.stage + 1),
-        )
+        Some(StepMetadata::build("rustc", self.target).built_by(self.build_compiler))
     }
 }
 
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 01b181f55de..2d4d9e53598 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1108,7 +1108,9 @@ impl Step for Tidy {
         if builder.config.cmd.bless() {
             cmd.arg("--bless");
         }
-        if let Some(s) = builder.config.cmd.extra_checks() {
+        if let Some(s) =
+            builder.config.cmd.extra_checks().or(builder.config.tidy_extra_checks.as_deref())
+        {
             cmd.arg(format!("--extra-checks={s}"));
         }
         let mut args = std::env::args_os();
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index a7220515ca0..ad3f8d89767 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -1195,7 +1195,6 @@ macro_rules! tool_extended {
                 Some(
                     StepMetadata::build($tool_name, self.target)
                         .built_by(self.compiler.with_stage(self.compiler.stage.saturating_sub(1)))
-                        .stage(self.compiler.stage)
                 )
             }
         }
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index 8e9e8b496de..b96a988cde3 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -153,6 +153,10 @@ impl StepMetadata {
         Self::new(name, target, Kind::Build)
     }
 
+    pub fn check(name: &'static str, target: TargetSelection) -> Self {
+        Self::new(name, target, Kind::Check)
+    }
+
     pub fn doc(name: &'static str, target: TargetSelection) -> Self {
         Self::new(name, target, Kind::Doc)
     }
@@ -178,6 +182,14 @@ impl StepMetadata {
         self.stage = Some(stage);
         self
     }
+
+    pub fn get_stage(&self) -> Option<u32> {
+        self.stage.or(self
+            .built_by
+            // For std, its stage corresponds to the stage of the compiler that builds it.
+            // For everything else, a stage N things gets built by a stage N-1 compiler.
+            .map(|compiler| if self.name == "std" { compiler.stage } else { compiler.stage + 1 }))
+    }
 }
 
 pub struct RunConfig<'a> {
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 8adf93ea528..1c5267cb75e 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -863,7 +863,7 @@ mod snapshot {
         insta::assert_snapshot!(
             ctx.config("build")
                 .path("opt-dist")
-                .render_steps(), @"[build] rustc 0 <host> -> OptimizedDist <host>");
+                .render_steps(), @"[build] rustc 0 <host> -> OptimizedDist 1 <host>");
     }
 
     #[test]
@@ -880,7 +880,7 @@ mod snapshot {
             ctx.config("build")
                 .path("opt-dist")
                 .stage(1)
-                .render_steps(), @"[build] rustc 0 <host> -> OptimizedDist <host>");
+                .render_steps(), @"[build] rustc 0 <host> -> OptimizedDist 1 <host>");
     }
 
     #[test]
@@ -890,7 +890,7 @@ mod snapshot {
             ctx.config("build")
                 .path("opt-dist")
                 .stage(2)
-                .render_steps(), @"[build] rustc 0 <host> -> OptimizedDist <host>");
+                .render_steps(), @"[build] rustc 0 <host> -> OptimizedDist 1 <host>");
     }
 
     #[test]
@@ -984,8 +984,8 @@ mod snapshot {
             ctx
                 .config("dist")
                 .render_steps(), @r"
-        [build] rustc 0 <host> -> UnstableBookGen <host>
-        [build] rustc 0 <host> -> Rustbook <host>
+        [build] rustc 0 <host> -> UnstableBookGen 1 <host>
+        [build] rustc 0 <host> -> Rustbook 1 <host>
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
@@ -993,14 +993,14 @@ mod snapshot {
         [build] rustdoc 1 <host>
         [doc] std 2 <host>
         [build] rustc 2 <host> -> std 2 <host>
-        [build] rustc 0 <host> -> LintDocs <host>
-        [build] rustc 0 <host> -> RustInstaller <host>
+        [build] rustc 0 <host> -> LintDocs 1 <host>
+        [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
         [doc] std 2 <host>
         [dist] mingw <host>
-        [build] rustc 0 <host> -> GenerateCopyright <host>
+        [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <host>
-        [dist] rustc 1 <host> -> std <host>
+        [dist] rustc 1 <host> -> std 1 <host>
         [dist] src <>
         "
         );
@@ -1014,25 +1014,25 @@ mod snapshot {
                 .config("dist")
                 .args(&["--set", "build.extended=true"])
                 .render_steps(), @r"
-        [build] rustc 0 <host> -> UnstableBookGen <host>
-        [build] rustc 0 <host> -> Rustbook <host>
+        [build] rustc 0 <host> -> UnstableBookGen 1 <host>
+        [build] rustc 0 <host> -> Rustbook 1 <host>
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustc 0 <host> -> WasmComponentLd <host>
+        [build] rustc 0 <host> -> WasmComponentLd 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustc 1 <host> -> WasmComponentLd <host>
+        [build] rustc 1 <host> -> WasmComponentLd 2 <host>
         [build] rustdoc 1 <host>
         [doc] std 2 <host>
         [build] rustc 2 <host> -> std 2 <host>
-        [build] rustc 0 <host> -> LintDocs <host>
-        [build] rustc 0 <host> -> RustInstaller <host>
+        [build] rustc 0 <host> -> LintDocs 1 <host>
+        [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
         [doc] std 2 <host>
         [dist] mingw <host>
-        [build] rustc 0 <host> -> GenerateCopyright <host>
+        [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <host>
-        [dist] rustc 1 <host> -> std <host>
+        [dist] rustc 1 <host> -> std 1 <host>
         [dist] src <>
         [build] rustc 0 <host> -> rustfmt 1 <host>
         [build] rustc 0 <host> -> cargo-fmt 1 <host>
@@ -1052,8 +1052,8 @@ mod snapshot {
                 .hosts(&[&host_target()])
                 .targets(&[&host_target(), TEST_TRIPLE_1])
                 .render_steps(), @r"
-        [build] rustc 0 <host> -> UnstableBookGen <host>
-        [build] rustc 0 <host> -> Rustbook <host>
+        [build] rustc 0 <host> -> UnstableBookGen 1 <host>
+        [build] rustc 0 <host> -> Rustbook 1 <host>
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
@@ -1062,19 +1062,19 @@ mod snapshot {
         [doc] std 2 <host>
         [doc] std 2 <target1>
         [build] rustc 2 <host> -> std 2 <host>
-        [build] rustc 0 <host> -> LintDocs <host>
-        [build] rustc 0 <host> -> RustInstaller <host>
+        [build] rustc 0 <host> -> LintDocs 1 <host>
+        [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
         [dist] docs <target1>
         [doc] std 2 <host>
         [doc] std 2 <target1>
         [dist] mingw <host>
         [dist] mingw <target1>
-        [build] rustc 0 <host> -> GenerateCopyright <host>
+        [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <host>
-        [dist] rustc 1 <host> -> std <host>
+        [dist] rustc 1 <host> -> std 1 <host>
         [build] rustc 2 <host> -> std 2 <target1>
-        [dist] rustc 2 <host> -> std <target1>
+        [dist] rustc 2 <host> -> std 2 <target1>
         [dist] src <>
         "
         );
@@ -1089,8 +1089,8 @@ mod snapshot {
                 .hosts(&[&host_target(), TEST_TRIPLE_1])
                 .targets(&[&host_target()])
                 .render_steps(), @r"
-        [build] rustc 0 <host> -> UnstableBookGen <host>
-        [build] rustc 0 <host> -> Rustbook <host>
+        [build] rustc 0 <host> -> UnstableBookGen 1 <host>
+        [build] rustc 0 <host> -> Rustbook 1 <host>
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
@@ -1098,20 +1098,20 @@ mod snapshot {
         [build] rustdoc 1 <host>
         [doc] std 2 <host>
         [build] rustc 2 <host> -> std 2 <host>
-        [build] rustc 0 <host> -> LintDocs <host>
+        [build] rustc 0 <host> -> LintDocs 1 <host>
         [build] rustc 1 <host> -> std 1 <target1>
         [build] rustc 2 <host> -> std 2 <target1>
-        [build] rustc 0 <host> -> RustInstaller <host>
+        [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
         [doc] std 2 <host>
         [dist] mingw <host>
-        [build] rustc 0 <host> -> GenerateCopyright <host>
+        [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <host>
         [build] llvm <target1>
         [build] rustc 1 <host> -> rustc 2 <target1>
         [build] rustdoc 1 <target1>
         [dist] rustc <target1>
-        [dist] rustc 1 <host> -> std <host>
+        [dist] rustc 1 <host> -> std 1 <host>
         [dist] src <>
         "
         );
@@ -1126,8 +1126,8 @@ mod snapshot {
                 .hosts(&[&host_target(), TEST_TRIPLE_1])
                 .targets(&[&host_target(), TEST_TRIPLE_1])
                 .render_steps(), @r"
-        [build] rustc 0 <host> -> UnstableBookGen <host>
-        [build] rustc 0 <host> -> Rustbook <host>
+        [build] rustc 0 <host> -> UnstableBookGen 1 <host>
+        [build] rustc 0 <host> -> Rustbook 1 <host>
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
@@ -1136,24 +1136,24 @@ mod snapshot {
         [doc] std 2 <host>
         [doc] std 2 <target1>
         [build] rustc 2 <host> -> std 2 <host>
-        [build] rustc 0 <host> -> LintDocs <host>
+        [build] rustc 0 <host> -> LintDocs 1 <host>
         [build] rustc 1 <host> -> std 1 <target1>
         [build] rustc 2 <host> -> std 2 <target1>
-        [build] rustc 0 <host> -> RustInstaller <host>
+        [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
         [dist] docs <target1>
         [doc] std 2 <host>
         [doc] std 2 <target1>
         [dist] mingw <host>
         [dist] mingw <target1>
-        [build] rustc 0 <host> -> GenerateCopyright <host>
+        [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <host>
         [build] llvm <target1>
         [build] rustc 1 <host> -> rustc 2 <target1>
         [build] rustdoc 1 <target1>
         [dist] rustc <target1>
-        [dist] rustc 1 <host> -> std <host>
-        [dist] rustc 1 <host> -> std <target1>
+        [dist] rustc 1 <host> -> std 1 <host>
+        [dist] rustc 1 <host> -> std 1 <target1>
         [dist] src <>
         "
         );
@@ -1168,8 +1168,8 @@ mod snapshot {
                 .hosts(&[])
                 .targets(&[TEST_TRIPLE_1])
                 .render_steps(), @r"
-        [build] rustc 0 <host> -> UnstableBookGen <host>
-        [build] rustc 0 <host> -> Rustbook <host>
+        [build] rustc 0 <host> -> UnstableBookGen 1 <host>
+        [build] rustc 0 <host> -> Rustbook 1 <host>
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
@@ -1177,12 +1177,12 @@ mod snapshot {
         [build] rustdoc 1 <host>
         [doc] std 2 <target1>
         [build] rustc 2 <host> -> std 2 <host>
-        [build] rustc 0 <host> -> RustInstaller <host>
+        [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <target1>
         [doc] std 2 <target1>
         [dist] mingw <target1>
         [build] rustc 2 <host> -> std 2 <target1>
-        [dist] rustc 2 <host> -> std <target1>
+        [dist] rustc 2 <host> -> std 2 <target1>
         ");
     }
 
@@ -1198,31 +1198,31 @@ mod snapshot {
                 .targets(&[TEST_TRIPLE_1])
                 .args(&["--set", "rust.channel=nightly", "--set", "build.extended=true"])
                 .render_steps(), @r"
-        [build] rustc 0 <host> -> UnstableBookGen <host>
-        [build] rustc 0 <host> -> Rustbook <host>
+        [build] rustc 0 <host> -> UnstableBookGen 1 <host>
+        [build] rustc 0 <host> -> Rustbook 1 <host>
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustc 0 <host> -> WasmComponentLd <host>
+        [build] rustc 0 <host> -> WasmComponentLd 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustc 1 <host> -> WasmComponentLd <host>
+        [build] rustc 1 <host> -> WasmComponentLd 2 <host>
         [build] rustdoc 1 <host>
         [doc] std 2 <target1>
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 1 <host> -> std 1 <target1>
         [build] rustc 2 <host> -> std 2 <target1>
-        [build] rustc 0 <host> -> LintDocs <host>
-        [build] rustc 0 <host> -> RustInstaller <host>
+        [build] rustc 0 <host> -> LintDocs 1 <host>
+        [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <target1>
         [doc] std 2 <target1>
         [dist] mingw <target1>
         [build] llvm <target1>
         [build] rustc 1 <host> -> rustc 2 <target1>
-        [build] rustc 1 <host> -> WasmComponentLd <target1>
+        [build] rustc 1 <host> -> WasmComponentLd 2 <target1>
         [build] rustdoc 1 <target1>
-        [build] rustc 0 <host> -> GenerateCopyright <host>
+        [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <target1>
-        [dist] rustc 1 <host> -> std <target1>
+        [dist] rustc 1 <host> -> std 1 <target1>
         [dist] src <>
         [build] rustc 0 <host> -> rustfmt 1 <target1>
         [build] rustc 0 <host> -> cargo-fmt 1 <target1>
@@ -1234,6 +1234,289 @@ mod snapshot {
     }
 
     #[test]
+    fn check_compiler_no_explicit_stage() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("compiler")
+                .render_steps(), @r"
+        [check] std <host>
+        [build] llvm <host>
+        [check] rustc <host>
+        [check] cranelift <host>
+        [check] gcc <host>
+        ");
+
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("rustc")
+                .render_steps(), @r"
+        [check] std <host>
+        [build] llvm <host>
+        [check] rustc <host>
+        ");
+    }
+
+    #[test]
+    fn check_compiler_stage_0() {
+        let ctx = TestCtx::new();
+        ctx.config("check").path("compiler").stage(0).run();
+    }
+
+    #[test]
+    fn check_compiler_stage_1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("compiler")
+                .stage(1)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [check] rustc <host>
+        [check] cranelift <host>
+        [check] gcc <host>
+        ");
+    }
+
+    #[test]
+    fn check_compiler_stage_2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("compiler")
+                .stage(2)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 2 <host> -> std 2 <host>
+        [check] rustc <host>
+        [check] cranelift <host>
+        [check] gcc <host>
+        ");
+    }
+
+    #[test]
+    fn check_cross_compile() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .stage(2)
+                .targets(&[TEST_TRIPLE_1])
+                .hosts(&[TEST_TRIPLE_1])
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 2 <host> -> std 2 <host>
+        [build] rustc 1 <host> -> std 1 <target1>
+        [build] rustc 2 <host> -> std 2 <target1>
+        [check] rustc <target1>
+        [check] Rustdoc <target1>
+        [check] cranelift <target1>
+        [check] gcc <target1>
+        [check] Clippy <target1>
+        [check] Miri <target1>
+        [check] CargoMiri <target1>
+        [check] MiroptTestTools <target1>
+        [check] Rustfmt <target1>
+        [check] rust-analyzer <target1>
+        [check] TestFloatParse <target1>
+        [check] FeaturesStatusDump <target1>
+        [check] std <target1>
+        ");
+    }
+
+    #[test]
+    fn check_library_no_explicit_stage() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("library")
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [check] std <host>
+        ");
+    }
+
+    #[test]
+    fn check_library_stage_0() {
+        let ctx = TestCtx::new();
+        ctx.config("check").path("library").stage(0).run();
+    }
+
+    #[test]
+    fn check_library_stage_1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("library")
+                .stage(1)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [check] std <host>
+        ");
+    }
+
+    #[test]
+    fn check_library_stage_2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("library")
+                .stage(2)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [check] std <host>
+        ");
+    }
+
+    #[test]
+    fn check_library_cross_compile() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .paths(&["core", "alloc", "std"])
+                .targets(&[TEST_TRIPLE_1, TEST_TRIPLE_2])
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [check] std <target1>
+        [check] std <target2>
+        ");
+    }
+
+    #[test]
+    fn check_miri_no_explicit_stage() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("miri")
+                .render_steps(), @r"
+        [check] std <host>
+        [build] llvm <host>
+        [check] rustc <host>
+        [check] Miri <host>
+        ");
+    }
+
+    #[test]
+    fn check_miri_stage_0() {
+        let ctx = TestCtx::new();
+        ctx.config("check").path("miri").stage(0).run();
+    }
+
+    #[test]
+    fn check_miri_stage_1() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("miri")
+                .stage(1)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [check] rustc <host>
+        [check] Miri <host>
+        ");
+    }
+
+    #[test]
+    fn check_miri_stage_2() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("miri")
+                .stage(2)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 2 <host> -> std 2 <host>
+        [check] rustc <host>
+        [check] Miri <host>
+        ");
+    }
+
+    #[test]
+    fn check_compiletest() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("compiletest")
+                .render_steps(), @"[check] compiletest <host>");
+    }
+
+    #[test]
+    fn check_compiletest_stage1_libtest() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("compiletest")
+                .args(&["--set", "build.compiletest-use-stage0-libtest=false"])
+                .render_steps(), @r"
+        [check] std <host>
+        [build] llvm <host>
+        [check] rustc <host>
+        [check] compiletest <host>
+        ");
+    }
+
+    #[test]
+    fn check_codegen() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("rustc_codegen_cranelift")
+                .render_steps(), @r"
+        [check] std <host>
+        [build] llvm <host>
+        [check] rustc <host>
+        [check] cranelift <host>
+        [check] gcc <host>
+        ");
+    }
+
+    #[test]
+    fn check_rust_analyzer() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("rust-analyzer")
+                .render_steps(), @r"
+        [check] std <host>
+        [build] llvm <host>
+        [check] rustc <host>
+        [check] rust-analyzer <host>
+        ");
+    }
+
+    #[test]
+    fn check_bootstrap_tool() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .path("run-make-support")
+                .render_steps(), @r"
+        [check] std <host>
+        [build] llvm <host>
+        [check] rustc <host>
+        [check] RunMakeSupport <host>
+        ");
+    }
+
+    #[test]
     fn test_exclude() {
         let ctx = TestCtx::new();
         let steps = ctx.config("test").args(&["--skip", "src/tools/tidy"]).get_steps();
@@ -1384,7 +1667,7 @@ fn render_metadata(metadata: &StepMetadata) -> String {
     if let Some(compiler) = metadata.built_by {
         write!(record, "{} -> ", render_compiler(compiler));
     }
-    let stage = if let Some(stage) = metadata.stage { format!("{stage} ") } else { "".to_string() };
+    let stage = metadata.get_stage().map(|stage| format!("{stage} ")).unwrap_or_default();
     write!(record, "{} {stage}<{}>", metadata.name, normalize_target(metadata.target));
     record
 }
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 0cdfbbdaf75..49b3fec4c35 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -297,7 +297,8 @@ pub struct Config {
 
     /// Whether to use the precompiled stage0 libtest with compiletest.
     pub compiletest_use_stage0_libtest: bool,
-
+    /// Default value for `--extra-checks`
+    pub tidy_extra_checks: Option<String>,
     pub is_running_on_ci: bool,
 
     /// Cache for determining path modifications
@@ -744,6 +745,7 @@ impl Config {
             jobs,
             compiletest_diff_tool,
             compiletest_use_stage0_libtest,
+            tidy_extra_checks,
             mut ccache,
             exclude,
         } = toml.build.unwrap_or_default();
@@ -1010,6 +1012,7 @@ impl Config {
             optimized_compiler_builtins.unwrap_or(config.channel != "dev");
         config.compiletest_diff_tool = compiletest_diff_tool;
         config.compiletest_use_stage0_libtest = compiletest_use_stage0_libtest.unwrap_or(true);
+        config.tidy_extra_checks = tidy_extra_checks;
 
         let download_rustc = config.download_rustc_commit.is_some();
         config.explicit_stage_from_cli = flags_stage.is_some();
diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs
index 98e1194de72..4d29691f38b 100644
--- a/src/bootstrap/src/core/config/toml/build.rs
+++ b/src/bootstrap/src/core/config/toml/build.rs
@@ -69,6 +69,7 @@ define_config! {
         jobs: Option<u32> = "jobs",
         compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
         compiletest_use_stage0_libtest: Option<bool> = "compiletest-use-stage0-libtest",
+        tidy_extra_checks: Option<String> = "tidy-extra-checks",
         ccache: Option<StringOrBool> = "ccache",
         exclude: Option<Vec<PathBuf>> = "exclude",
     }
diff --git a/src/bootstrap/src/utils/cache/tests.rs b/src/bootstrap/src/utils/cache/tests.rs
index 8562a35b3e0..fd0a7cccd60 100644
--- a/src/bootstrap/src/utils/cache/tests.rs
+++ b/src/bootstrap/src/utils/cache/tests.rs
@@ -1,12 +1,13 @@
 use std::path::PathBuf;
 
-use crate::utils::cache::{INTERNER, Internable, TyIntern};
+use crate::utils::cache::{INTERNER, Internable, Interner, TyIntern};
 
 #[test]
 fn test_string_interning() {
-    let s1 = INTERNER.intern_str("Hello");
-    let s2 = INTERNER.intern_str("Hello");
-    let s3 = INTERNER.intern_str("world");
+    let interner = Interner::default();
+    let s1 = interner.intern_str("Hello");
+    let s2 = interner.intern_str("Hello");
+    let s3 = interner.intern_str("world");
 
     assert_eq!(s1, s2, "Same strings should be interned to the same instance");
     assert_ne!(s1, s3, "Different strings should have different interned values");
@@ -14,6 +15,8 @@ fn test_string_interning() {
 
 #[test]
 fn test_interned_equality() {
+    // Because we compare with &str, and the Deref impl accesses the global
+    // INTERNER variable, we cannot use a local Interner variable here.
     let s1 = INTERNER.intern_str("test");
     let s2 = INTERNER.intern_str("test");
 
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index f873c62588b..4041e6dd545 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -441,4 +441,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Warning,
         summary: "`llvm.lld` is no longer enabled by default for the dist profile.",
     },
+    ChangeInfo {
+        change_id: 143251,
+        severity: ChangeSeverity::Info,
+        summary: "Added new option `build.tidy-extra-checks` to specify a default value for the --extra-checks cli flag.",
+    },
 ];
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 7be83b65fbf..99b3da8b2cd 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -8,6 +8,8 @@
 	3. Copy the filenames with updated suffixes from the directory.
 */
 
+/* ignore-tidy-filelength */
+
 :root {
 	--nav-sub-mobile-padding: 8px;
 	--search-typename-width: 6.75rem;
@@ -915,32 +917,30 @@ ul.block, .block li, .block ul {
 	overflow: auto;
 }
 
-.example-wrap.digits-1:not(.hide-lines) [data-nosnippet] {
-	width: calc(1ch + var(--line-number-padding) * 2);
-}
-.example-wrap.digits-2:not(.hide-lines) [data-nosnippet] {
-	width: calc(2ch + var(--line-number-padding) * 2);
-}
-.example-wrap.digits-3:not(.hide-lines) [data-nosnippet] {
-	width: calc(3ch + var(--line-number-padding) * 2);
-}
-.example-wrap.digits-4:not(.hide-lines) [data-nosnippet] {
-	width: calc(4ch + var(--line-number-padding) * 2);
-}
-.example-wrap.digits-5:not(.hide-lines) [data-nosnippet] {
-	width: calc(5ch + var(--line-number-padding) * 2);
-}
-.example-wrap.digits-6:not(.hide-lines) [data-nosnippet] {
-	width: calc(6ch + var(--line-number-padding) * 2);
+.example-wrap code {
+	position: relative;
 }
-.example-wrap.digits-7:not(.hide-lines) [data-nosnippet] {
-	width: calc(7ch + var(--line-number-padding) * 2);
+.example-wrap pre code span {
+	display: inline;
 }
-.example-wrap.digits-8:not(.hide-lines) [data-nosnippet] {
-	width: calc(8ch + var(--line-number-padding) * 2);
+
+.example-wrap.digits-1 { --example-wrap-digits-count: 1ch; }
+.example-wrap.digits-2 { --example-wrap-digits-count: 2ch; }
+.example-wrap.digits-3 { --example-wrap-digits-count: 3ch; }
+.example-wrap.digits-4 { --example-wrap-digits-count: 4ch; }
+.example-wrap.digits-5 { --example-wrap-digits-count: 5ch; }
+.example-wrap.digits-6 { --example-wrap-digits-count: 6ch; }
+.example-wrap.digits-7 { --example-wrap-digits-count: 7ch; }
+.example-wrap.digits-8 { --example-wrap-digits-count: 8ch; }
+.example-wrap.digits-9 { --example-wrap-digits-count: 9ch; }
+
+.example-wrap [data-nosnippet] {
+	width: calc(var(--example-wrap-digits-count) + var(--line-number-padding) * 2);
 }
-.example-wrap.digits-9:not(.hide-lines) [data-nosnippet] {
-	width: calc(9ch + var(--line-number-padding) * 2);
+.example-wrap pre > code {
+	padding-left: calc(
+		var(--example-wrap-digits-count) + var(--line-number-padding) * 2
+		+ var(--line-number-right-margin));
 }
 
 .example-wrap [data-nosnippet] {
@@ -953,63 +953,25 @@ ul.block, .block li, .block ul {
 	-ms-user-select: none;
 	user-select: none;
 	padding: 0 var(--line-number-padding);
-}
-.example-wrap [data-nosnippet]:target {
-	border-right: none;
+	position: absolute;
+	left: 0;
 }
 .example-wrap .line-highlighted[data-nosnippet] {
 	background-color: var(--src-line-number-highlighted-background-color);
 }
-:root.word-wrap-source-code .example-wrap [data-nosnippet] {
-	position: absolute;
-	left: 0;
-}
-.word-wrap-source-code .example-wrap pre > code {
+.example-wrap pre > code {
 	position: relative;
-	word-break: break-all;
+	display: block;
 }
 :root.word-wrap-source-code .example-wrap pre > code {
-	display: block;
+	word-break: break-all;
 	white-space: pre-wrap;
 }
 :root.word-wrap-source-code .example-wrap pre > code * {
 	word-break: break-all;
 }
-:root.word-wrap-source-code .example-wrap.digits-1 pre > code {
-	padding-left: calc(
-		1ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
-}
-:root.word-wrap-source-code .example-wrap.digits-2 pre > code {
-	padding-left: calc(
-		2ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
-}
-:root.word-wrap-source-code .example-wrap.digits-3 pre > code {
-	padding-left: calc(
-		3ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
-}
-:root.word-wrap-source-code .example-wrap.digits-4 pre > code {
-	padding-left: calc(
-		4ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
-}
-:root.word-wrap-source-code .example-wrap.digits-5 pre > code {
-	padding-left: calc(
-		5ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
-}
-:root.word-wrap-source-code .example-wrap.digits-6 pre > code {
-	padding-left: calc(
-		6ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
-}
-:root.word-wrap-source-code .example-wrap.digits-7 pre > code {
-	padding-left: calc(
-		7ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
-}
-:root.word-wrap-source-code .example-wrap.digits-8 pre > code {
-	padding-left: calc(
-		8ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
-}
-:root.word-wrap-source-code .example-wrap.digits-9 pre > code {
-	padding-left: calc(
-		9ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
+.example-wrap [data-nosnippet]:target {
+	border-right: none;
 }
 .example-wrap.hide-lines [data-nosnippet] {
 	display: none;
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index 1796120cf8a..3cc38fa087c 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -466,17 +466,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         Some((alloc_id, Size::from_bytes(rel_offset)))
     }
 
-    /// Prepare all exposed memory for a native call.
-    /// This overapproximates the modifications which external code might make to memory:
-    /// We set all reachable allocations as initialized, mark all reachable provenances as exposed
-    /// and overwrite them with `Provenance::WILDCARD`.
-    fn prepare_exposed_for_native_call(&mut self) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
-        // We need to make a deep copy of this list, but it's fine; it also serves as scratch space
-        // for the search within `prepare_for_native_call`.
-        let exposed: Vec<AllocId> =
-            this.machine.alloc_addresses.get_mut().exposed.iter().copied().collect();
-        this.prepare_for_native_call(exposed)
+    /// Return a list of all exposed allocations.
+    fn exposed_allocs(&self) -> Vec<AllocId> {
+        let this = self.eval_context_ref();
+        this.machine.alloc_addresses.borrow().exposed.iter().copied().collect()
     }
 }
 
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index ed1851a19ae..4efa7dd4dcf 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -457,6 +457,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap")))
             }
 
+            "assert_inhabited" | "assert_zero_valid" | "assert_mem_uninitialized_valid" => {
+                // Make these a NOP, so we get the better Miri-native error messages.
+            }
+
             _ => return interp_ok(EmulateItemResult::NotSupported),
         }
 
diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs
index 9c659f65e50..9b30d8ce78b 100644
--- a/src/tools/miri/src/shims/native_lib/mod.rs
+++ b/src/tools/miri/src/shims/native_lib/mod.rs
@@ -198,7 +198,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
         for arg in args.iter() {
             if !matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) {
-                throw_unsup_format!("only scalar argument types are support for native calls")
+                throw_unsup_format!("only scalar argument types are supported for native calls")
             }
             let imm = this.read_immediate(arg)?;
             libffi_args.push(imm_to_carg(&imm, this)?);
@@ -224,16 +224,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.expose_provenance(prov)?;
             }
         }
-
-        // Prepare all exposed memory.
-        this.prepare_exposed_for_native_call()?;
-
-        // Convert them to `libffi::high::Arg` type.
+        // Convert arguments to `libffi::high::Arg` type.
         let libffi_args = libffi_args
             .iter()
             .map(|arg| arg.arg_downcast())
             .collect::<Vec<libffi::high::Arg<'_>>>();
 
+        // Prepare all exposed memory (both previously exposed, and just newly exposed since a
+        // pointer was passed as argument).
+        this.visit_reachable_allocs(this.exposed_allocs(), |this, alloc_id, info| {
+            // If there is no data behind this pointer, skip this.
+            if !matches!(info.kind, AllocKind::LiveData) {
+                return interp_ok(());
+            }
+            // It's okay to get raw access, what we do does not correspond to any actual
+            // AM operation, it just approximates the state to account for the native call.
+            let alloc = this.get_alloc_raw(alloc_id)?;
+            // Also expose the provenance of the interpreter-level allocation, so it can
+            // be read by FFI. The `black_box` is defensive programming as LLVM likes
+            // to (incorrectly) optimize away ptr2int casts whose result is unused.
+            std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance());
+            // Expose all provenances in this allocation, since the native code can do $whatever.
+            for prov in alloc.provenance().provenances() {
+                this.expose_provenance(prov)?;
+            }
+
+            // Prepare for possible write from native code if mutable.
+            if info.mutbl.is_mut() {
+                let alloc = &mut this.get_alloc_raw_mut(alloc_id)?.0;
+                alloc.prepare_for_native_access();
+                // Also expose *mutable* provenance for the interpreter-level allocation.
+                std::hint::black_box(alloc.get_bytes_unchecked_raw_mut().expose_provenance());
+            }
+
+            interp_ok(())
+        })?;
+
         // Call the function and store output, depending on return type in the function signature.
         let (ret, maybe_memevents) =
             this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
@@ -321,7 +347,8 @@ fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'
             CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()),
         ty::RawPtr(..) => {
             let s = v.to_scalar().to_pointer(cx)?.addr();
-            // This relies on the `expose_provenance` in `prepare_for_native_call`.
+            // This relies on the `expose_provenance` in the `visit_reachable_allocs` callback
+            // above.
             CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
         }
         _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs
index dd2dd346231..1dc334486c3 100644
--- a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs
+++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs
@@ -20,7 +20,7 @@ fn main() {
         let mut buf: MaybeUninit<[u8; 4]> = std::mem::MaybeUninit::uninit();
         // Read 4 bytes from a 3-byte file.
         assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 4), 3);
-        buf.assume_init(); //~ERROR: Undefined Behavior: constructing invalid value at .value[3]: encountered uninitialized memory, but expected an integer
+        buf.assume_init(); //~ERROR: encountered uninitialized memory, but expected an integer
         assert_eq!(libc::close(fd), 0);
     }
     remove_file(&path).unwrap();
diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr
index fadb31e3a8f..83119f087ff 100644
--- a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr
+++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: constructing invalid value at .value[3]: encountered uninitialized memory, but expected an integer
+error: Undefined Behavior: constructing invalid value at [3]: encountered uninitialized memory, but expected an integer
   --> tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs:LL:CC
    |
-LL | ...   buf.assume_init();
-   |       ^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+LL |         buf.assume_init();
+   |         ^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs
index dd3246d8120..34fef6b9ee5 100644
--- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs
+++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs
@@ -1,11 +1,6 @@
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
-//@normalize-stderr-test: "\n +at [^\n]+" -> ""
-//@error-in-other-file: aborted execution
 #![feature(never_type)]
 
 #[allow(deprecated, invalid_value)]
 fn main() {
-    let _ = unsafe { std::mem::uninitialized::<!>() };
+    let _ = unsafe { std::mem::uninitialized::<!>() }; //~ERROR: constructing invalid value
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr
index 3db8a5be205..36642208afe 100644
--- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr
@@ -1,27 +1,13 @@
-
-thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
-aborted execution: attempted to instantiate uninhabited type `!`
-note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
-thread caused non-unwinding panic. aborting.
-error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   |
-LL | ABORT()
-   | ^ abnormal termination occurred here
-   |
-   = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
-   = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
-   = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
-   = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC
-   = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC
-note: inside `main`
+error: Undefined Behavior: constructing invalid value: encountered a value of the never type `!`
   --> tests/fail/intrinsics/uninit_uninhabited_type.rs:LL:CC
    |
 LL |     let _ = unsafe { std::mem::uninitialized::<!>() };
-   | ^
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/fail/intrinsics/uninit_uninhabited_type.rs:LL:CC
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs
index 3d355bad626..cca2c5ae984 100644
--- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs
+++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs
@@ -1,10 +1,4 @@
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
-//@normalize-stderr-test: "\n +at [^\n]+" -> ""
-//@error-in-other-file: aborted execution
-
 #[allow(deprecated, invalid_value)]
 fn main() {
-    let _ = unsafe { std::mem::zeroed::<fn()>() };
+    let _ = unsafe { std::mem::zeroed::<fn()>() }; //~ERROR: constructing invalid value
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr
index a1e476328b0..53f3f8d1404 100644
--- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr
@@ -1,27 +1,13 @@
-
-thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
-aborted execution: attempted to zero-initialize type `fn()`, which is invalid
-note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
-thread caused non-unwinding panic. aborting.
-error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   |
-LL | ABORT()
-   | ^ abnormal termination occurred here
-   |
-   = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
-   = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
-   = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
-   = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC
-   = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC
-note: inside `main`
+error: Undefined Behavior: constructing invalid value: encountered a null function pointer
   --> tests/fail/intrinsics/zero_fn_ptr.rs:LL:CC
    |
 LL |     let _ = unsafe { std::mem::zeroed::<fn()>() };
-   | ^
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/fail/intrinsics/zero_fn_ptr.rs:LL:CC
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/validity/uninit_float.stderr b/src/tools/miri/tests/fail/validity/uninit_float.stderr
index 1b948b062e1..0c859d72764 100644
--- a/src/tools/miri/tests/fail/validity/uninit_float.stderr
+++ b/src/tools/miri/tests/fail/validity/uninit_float.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: constructing invalid value at .value[0]: encountered uninitialized memory, but expected a floating point number
+error: Undefined Behavior: constructing invalid value at [0]: encountered uninitialized memory, but expected a floating point number
   --> tests/fail/validity/uninit_float.rs:LL:CC
    |
 LL |     let _val: [f32; 1] = unsafe { std::mem::uninitialized() };
diff --git a/src/tools/miri/tests/fail/validity/uninit_integer.stderr b/src/tools/miri/tests/fail/validity/uninit_integer.stderr
index b17bdee65da..5d31e2659ee 100644
--- a/src/tools/miri/tests/fail/validity/uninit_integer.stderr
+++ b/src/tools/miri/tests/fail/validity/uninit_integer.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: constructing invalid value at .value[0]: encountered uninitialized memory, but expected an integer
+error: Undefined Behavior: constructing invalid value at [0]: encountered uninitialized memory, but expected an integer
   --> tests/fail/validity/uninit_integer.rs:LL:CC
    |
 LL |     let _val = unsafe { std::mem::MaybeUninit::<[usize; 1]>::uninit().assume_init() };
diff --git a/src/tools/miri/tests/fail/validity/uninit_raw_ptr.stderr b/src/tools/miri/tests/fail/validity/uninit_raw_ptr.stderr
index 269af6061c2..d2e9408adbe 100644
--- a/src/tools/miri/tests/fail/validity/uninit_raw_ptr.stderr
+++ b/src/tools/miri/tests/fail/validity/uninit_raw_ptr.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: constructing invalid value at .value[0]: encountered uninitialized memory, but expected a raw pointer
+error: Undefined Behavior: constructing invalid value at [0]: encountered uninitialized memory, but expected a raw pointer
   --> tests/fail/validity/uninit_raw_ptr.rs:LL:CC
    |
 LL |     let _val = unsafe { std::mem::MaybeUninit::<[*const u8; 1]>::uninit().assume_init() };
diff --git a/tests/rustdoc-gui/docblock-code-block-line-number.goml b/tests/rustdoc-gui/docblock-code-block-line-number.goml
index 97273ceb195..0df9cc2a659 100644
--- a/tests/rustdoc-gui/docblock-code-block-line-number.goml
+++ b/tests/rustdoc-gui/docblock-code-block-line-number.goml
@@ -129,13 +129,13 @@ define-function: ("check-line-numbers-existence", [], block {
     wait-for-local-storage-false: {"rustdoc-line-numbers": "true" }
     assert-false: ".example-line-numbers"
     // Line numbers should still be there.
-    assert-css: ("[data-nosnippet]", { "display": "inline-block"})
+    assert-css: ("[data-nosnippet]", { "display": "block"})
     // Now disabling the setting.
     click: "input#line-numbers"
     wait-for-local-storage: {"rustdoc-line-numbers": "true" }
     assert-false: ".example-line-numbers"
     // Line numbers should still be there.
-    assert-css: ("[data-nosnippet]", { "display": "inline-block"})
+    assert-css: ("[data-nosnippet]", { "display": "block"})
     // Closing settings menu.
     click: "#settings-menu"
     wait-for-css: ("#settings", {"display": "none"})
diff --git a/tests/rustdoc-gui/scrape-examples-button-focus.goml b/tests/rustdoc-gui/scrape-examples-button-focus.goml
index 12246a37661..f6e836e2360 100644
--- a/tests/rustdoc-gui/scrape-examples-button-focus.goml
+++ b/tests/rustdoc-gui/scrape-examples-button-focus.goml
@@ -5,7 +5,7 @@ go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
 // The next/prev buttons vertically scroll the code viewport between examples
 move-cursor-to: ".scraped-example-list > .scraped-example"
 wait-for: ".scraped-example-list > .scraped-example .next"
-store-value: (initialScrollTop, 250)
+store-value: (initialScrollTop, 236)
 assert-property: (".scraped-example-list > .scraped-example .rust", {
     "scrollTop": |initialScrollTop|,
 }, NEAR)
diff --git a/tests/rustdoc-gui/source-code-wrapping.goml b/tests/rustdoc-gui/source-code-wrapping.goml
index cb2fd3052cd..0dab9c72ea9 100644
--- a/tests/rustdoc-gui/source-code-wrapping.goml
+++ b/tests/rustdoc-gui/source-code-wrapping.goml
@@ -31,17 +31,32 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/trait_bounds/index.html"
 click: "#settings-menu"
 wait-for: "#settings"
 
-store-size: (".example-wrap .rust code", {"width": rust_width, "height": rust_height})
-store-size: (".example-wrap .language-text code", {"width": txt_width, "height": txt_height})
+store-property: (".example-wrap .rust code", {"scrollWidth": rust_width, "scrollHeight": rust_height})
+store-property: (".example-wrap .language-text code", {"scrollWidth": txt_width, "scrollHeight": txt_height})
 call-function: ("click-code-wrapping", {"expected": "true"})
-wait-for-size-false: (".example-wrap .rust code", {"width": |rust_width|, "height": |rust_height|})
+wait-for-property-false: (
+    ".example-wrap .rust code",
+    {"scrollWidth": |rust_width|, "scrollHeight": |rust_height|},
+)
 
-store-size: (".example-wrap .rust code", {"width": new_rust_width, "height": new_rust_height})
-store-size: (".example-wrap .language-text code", {"width": new_txt_width, "height": new_txt_height})
+store-property: (
+    ".example-wrap .rust code",
+    {"scrollWidth": new_rust_width, "scrollHeight": new_rust_height},
+)
+store-property: (
+    ".example-wrap .language-text code",
+    {"scrollWidth": new_txt_width, "scrollHeight": new_txt_height},
+)
 
 assert: |rust_width| > |new_rust_width| && |rust_height| < |new_rust_height|
 assert: |txt_width| > |new_txt_width| && |txt_height| < |new_txt_height|
 
 call-function: ("click-code-wrapping", {"expected": "false"})
-wait-for-size: (".example-wrap .rust code", {"width": |rust_width|, "height": |rust_height|})
-assert-size: (".example-wrap .language-text code", {"width": |txt_width|, "height": |txt_height|})
+wait-for-property: (
+    ".example-wrap .rust code",
+    {"scrollWidth": |rust_width|, "scrollHeight": |rust_height|},
+)
+assert-property: (
+    ".example-wrap .language-text code",
+    {"scrollWidth": |txt_width|, "scrollHeight": |txt_height|},
+)
diff --git a/tests/ui/mir/enum/negative_discr_break.rs b/tests/ui/mir/enum/negative_discr_break.rs
new file mode 100644
index 00000000000..fa1284f72a0
--- /dev/null
+++ b/tests/ui/mir/enum/negative_discr_break.rs
@@ -0,0 +1,14 @@
+//@ run-fail
+//@ compile-flags: -C debug-assertions
+//@ error-pattern: trying to construct an enum from an invalid value 0xfd
+
+#[allow(dead_code)]
+enum Foo {
+    A = -2,
+    B = -1,
+    C = 1,
+}
+
+fn main() {
+    let _val: Foo = unsafe { std::mem::transmute::<i8, Foo>(-3) };
+}
diff --git a/tests/ui/mir/enum/negative_discr_ok.rs b/tests/ui/mir/enum/negative_discr_ok.rs
new file mode 100644
index 00000000000..5c15b33fa84
--- /dev/null
+++ b/tests/ui/mir/enum/negative_discr_ok.rs
@@ -0,0 +1,53 @@
+//@ run-pass
+//@ compile-flags: -C debug-assertions
+
+#[allow(dead_code)]
+#[derive(Debug, PartialEq)]
+enum Foo {
+    A = -12121,
+    B = -2,
+    C = -1,
+    D = 1,
+    E = 2,
+    F = 12121,
+}
+
+#[allow(dead_code)]
+#[repr(i64)]
+#[derive(Debug, PartialEq)]
+enum Bar {
+    A = i64::MIN,
+    B = -2,
+    C = -1,
+    D = 1,
+    E = 2,
+    F = i64::MAX,
+}
+
+fn main() {
+    let val: Foo = unsafe { std::mem::transmute::<i16, Foo>(-12121) };
+    assert_eq!(val, Foo::A);
+    let val: Foo = unsafe { std::mem::transmute::<i16, Foo>(-2) };
+    assert_eq!(val, Foo::B);
+    let val: Foo = unsafe { std::mem::transmute::<i16, Foo>(-1) };
+    assert_eq!(val, Foo::C);
+    let val: Foo = unsafe { std::mem::transmute::<i16, Foo>(1) };
+    assert_eq!(val, Foo::D);
+    let val: Foo = unsafe { std::mem::transmute::<i16, Foo>(2) };
+    assert_eq!(val, Foo::E);
+    let val: Foo = unsafe { std::mem::transmute::<i16, Foo>(12121) };
+    assert_eq!(val, Foo::F);
+
+    let val: Bar = unsafe { std::mem::transmute::<i64, Bar>(i64::MIN) };
+    assert_eq!(val, Bar::A);
+    let val: Bar = unsafe { std::mem::transmute::<i64, Bar>(-2) };
+    assert_eq!(val, Bar::B);
+    let val: Bar = unsafe { std::mem::transmute::<i64, Bar>(-1) };
+    assert_eq!(val, Bar::C);
+    let val: Bar = unsafe { std::mem::transmute::<i64, Bar>(1) };
+    assert_eq!(val, Bar::D);
+    let val: Bar = unsafe { std::mem::transmute::<i64, Bar>(2) };
+    assert_eq!(val, Bar::E);
+    let val: Bar = unsafe { std::mem::transmute::<i64, Bar>(i64::MAX) };
+    assert_eq!(val, Bar::F);
+}