about summary refs log tree commit diff
path: root/compiler/rustc_const_eval
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_const_eval')
-rw-r--r--compiler/rustc_const_eval/messages.ftl38
-rw-r--r--compiler/rustc_const_eval/src/check_consts/mod.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/dummy_machine.rs1
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs36
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs13
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs29
-rw-r--r--compiler/rustc_const_eval/src/errors.rs8
-rw-r--r--compiler/rustc_const_eval/src/interpret/call.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs55
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs92
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs115
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs1
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs52
-rw-r--r--compiler/rustc_const_eval/src/interpret/mod.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/stack.rs7
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs1
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs10
-rw-r--r--compiler/rustc_const_eval/src/util/check_validity_requirement.rs13
18 files changed, 326 insertions, 151 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index b767ca9a3c2..aa0bc42d448 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -56,6 +56,17 @@ const_eval_const_context = {$kind ->
     *[other] {""}
 }
 
+const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global
+    .note = use `const_make_global` to make allocated pointers immutable before returning
+
+const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc}
+
+const_eval_const_make_global_ptr_is_non_heap = pointer passed to `const_make_global` does not point to a heap allocation: {$ptr}
+
+const_eval_const_make_global_with_dangling_ptr = pointer passed to `const_make_global` is dangling: {$ptr}
+
+const_eval_const_make_global_with_offset = making {$ptr} global which does not point to the beginning of an object
+
 const_eval_copy_nonoverlapping_overlapping =
     `copy_nonoverlapping` called on overlapping ranges
 
@@ -296,19 +307,22 @@ const_eval_pointer_arithmetic_overflow =
     overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
 
 const_eval_pointer_out_of_bounds =
-    {const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$inbounds_size_is_neg ->
-        [false] {$alloc_size_minus_ptr_offset ->
-                [0] is at or beyond the end of the allocation of size {$alloc_size ->
-                    [1] 1 byte
-                    *[x] {$alloc_size} bytes
+    {const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$ptr_offset_is_neg ->
+        [true] points to before the beginning of the allocation
+        *[false] {$inbounds_size_is_neg ->
+            [false] {$alloc_size_minus_ptr_offset ->
+                        [0] is at or beyond the end of the allocation of size {$alloc_size ->
+                            [1] 1 byte
+                            *[x] {$alloc_size} bytes
+                        }
+                        [1] is only 1 byte from the end of the allocation
+                        *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
+                    }
+            *[true] {$ptr_offset_abs ->
+                    [0] is at the beginning of the allocation
+                    *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation
                 }
-                [1] is only 1 byte from the end of the allocation
-                *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
-            }
-        *[true] {$ptr_offset_abs ->
-                [0] is at the beginning of the allocation
-                *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation
-            }
+        }
     }
 
 const_eval_pointer_use_after_free =
diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs
index 9ab8e0692e1..ebf18c6f2ac 100644
--- a/compiler/rustc_const_eval/src/check_consts/mod.rs
+++ b/compiler/rustc_const_eval/src/check_consts/mod.rs
@@ -93,7 +93,7 @@ pub fn rustc_allow_const_fn_unstable(
 /// world into two functions: those that are safe to expose on stable (and hence may not use
 /// unstable features, not even recursively), and those that are not.
 pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
-    // A default body in a `#[const_trait]` is const-stable when the trait is const-stable.
+    // A default body in a `const trait` is const-stable when the trait is const-stable.
     if tcx.is_const_default_method(def_id) {
         return is_fn_or_trait_safe_to_expose_on_stable(tcx, tcx.parent(def_id));
     }
diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs
index b6e2682af36..438aed41b8b 100644
--- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs
@@ -49,7 +49,6 @@ impl HasStaticRootDefId for DummyMachine {
 
 impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
     interpret::compile_time_machine!(<'tcx>);
-    type MemoryKind = !;
     const PANIC_ON_ALLOC_FAIL: bool = true;
 
     // We want to just eval random consts in the program, so `eval_mir_const` can fail.
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index 08fc03d9c46..3e880d02001 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -2,7 +2,7 @@ use std::mem;
 
 use rustc_errors::{Diag, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg};
 use rustc_middle::mir::AssertKind;
-use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo};
+use rustc_middle::mir::interpret::{AllocId, Provenance, ReportedErrorInfo};
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::layout::LayoutError;
 use rustc_middle::ty::{ConstInt, TyCtxt};
@@ -11,8 +11,8 @@ use rustc_span::{Span, Symbol};
 use super::CompileTimeMachine;
 use crate::errors::{self, FrameNote, ReportErrorExt};
 use crate::interpret::{
-    ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, err_inval,
-    err_machine_stop,
+    CtfeProvenance, ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType,
+    Pointer, err_inval, err_machine_stop,
 };
 
 /// The CTFE machine has some custom error kinds.
@@ -22,8 +22,22 @@ pub enum ConstEvalErrKind {
     ModifiedGlobal,
     RecursiveStatic,
     AssertFailure(AssertKind<ConstInt>),
-    Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
+    Panic {
+        msg: Symbol,
+        line: u32,
+        col: u32,
+        file: Symbol,
+    },
     WriteThroughImmutablePointer,
+    /// Called `const_make_global` twice.
+    ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId),
+    /// Called `const_make_global` on a non-heap pointer.
+    ConstMakeGlobalPtrIsNonHeap(Pointer<Option<CtfeProvenance>>),
+    /// Called `const_make_global` on a dangling pointer.
+    ConstMakeGlobalWithDanglingPtr(Pointer<Option<CtfeProvenance>>),
+    /// Called `const_make_global` on a pointer that does not start at the
+    /// beginning of an object.
+    ConstMakeGlobalWithOffset(Pointer<Option<CtfeProvenance>>),
 }
 
 impl MachineStopType for ConstEvalErrKind {
@@ -38,6 +52,12 @@ impl MachineStopType for ConstEvalErrKind {
             RecursiveStatic => const_eval_recursive_static,
             AssertFailure(x) => x.diagnostic_message(),
             WriteThroughImmutablePointer => const_eval_write_through_immutable_pointer,
+            ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => {
+                const_eval_const_make_global_ptr_already_made_global
+            }
+            ConstMakeGlobalPtrIsNonHeap(_) => const_eval_const_make_global_ptr_is_non_heap,
+            ConstMakeGlobalWithDanglingPtr(_) => const_eval_const_make_global_with_dangling_ptr,
+            ConstMakeGlobalWithOffset(_) => const_eval_const_make_global_with_offset,
         }
     }
     fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) {
@@ -51,6 +71,14 @@ impl MachineStopType for ConstEvalErrKind {
             Panic { msg, .. } => {
                 adder("msg".into(), msg.into_diag_arg(&mut None));
             }
+            ConstMakeGlobalPtrIsNonHeap(ptr)
+            | ConstMakeGlobalWithOffset(ptr)
+            | ConstMakeGlobalWithDanglingPtr(ptr) => {
+                adder("ptr".into(), format!("{ptr:?}").into_diag_arg(&mut None));
+            }
+            ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => {
+                adder("alloc".into(), alloc.into_diag_arg(&mut None));
+            }
         }
     }
 }
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 4bd4b493009..ce72d59b8b0 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -18,7 +18,7 @@ use tracing::{debug, instrument, trace};
 use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine};
 use crate::const_eval::CheckAlignment;
 use crate::interpret::{
-    CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind,
+    CtfeValidationMode, GlobalId, Immediate, InternError, InternKind, InterpCx, InterpErrorKind,
     InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, ReturnContinuation, create_static_alloc,
     intern_const_alloc_recursive, interp_ok, throw_exhaust,
 };
@@ -93,25 +93,30 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
     // Since evaluation had no errors, validate the resulting constant.
     const_validate_mplace(ecx, &ret, cid)?;
 
-    // Only report this after validation, as validaiton produces much better diagnostics.
+    // Only report this after validation, as validation produces much better diagnostics.
     // FIXME: ensure validation always reports this and stop making interning care about it.
 
     match intern_result {
         Ok(()) => {}
-        Err(InternResult::FoundDanglingPointer) => {
+        Err(InternError::DanglingPointer) => {
             throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
                 ecx.tcx
                     .dcx()
                     .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
             )));
         }
-        Err(InternResult::FoundBadMutablePointer) => {
+        Err(InternError::BadMutablePointer) => {
             throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
                 ecx.tcx
                     .dcx()
                     .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
             )));
         }
+        Err(InternError::ConstAllocNotGlobal) => {
+            throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
+                ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }),
+            )));
+        }
     }
 
     interp_ok(R::make_result(ret, ecx))
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 52fc898192a..f24fb18f83b 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -169,13 +169,19 @@ pub type CompileTimeInterpCx<'tcx> = InterpCx<'tcx, CompileTimeMachine<'tcx>>;
 
 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
 pub enum MemoryKind {
-    Heap,
+    Heap {
+        /// Indicates whether `make_global` was called on this allocation.
+        /// If this is `true`, the allocation must be immutable.
+        was_made_global: bool,
+    },
 }
 
 impl fmt::Display for MemoryKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            MemoryKind::Heap => write!(f, "heap allocation"),
+            MemoryKind::Heap { was_made_global } => {
+                write!(f, "heap allocation{}", if *was_made_global { " (made global)" } else { "" })
+            }
         }
     }
 }
@@ -184,7 +190,7 @@ impl interpret::MayLeak for MemoryKind {
     #[inline(always)]
     fn may_leak(self) -> bool {
         match self {
-            MemoryKind::Heap => false,
+            MemoryKind::Heap { was_made_global } => was_made_global,
         }
     }
 }
@@ -314,8 +320,6 @@ impl<'tcx> CompileTimeMachine<'tcx> {
 impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
     compile_time_machine!(<'tcx>);
 
-    type MemoryKind = MemoryKind;
-
     const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
 
     #[inline(always)]
@@ -359,8 +363,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
         if let ty::InstanceKind::Item(def) = instance.def {
             // Execution might have wandered off into other crates, so we cannot do a stability-
             // sensitive check here. But we can at least rule out functions that are not const at
-            // all. That said, we have to allow calling functions inside a trait marked with
-            // #[const_trait]. These *are* const-checked!
+            // all. That said, we have to allow calling functions inside a `const trait`. These
+            // *are* const-checked!
             if !ecx.tcx.is_const_fn(def) || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) {
                 // We certainly do *not* want to actually call the fn
                 // though, so be sure we return here.
@@ -420,7 +424,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
                 let ptr = ecx.allocate_ptr(
                     Size::from_bytes(size),
                     align,
-                    interpret::MemoryKind::Machine(MemoryKind::Heap),
+                    interpret::MemoryKind::Machine(MemoryKind::Heap { was_made_global: false }),
                     AllocInit::Uninit,
                 )?;
                 ecx.write_pointer(ptr, dest)?;
@@ -453,10 +457,17 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
                     ecx.deallocate_ptr(
                         ptr,
                         Some((size, align)),
-                        interpret::MemoryKind::Machine(MemoryKind::Heap),
+                        interpret::MemoryKind::Machine(MemoryKind::Heap { was_made_global: false }),
                     )?;
                 }
             }
+
+            sym::const_make_global => {
+                let ptr = ecx.read_pointer(&args[0])?;
+                ecx.make_const_heap_ptr_global(ptr)?;
+                ecx.write_pointer(ptr, dest)?;
+            }
+
             // The intrinsic represents whether the value is known to the optimizer (LLVM).
             // We're not doing any optimizations here, so there is no optimizer that could know the value.
             // (We know the value here in the machine of course, but this is the runtime of that code,
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 49cd7138748..b6a64035261 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -44,6 +44,14 @@ pub(crate) struct MutablePtrInFinal {
 }
 
 #[derive(Diagnostic)]
+#[diag(const_eval_const_heap_ptr_in_final)]
+#[note]
+pub(crate) struct ConstHeapPtrInFinal {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(const_eval_unstable_in_stable_exposed)]
 pub(crate) struct UnstableInStableExposed {
     pub gate: String,
diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs
index ad3e02580f3..1503f3bcd99 100644
--- a/compiler/rustc_const_eval/src/interpret/call.rs
+++ b/compiler/rustc_const_eval/src/interpret/call.rs
@@ -6,7 +6,7 @@ use std::borrow::Cow;
 use either::{Left, Right};
 use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx};
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, TyAndLayout};
+use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
 use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
 use rustc_middle::{bug, mir, span_bug};
 use rustc_span::sym;
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 41fc8d47cd3..11e7706fe60 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -7,8 +7,8 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::layout::{
-    self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
-    TyAndLayout,
+    self, FnAbiError, FnAbiOf, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf,
+    LayoutOfHelpers, TyAndLayout,
 };
 use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypingEnv, Variance};
 use rustc_middle::{mir, span_bug};
@@ -92,20 +92,6 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> {
     }
 }
 
-impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
-    /// This inherent method takes priority over the trait method with the same name in LayoutOf,
-    /// and allows wrapping the actual [LayoutOf::layout_of] with a tracing span.
-    /// See [LayoutOf::layout_of] for the original documentation.
-    #[inline(always)]
-    pub fn layout_of(
-        &self,
-        ty: Ty<'tcx>,
-    ) -> <InterpCx<'tcx, M> as LayoutOfHelpers<'tcx>>::LayoutOfResult {
-        let _span = enter_trace_span!(M, "InterpCx::layout_of", "ty = {:?}", ty.kind());
-        LayoutOf::layout_of(self, ty)
-    }
-}
-
 impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> {
     type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpErrorKind<'tcx>>;
 
@@ -121,6 +107,43 @@ impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> {
     }
 }
 
+impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
+    /// This inherent method takes priority over the trait method with the same name in LayoutOf,
+    /// and allows wrapping the actual [LayoutOf::layout_of] with a tracing span.
+    /// See [LayoutOf::layout_of] for the original documentation.
+    #[inline(always)]
+    pub fn layout_of(&self, ty: Ty<'tcx>) -> <Self as LayoutOfHelpers<'tcx>>::LayoutOfResult {
+        let _span = enter_trace_span!(M, "InterpCx::layout_of", ty = ?ty.kind());
+        LayoutOf::layout_of(self, ty)
+    }
+
+    /// This inherent method takes priority over the trait method with the same name in FnAbiOf,
+    /// and allows wrapping the actual [FnAbiOf::fn_abi_of_fn_ptr] with a tracing span.
+    /// See [FnAbiOf::fn_abi_of_fn_ptr] for the original documentation.
+    #[inline(always)]
+    pub fn fn_abi_of_fn_ptr(
+        &self,
+        sig: ty::PolyFnSig<'tcx>,
+        extra_args: &'tcx ty::List<Ty<'tcx>>,
+    ) -> <Self as FnAbiOfHelpers<'tcx>>::FnAbiOfResult {
+        let _span = enter_trace_span!(M, "InterpCx::fn_abi_of_fn_ptr", ?sig, ?extra_args);
+        FnAbiOf::fn_abi_of_fn_ptr(self, sig, extra_args)
+    }
+
+    /// This inherent method takes priority over the trait method with the same name in FnAbiOf,
+    /// and allows wrapping the actual [FnAbiOf::fn_abi_of_instance] with a tracing span.
+    /// See [FnAbiOf::fn_abi_of_instance] for the original documentation.
+    #[inline(always)]
+    pub fn fn_abi_of_instance(
+        &self,
+        instance: ty::Instance<'tcx>,
+        extra_args: &'tcx ty::List<Ty<'tcx>>,
+    ) -> <Self as FnAbiOfHelpers<'tcx>>::FnAbiOfResult {
+        let _span = enter_trace_span!(M, "InterpCx::fn_abi_of_instance", ?instance, ?extra_args);
+        FnAbiOf::fn_abi_of_instance(self, instance, extra_args)
+    }
+}
+
 /// Test if it is valid for a MIR assignment to assign `src`-typed place to `dest`-typed value.
 /// This test should be symmetric, as it is primarily about layout compatibility.
 pub(super) fn mir_assign_valid_types<'tcx>(
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index f0f958d069e..bb59b9f5418 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -26,21 +26,18 @@ use rustc_middle::ty::layout::TyAndLayout;
 use rustc_span::def_id::LocalDefId;
 use tracing::{instrument, trace};
 
-use super::{
-    AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok,
-};
-use crate::const_eval;
+use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, interp_ok};
 use crate::const_eval::DummyMachine;
-use crate::errors::NestedStaticInThreadLocal;
+use crate::{const_eval, errors};
 
-pub trait CompileTimeMachine<'tcx, T> = Machine<
+pub trait CompileTimeMachine<'tcx> = Machine<
         'tcx,
-        MemoryKind = T,
+        MemoryKind = const_eval::MemoryKind,
         Provenance = CtfeProvenance,
         ExtraFnVal = !,
         FrameExtra = (),
         AllocExtra = (),
-        MemoryMap = FxIndexMap<AllocId, (MemoryKind<T>, Allocation)>,
+        MemoryMap = FxIndexMap<AllocId, (MemoryKind<const_eval::MemoryKind>, Allocation)>,
     > + HasStaticRootDefId;
 
 pub trait HasStaticRootDefId {
@@ -62,18 +59,32 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> {
 /// already mutable (as a sanity check).
 ///
 /// Returns an iterator over all relocations referred to by this allocation.
-fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
+fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>(
     ecx: &mut InterpCx<'tcx, M>,
     alloc_id: AllocId,
     mutability: Mutability,
     disambiguator: Option<&mut DisambiguatorState>,
-) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> {
+) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, InternError> {
     trace!("intern_shallow {:?}", alloc_id);
     // remove allocation
     // FIXME(#120456) - is `swap_remove` correct?
-    let Some((_kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
-        return Err(());
+    let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
+        return Err(InternError::DanglingPointer);
     };
+
+    match kind {
+        MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => {
+            if !was_made_global {
+                // Attempting to intern a `const_allocate`d pointer that was not made global via
+                // `const_make_global`. We want to error here, but we have to first put the
+                // allocation back into the `alloc_map` to keep things in a consistent state.
+                ecx.memory.alloc_map.insert(alloc_id, (kind, alloc));
+                return Err(InternError::ConstAllocNotGlobal);
+            }
+        }
+        MemoryKind::Stack | MemoryKind::CallerLocation => {}
+    }
+
     // Set allocation mutability as appropriate. This is used by LLVM to put things into
     // read-only memory, and also by Miri when evaluating other globals that
     // access this one.
@@ -99,7 +110,7 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
     } else {
         ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
     }
-    Ok(alloc.0.0.provenance().ptrs().iter().map(|&(_, prov)| prov))
+    Ok(alloc.inner().provenance().ptrs().iter().map(|&(_, prov)| prov))
 }
 
 /// Creates a new `DefId` and feeds all the right queries to make this `DefId`
@@ -125,7 +136,7 @@ fn intern_as_new_static<'tcx>(
     tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
 
     if tcx.is_thread_local_static(static_id.into()) {
-        tcx.dcx().emit_err(NestedStaticInThreadLocal { span: tcx.def_span(static_id) });
+        tcx.dcx().emit_err(errors::NestedStaticInThreadLocal { span: tcx.def_span(static_id) });
     }
 
     // These do not inherit the codegen attrs of the parent static allocation, since
@@ -151,9 +162,10 @@ pub enum InternKind {
 }
 
 #[derive(Debug)]
-pub enum InternResult {
-    FoundBadMutablePointer,
-    FoundDanglingPointer,
+pub enum InternError {
+    BadMutablePointer,
+    DanglingPointer,
+    ConstAllocNotGlobal,
 }
 
 /// Intern `ret` and everything it references.
@@ -163,11 +175,11 @@ pub enum InternResult {
 ///
 /// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller.
 #[instrument(level = "debug", skip(ecx))]
-pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval::MemoryKind>>(
+pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>(
     ecx: &mut InterpCx<'tcx, M>,
     intern_kind: InternKind,
     ret: &MPlaceTy<'tcx>,
-) -> Result<(), InternResult> {
+) -> Result<(), InternError> {
     let mut disambiguator = DisambiguatorState::new();
 
     // We are interning recursively, and for mutability we are distinguishing the "root" allocation
@@ -181,7 +193,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
         }
         InternKind::Static(Mutability::Not) => {
             (
-                // Outermost allocation is mutable if `!Freeze`.
+                // Outermost allocation is mutable if `!Freeze` i.e. contains interior mutable types.
                 if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env) {
                     Mutability::Not
                 } else {
@@ -224,6 +236,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
     // We want to first report "dangling" and then "mutable", so we need to delay reporting these
     // errors.
     let mut result = Ok(());
+    let mut found_bad_mutable_ptr = false;
 
     // Keep interning as long as there are things to intern.
     // We show errors if there are dangling pointers, or mutable pointers in immutable contexts
@@ -278,18 +291,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
             // when there is memory there that someone might expect to be mutable, but we make it immutable.
             let dangling = !is_already_global && !ecx.memory.alloc_map.contains_key(&alloc_id);
             if !dangling {
-                // Found a mutable pointer inside a const where inner allocations should be
-                // immutable.
-                if !ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
-                    span_bug!(
-                        ecx.tcx.span,
-                        "the static const safety checks accepted a mutable pointer they should not have accepted"
-                    );
-                }
-                // Prefer dangling pointer errors over mutable pointer errors
-                if result.is_ok() {
-                    result = Err(InternResult::FoundBadMutablePointer);
-                }
+                found_bad_mutable_ptr = true;
             }
         }
         if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
@@ -310,18 +312,31 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
         just_interned.insert(alloc_id);
         match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) {
             Ok(nested) => todo.extend(nested),
-            Err(()) => {
-                ecx.tcx.dcx().delayed_bug("found dangling pointer during const interning");
-                result = Err(InternResult::FoundDanglingPointer);
+            Err(err) => {
+                ecx.tcx.dcx().delayed_bug("error during const interning");
+                result = Err(err);
             }
         }
     }
+    if found_bad_mutable_ptr && result.is_ok() {
+        // We found a mutable pointer inside a const where inner allocations should be immutable,
+        // and there was no other error. This should usually never happen! However, this can happen
+        // in unleash-miri mode, so report it as a normal error then.
+        if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
+            result = Err(InternError::BadMutablePointer);
+        } else {
+            span_bug!(
+                ecx.tcx.span,
+                "the static const safety checks accepted a mutable pointer they should not have accepted"
+            );
+        }
+    }
     result
 }
 
 /// Intern `ret`. This function assumes that `ret` references no other allocation.
 #[instrument(level = "debug", skip(ecx))]
-pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
+pub fn intern_const_alloc_for_constprop<'tcx, M: CompileTimeMachine<'tcx>>(
     ecx: &mut InterpCx<'tcx, M>,
     alloc_id: AllocId,
 ) -> InterpResult<'tcx, ()> {
@@ -330,10 +345,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>
         return interp_ok(());
     }
     // Move allocation to `tcx`.
-    if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None)
-        .map_err(|()| err_ub!(DeadLocal))?
-        .next()
-    {
+    if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None).unwrap().next() {
         // We are not doing recursive interning, so we don't currently support provenance.
         // (If this assertion ever triggers, we should just implement a
         // proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 1eba1f2f03c..e24a355891d 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -4,8 +4,9 @@
 
 use std::assert_matches::assert_matches;
 
-use rustc_abi::{FieldIdx, Size};
+use rustc_abi::{FieldIdx, HasDataLayout, Size};
 use rustc_apfloat::ieee::{Double, Half, Quad, Single};
+use rustc_middle::mir::interpret::{read_target_uint, write_target_uint};
 use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{Ty, TyCtxt};
@@ -30,7 +31,7 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll
 }
 impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// Generates a value of `TypeId` for `ty` in-place.
-    pub(crate) fn write_type_id(
+    fn write_type_id(
         &mut self,
         ty: Ty<'tcx>,
         dest: &PlaceTy<'tcx, M::Provenance>,
@@ -44,17 +45,67 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         )?;
         self.copy_op_allow_transmute(&op, dest)?;
 
-        // Give the first pointer-size bytes provenance that knows about the type id.
+        // Give the each pointer-sized chunk provenance that knows about the type id.
         // Here we rely on `TypeId` being a newtype around an array of pointers, so we
-        // first project to its only field and then the first array element.
+        // first project to its only field and then the array elements.
         let alloc_id = tcx.reserve_and_set_type_id_alloc(ty);
-        let first = self.project_field(dest, FieldIdx::ZERO)?;
-        let first = self.project_index(&first, 0)?;
-        let offset = self.read_scalar(&first)?.to_target_usize(&tcx)?;
-        let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset));
-        let ptr = self.global_root_pointer(ptr)?;
-        let val = Scalar::from_pointer(ptr, &tcx);
-        self.write_scalar(val, &first)
+        let arr = self.project_field(dest, FieldIdx::ZERO)?;
+        let mut elem_iter = self.project_array_fields(&arr)?;
+        while let Some((_, elem)) = elem_iter.next(self)? {
+            // Decorate this part of the hash with provenance; leave the integer part unchanged.
+            let hash_fragment = self.read_scalar(&elem)?.to_target_usize(&tcx)?;
+            let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(hash_fragment));
+            let ptr = self.global_root_pointer(ptr)?;
+            let val = Scalar::from_pointer(ptr, &tcx);
+            self.write_scalar(val, &elem)?;
+        }
+        interp_ok(())
+    }
+
+    /// Read a value of type `TypeId`, returning the type it represents.
+    pub(crate) fn read_type_id(
+        &self,
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, Ty<'tcx>> {
+        // `TypeId` is a newtype around an array of pointers. All pointers must have the same
+        // provenance, and that provenance represents the type.
+        let ptr_size = self.pointer_size().bytes_usize();
+        let arr = self.project_field(op, FieldIdx::ZERO)?;
+
+        let mut ty_and_hash = None;
+        let mut elem_iter = self.project_array_fields(&arr)?;
+        while let Some((idx, elem)) = elem_iter.next(self)? {
+            let elem = self.read_pointer(&elem)?;
+            let (elem_ty, elem_hash) = self.get_ptr_type_id(elem)?;
+            // If this is the first element, remember the type and its hash.
+            // If this is not the first element, ensure it is consistent with the previous ones.
+            let full_hash = match ty_and_hash {
+                None => {
+                    let hash = self.tcx.type_id_hash(elem_ty).as_u128();
+                    let mut hash_bytes = [0u8; 16];
+                    write_target_uint(self.data_layout().endian, &mut hash_bytes, hash).unwrap();
+                    ty_and_hash = Some((elem_ty, hash_bytes));
+                    hash_bytes
+                }
+                Some((ty, hash_bytes)) => {
+                    if ty != elem_ty {
+                        throw_ub_format!(
+                            "invalid `TypeId` value: not all bytes carry the same type id metadata"
+                        );
+                    }
+                    hash_bytes
+                }
+            };
+            // Ensure the elem_hash matches the corresponding part of the full hash.
+            let hash_frag = &full_hash[(idx as usize) * ptr_size..][..ptr_size];
+            if read_target_uint(self.data_layout().endian, hash_frag).unwrap() != elem_hash.into() {
+                throw_ub_format!(
+                    "invalid `TypeId` value: the hash does not match the type id metadata"
+                );
+            }
+        }
+
+        interp_ok(ty_and_hash.unwrap().0)
     }
 
     /// Returns `true` if emulation happened.
@@ -93,45 +144,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 self.write_type_id(tp_ty, dest)?;
             }
             sym::type_id_eq => {
-                // Both operands are `TypeId`, which is a newtype around an array of pointers.
-                // Project until we have the array elements.
-                let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?;
-                let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?;
-
-                let mut a_fields = self.project_array_fields(&a_fields)?;
-                let mut b_fields = self.project_array_fields(&b_fields)?;
-
-                let (_idx, a) = a_fields
-                    .next(self)?
-                    .expect("we know the layout of TypeId has at least 2 array elements");
-                let a = self.deref_pointer(&a)?;
-                let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
-
-                let (_idx, b) = b_fields
-                    .next(self)?
-                    .expect("we know the layout of TypeId has at least 2 array elements");
-                let b = self.deref_pointer(&b)?;
-                let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
-
-                let provenance_matches = a == b;
-
-                let mut eq_id = offset_a == offset_b;
-
-                while let Some((_, a)) = a_fields.next(self)? {
-                    let (_, b) = b_fields.next(self)?.unwrap();
-
-                    let a = self.read_target_usize(&a)?;
-                    let b = self.read_target_usize(&b)?;
-                    eq_id &= a == b;
-                }
-
-                if !eq_id && provenance_matches {
-                    throw_ub_format!(
-                        "type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents"
-                    )
-                }
-
-                self.write_scalar(Scalar::from_bool(provenance_matches), dest)?;
+                let a_ty = self.read_type_id(&args[0])?;
+                let b_ty = self.read_type_id(&args[1])?;
+                self.write_scalar(Scalar::from_bool(a_ty == b_ty), dest)?;
             }
             sym::variant_count => {
                 let tp_ty = instance.args.type_at(0);
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index d150ed69250..e981f3973ae 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -649,6 +649,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
 
     type ExtraFnVal = !;
 
+    type MemoryKind = $crate::const_eval::MemoryKind;
     type MemoryMap =
         rustc_data_structures::fx::FxIndexMap<AllocId, (MemoryKind<Self::MemoryKind>, Allocation)>;
     const GLOBAL_KIND: Option<Self::MemoryKind> = None; // no copying of globals from `tcx` to machine memory
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 0dd274b1b00..47228de5213 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -26,6 +26,7 @@ use super::{
     Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub,
     err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format,
 };
+use crate::const_eval::ConstEvalErrKind;
 use crate::fluent_generated as fluent;
 
 #[derive(Debug, PartialEq, Copy, Clone)]
@@ -311,6 +312,51 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         interp_ok(new_ptr)
     }
 
+    /// Mark the `const_allocate`d allocation `ptr` points to as immutable so we can intern it.
+    pub fn make_const_heap_ptr_global(
+        &mut self,
+        ptr: Pointer<Option<CtfeProvenance>>,
+    ) -> InterpResult<'tcx>
+    where
+        M: Machine<'tcx, MemoryKind = crate::const_eval::MemoryKind, Provenance = CtfeProvenance>,
+    {
+        let (alloc_id, offset, _) = self.ptr_get_alloc_id(ptr, 0)?;
+        if offset.bytes() != 0 {
+            return Err(ConstEvalErrKind::ConstMakeGlobalWithOffset(ptr)).into();
+        }
+
+        if matches!(self.tcx.try_get_global_alloc(alloc_id), Some(_)) {
+            // This points to something outside the current interpreter.
+            return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into();
+        }
+
+        // If we can't find it in `alloc_map` it must be dangling (because we don't use
+        // `extra_fn_ptr_map` in const-eval).
+        let (kind, alloc) = self
+            .memory
+            .alloc_map
+            .get_mut_or(alloc_id, || Err(ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(ptr)))?;
+
+        // Ensure this is actually a *heap* allocation, and record it as made-global.
+        match kind {
+            MemoryKind::Stack | MemoryKind::CallerLocation => {
+                return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into();
+            }
+            MemoryKind::Machine(crate::const_eval::MemoryKind::Heap { was_made_global }) => {
+                if *was_made_global {
+                    return Err(ConstEvalErrKind::ConstMakeGlobalPtrAlreadyMadeGlobal(alloc_id))
+                        .into();
+                }
+                *was_made_global = true;
+            }
+        }
+
+        // Prevent further mutation, this is now an immutable global.
+        alloc.mutability = Mutability::Not;
+
+        interp_ok(())
+    }
+
     #[instrument(skip(self), level = "debug")]
     pub fn deallocate_ptr(
         &mut self,
@@ -951,12 +997,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     pub fn get_ptr_type_id(
         &self,
         ptr: Pointer<Option<M::Provenance>>,
-    ) -> InterpResult<'tcx, (Ty<'tcx>, Size)> {
+    ) -> InterpResult<'tcx, (Ty<'tcx>, u64)> {
         let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?;
         let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else {
-            throw_ub_format!("type_id_eq: `TypeId` provenance is not a type id")
+            throw_ub_format!("invalid `TypeId` value: not all bytes carry type id metadata")
         };
-        interp_ok((ty, offset))
+        interp_ok((ty, offset.bytes()))
     }
 
     pub fn get_ptr_fn(
diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs
index 2fc372dd019..2f365ec77b3 100644
--- a/compiler/rustc_const_eval/src/interpret/mod.rs
+++ b/compiler/rustc_const_eval/src/interpret/mod.rs
@@ -26,7 +26,7 @@ pub use self::call::FnArg;
 pub use self::eval_context::{InterpCx, format_interp_error};
 use self::eval_context::{from_known_layout, mir_assign_valid_types};
 pub use self::intern::{
-    HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop,
+    HasStaticRootDefId, InternError, InternKind, intern_const_alloc_for_constprop,
     intern_const_alloc_recursive,
 };
 pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine};
diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs
index b6ba069526c..2e99bb4209f 100644
--- a/compiler/rustc_const_eval/src/interpret/stack.rs
+++ b/compiler/rustc_const_eval/src/interpret/stack.rs
@@ -12,6 +12,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, mir};
 use rustc_mir_dataflow::impls::always_storage_live_locals;
 use rustc_span::Span;
+use tracing::field::Empty;
 use tracing::{info_span, instrument, trace};
 
 use super::{
@@ -396,7 +397,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         // Finish things up.
         M::after_stack_push(self)?;
         self.frame_mut().loc = Left(mir::Location::START);
-        let span = info_span!("frame", "{}", instance);
+        // `tracing_separate_thread` is used to instruct the chrome_tracing [tracing::Layer] in Miri
+        // to put the "frame" span on a separate trace thread/line than other spans, to make the
+        // visualization in https://ui.perfetto.dev easier to interpret. It is set to a value of
+        // [tracing::field::Empty] so that other tracing layers (e.g. the logger) will ignore it.
+        let span = info_span!("frame", tracing_separate_thread = Empty, "{}", instance);
         self.frame_mut().tracing_span.enter(span);
 
         interp_ok(())
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 833fcc38817..629dcc3523c 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -5,7 +5,6 @@
 use either::Either;
 use rustc_abi::{FIRST_VARIANT, FieldIdx};
 use rustc_index::IndexSlice;
-use rustc_middle::ty::layout::FnAbiOf;
 use rustc_middle::ty::{self, Instance, Ty};
 use rustc_middle::{bug, mir, span_bug};
 use rustc_span::source_map::Spanned;
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index fc44490c96d..62f591ceaa9 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -558,7 +558,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                 {
                     // Everything should be already interned.
                     let Some(global_alloc) = self.ecx.tcx.try_get_global_alloc(alloc_id) else {
-                        assert!(self.ecx.memory.alloc_map.get(alloc_id).is_none());
+                        if self.ecx.memory.alloc_map.contains_key(&alloc_id) {
+                            // This can happen when interning didn't complete due to, e.g.
+                            // missing `make_global`. This must mean other errors are already
+                            // being reported.
+                            self.ecx.tcx.dcx().delayed_bug(
+                                "interning did not complete, there should be an error",
+                            );
+                            return interp_ok(());
+                        }
                         // We can't have *any* references to non-existing allocations in const-eval
                         // as the rest of rustc isn't happy with them... so we throw an error, even
                         // though for zero-sized references this isn't really UB.
diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
index 4ca39bbc68e..b1f29598750 100644
--- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
+++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
@@ -52,9 +52,9 @@ fn check_validity_requirement_strict<'tcx>(
 
     let mut cx = InterpCx::new(cx.tcx(), DUMMY_SP, cx.typing_env, machine);
 
-    let allocated = cx
-        .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
-        .expect("OOM: failed to allocate for uninit check");
+    // It doesn't really matter which `MemoryKind` we use here, `Stack` is the least wrong.
+    let allocated =
+        cx.allocate(ty, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check");
 
     if kind == ValidityRequirement::Zero {
         cx.write_bytes_ptr(
@@ -184,9 +184,10 @@ pub(crate) fn validate_scalar_in_layout<'tcx>(
     let Ok(layout) = cx.layout_of(ty) else {
         bug!("could not compute layout of {scalar:?}:{ty:?}")
     };
-    let allocated = cx
-        .allocate(layout, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
-        .expect("OOM: failed to allocate for uninit check");
+
+    // It doesn't really matter which `MemoryKind` we use here, `Stack` is the least wrong.
+    let allocated =
+        cx.allocate(layout, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check");
 
     cx.write_scalar(scalar, &allocated).unwrap();