Skip to content

Commit

Permalink
handle Box with non-ZST allocator
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Apr 19, 2022
1 parent 9d47a56 commit 21e34fa
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 7 deletions.
18 changes: 11 additions & 7 deletions src/stacked_borrows.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
//! Implements "Stacked Borrows". See <https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md>
//! 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;
Expand Down Expand Up @@ -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((
Expand All @@ -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 <https://github.com/rust-lang/unsafe-code-guidelines/issues/125>,
// 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)?;
Expand Down
78 changes: 78 additions & 0 deletions tests/run-pass/issue-95453.rs
Original file line number Diff line number Diff line change
@@ -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<u8>]>,
}

unsafe impl<'shared, 'a: 'shared> Allocator for &'shared OnceAlloc<'a> {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, 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<u8>, _layout: Layout) {}
}

trait MyTrait {
fn hello(&self) -> u8;
}

impl MyTrait for [u8; 1] {
fn hello(&self) -> u8 {
self[0]
}
}

/// `Box<T, G>` 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<NonNull<[u8]>, AllocError> {
self.0.allocate(layout)
}

unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
self.0.deallocate(ptr, layout)
}
}

/// `Box<T, G>` 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();
}

0 comments on commit 21e34fa

Please sign in to comment.