diff --git a/src/stacked_borrows.rs b/src/stacked_borrows.rs index a365d90998..3373333285 100644 --- a/src/stacked_borrows.rs +++ b/src/stacked_borrows.rs @@ -1,17 +1,18 @@ //! Implements "Stacked Borrows". See //! for further information. -use log::trace; use std::cell::RefCell; use std::fmt; use std::num::NonZeroU64; +use log::trace; + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::Mutability; use rustc_middle::mir::RetagKind; use rustc_middle::ty::{ self, - layout::{HasParamEnv, LayoutOf}, + layout::{HasParamEnv, LayoutOf, TyAndLayout}, }; use rustc_span::DUMMY_SP; use rustc_target::abi::Size; @@ -835,8 +836,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Determine mutability and whether to add a protector. // Cannot use `builtin_deref` because that reports *immutable* for `Box`, // making it useless. - fn qualify(ty: ty::Ty<'_>, kind: RetagKind) -> Option<(RefKind, bool)> { - match ty.kind() { + let qualify = |layout: TyAndLayout<'tcx>, kind: RetagKind| -> Option<(RefKind, bool)> { + match layout.ty.kind() { // References are simple. ty::Ref(_, _, Mutability::Mut) => Some(( @@ -850,15 +851,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Some((RefKind::Raw { mutable: tym.mutbl == Mutability::Mut }, false)), // Boxes do not get a protector: protectors reflect that references outlive the call // they were passed in to; that's just not the case for boxes. - ty::Adt(..) if ty.is_box() => Some((RefKind::Unique { two_phase: false }, false)), + // HACK: We only treat boxes with ZST allocators as 'noalias'. + // See https://github.com/rust-lang/rust/issues/95453. + ty::Adt(..) if layout.ty.is_box() && layout.field(this, 1).is_zst() => + Some((RefKind::Unique { two_phase: false }, false)), _ => None, } - } + }; // We only reborrow "bare" references/boxes. // Not traversing into fields helps with , // but might also cost us optimization and analyses. We will have to experiment more with this. - if let Some((mutbl, protector)) = qualify(place.layout.ty, kind) { + if let Some((mutbl, protector)) = qualify(place.layout, kind) { // Fast path. let val = this.read_immediate(&this.place_to_op(place)?)?; let val = this.retag_reference(&val, mutbl, protector)?; diff --git a/tests/run-pass/issue-95453.rs b/tests/run-pass/issue-95453.rs new file mode 100644 index 0000000000..c3fd12b6bd --- /dev/null +++ b/tests/run-pass/issue-95453.rs @@ -0,0 +1,78 @@ +#![feature(allocator_api)] + +use std::alloc::{AllocError, Allocator}; +use std::alloc::Layout; +use std::cell::Cell; +use std::mem::MaybeUninit; +use std::ptr::{self, NonNull}; + +struct OnceAlloc<'a> { + space: Cell<&'a mut [MaybeUninit]>, +} + +unsafe impl<'shared, 'a: 'shared> Allocator for &'shared OnceAlloc<'a> { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + let space = self.space.replace(&mut []); + + let (ptr, len) = (space.as_mut_ptr(), space.len()); + + if ptr.align_offset(layout.align()) != 0 || len < layout.size() { + return Err(AllocError); + } + + let slice_ptr = ptr::slice_from_raw_parts_mut(ptr as *mut u8, len); + unsafe { Ok(NonNull::new_unchecked(slice_ptr)) } + } + + unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) {} +} + +trait MyTrait { + fn hello(&self) -> u8; +} + +impl MyTrait for [u8; 1] { + fn hello(&self) -> u8 { + self[0] + } +} + +/// `Box` is a `ScalarPair` where the 2nd component is the allocator. +fn test1() { + let mut space = vec![MaybeUninit::new(0); 1]; + let once_alloc = OnceAlloc { + space: Cell::new(&mut space[..]), + }; + + let boxed = Box::new_in([42u8; 1], &once_alloc); + let _val = *boxed; +} + +// Make the allocator itself so big that the Box is not even a ScalarPair any more. +struct OnceAllocRef<'s, 'a>(&'s OnceAlloc<'a>, u64); + +unsafe impl<'shared, 'a: 'shared> Allocator for OnceAllocRef<'shared, 'a> { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + self.0.allocate(layout) + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + self.0.deallocate(ptr, layout) + } +} + +/// `Box` is an `Aggregate`. +fn test2() { + let mut space = vec![MaybeUninit::new(0); 1]; + let once_alloc = OnceAlloc { + space: Cell::new(&mut space[..]), + }; + + let boxed = Box::new_in([0u8; 1], OnceAllocRef(&once_alloc, 0)); + let _val = *boxed; +} + +fn main() { + test1(); + test2(); +}