Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests for Box with non-ZST allocator #2072

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions src/stacked_borrows.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
//! 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::cmp;
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 @@ -1086,8 +1087,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 @@ -1101,15 +1102,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
91 changes: 91 additions & 0 deletions tests/pass/issue-95453.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#![allow(incomplete_features)] // for triat upcasting
#![feature(allocator_api, trait_upcasting)]

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]
}
}

trait TheTrait: MyTrait {}

impl TheTrait for [u8; 1] {}

/// `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;
let with_dyn: Box<dyn TheTrait, &OnceAlloc> = boxed;
assert_eq!(42, with_dyn.hello());
let with_dyn: Box<dyn MyTrait, &OnceAlloc> = with_dyn; // upcast
assert_eq!(42, with_dyn.hello());
}

// 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;
let with_dyn: Box<dyn TheTrait, OnceAllocRef> = boxed;
assert_eq!(42, with_dyn.hello());
let with_dyn: Box<dyn MyTrait, OnceAllocRef> = with_dyn; // upcast
assert_eq!(42, with_dyn.hello());
}

fn main() {
test1();
test2();
}