diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 1fda60c021e..808e7db5ae3 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -168,10 +168,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory mplace: &MPlaceTy<'tcx>, fields: impl Iterator>, ) -> InterpResult<'tcx> { - // ZSTs cannot contain pointers, so we can skip them. - if mplace.layout.is_zst() { + // We want to walk the aggregate to look for reference types to intern. While doing that we + // also need to take special care of interior mutability. + // + // As an optimization, however, if the allocation does not contain any pointers: we don't + // need to do the walk. It can be costly for big arrays for example (e.g. issue #93215). + + let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else { + // We could be dealing with an extern type here in the future, so we do the regular + // walk. + return self.walk_aggregate(mplace, fields); + }; + + let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? else { + // ZSTs cannot contain pointers, so we can skip them. return Ok(()); - } + }; if let Some(def) = mplace.layout.ty.ty_adt_def() { if Some(def.did()) == self.ecx.tcx.lang_items().unsafe_cell_type() { @@ -186,6 +198,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory } } + if !alloc.has_relocations() { + // There are no refs or relocations in this allocation, we can skip the interning walk. + return Ok(()); + } + self.walk_aggregate(mplace, fields) } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index d5e68dbd5b7..fb82406ce82 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -942,6 +942,11 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> { .check_bytes(&self.tcx, self.range.subrange(range), allow_uninit, allow_ptr) .map_err(|e| e.to_interp_error(self.alloc_id))?) } + + /// Returns whether the allocation has relocations for the entire range of the `AllocRef`. + pub(crate) fn has_relocations(&self) -> bool { + !self.alloc.get_relocations(&self.tcx, self.range).is_empty() + } } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {