Skip to content

Commit 21e34fa

Browse files
committed
handle Box with non-ZST allocator
1 parent 9d47a56 commit 21e34fa

File tree

2 files changed

+89
-7
lines changed

2 files changed

+89
-7
lines changed

src/stacked_borrows.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
//! Implements "Stacked Borrows". See <https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md>
22
//! for further information.
33
4-
use log::trace;
54
use std::cell::RefCell;
65
use std::fmt;
76
use std::num::NonZeroU64;
87

8+
use log::trace;
9+
910
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1011
use rustc_hir::Mutability;
1112
use rustc_middle::mir::RetagKind;
1213
use rustc_middle::ty::{
1314
self,
14-
layout::{HasParamEnv, LayoutOf},
15+
layout::{HasParamEnv, LayoutOf, TyAndLayout},
1516
};
1617
use rustc_span::DUMMY_SP;
1718
use rustc_target::abi::Size;
@@ -835,8 +836,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
835836
// Determine mutability and whether to add a protector.
836837
// Cannot use `builtin_deref` because that reports *immutable* for `Box`,
837838
// making it useless.
838-
fn qualify(ty: ty::Ty<'_>, kind: RetagKind) -> Option<(RefKind, bool)> {
839-
match ty.kind() {
839+
let qualify = |layout: TyAndLayout<'tcx>, kind: RetagKind| -> Option<(RefKind, bool)> {
840+
match layout.ty.kind() {
840841
// References are simple.
841842
ty::Ref(_, _, Mutability::Mut) =>
842843
Some((
@@ -850,15 +851,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
850851
Some((RefKind::Raw { mutable: tym.mutbl == Mutability::Mut }, false)),
851852
// Boxes do not get a protector: protectors reflect that references outlive the call
852853
// they were passed in to; that's just not the case for boxes.
853-
ty::Adt(..) if ty.is_box() => Some((RefKind::Unique { two_phase: false }, false)),
854+
// HACK: We only treat boxes with ZST allocators as 'noalias'.
855+
// See https://github.com/rust-lang/rust/issues/95453.
856+
ty::Adt(..) if layout.ty.is_box() && layout.field(this, 1).is_zst() =>
857+
Some((RefKind::Unique { two_phase: false }, false)),
854858
_ => None,
855859
}
856-
}
860+
};
857861

858862
// We only reborrow "bare" references/boxes.
859863
// Not traversing into fields helps with <https://github.com/rust-lang/unsafe-code-guidelines/issues/125>,
860864
// but might also cost us optimization and analyses. We will have to experiment more with this.
861-
if let Some((mutbl, protector)) = qualify(place.layout.ty, kind) {
865+
if let Some((mutbl, protector)) = qualify(place.layout, kind) {
862866
// Fast path.
863867
let val = this.read_immediate(&this.place_to_op(place)?)?;
864868
let val = this.retag_reference(&val, mutbl, protector)?;

tests/run-pass/issue-95453.rs

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#![feature(allocator_api)]
2+
3+
use std::alloc::{AllocError, Allocator};
4+
use std::alloc::Layout;
5+
use std::cell::Cell;
6+
use std::mem::MaybeUninit;
7+
use std::ptr::{self, NonNull};
8+
9+
struct OnceAlloc<'a> {
10+
space: Cell<&'a mut [MaybeUninit<u8>]>,
11+
}
12+
13+
unsafe impl<'shared, 'a: 'shared> Allocator for &'shared OnceAlloc<'a> {
14+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
15+
let space = self.space.replace(&mut []);
16+
17+
let (ptr, len) = (space.as_mut_ptr(), space.len());
18+
19+
if ptr.align_offset(layout.align()) != 0 || len < layout.size() {
20+
return Err(AllocError);
21+
}
22+
23+
let slice_ptr = ptr::slice_from_raw_parts_mut(ptr as *mut u8, len);
24+
unsafe { Ok(NonNull::new_unchecked(slice_ptr)) }
25+
}
26+
27+
unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {}
28+
}
29+
30+
trait MyTrait {
31+
fn hello(&self) -> u8;
32+
}
33+
34+
impl MyTrait for [u8; 1] {
35+
fn hello(&self) -> u8 {
36+
self[0]
37+
}
38+
}
39+
40+
/// `Box<T, G>` is a `ScalarPair` where the 2nd component is the allocator.
41+
fn test1() {
42+
let mut space = vec![MaybeUninit::new(0); 1];
43+
let once_alloc = OnceAlloc {
44+
space: Cell::new(&mut space[..]),
45+
};
46+
47+
let boxed = Box::new_in([42u8; 1], &once_alloc);
48+
let _val = *boxed;
49+
}
50+
51+
// Make the allocator itself so big that the Box is not even a ScalarPair any more.
52+
struct OnceAllocRef<'s, 'a>(&'s OnceAlloc<'a>, u64);
53+
54+
unsafe impl<'shared, 'a: 'shared> Allocator for OnceAllocRef<'shared, 'a> {
55+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
56+
self.0.allocate(layout)
57+
}
58+
59+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
60+
self.0.deallocate(ptr, layout)
61+
}
62+
}
63+
64+
/// `Box<T, G>` is an `Aggregate`.
65+
fn test2() {
66+
let mut space = vec![MaybeUninit::new(0); 1];
67+
let once_alloc = OnceAlloc {
68+
space: Cell::new(&mut space[..]),
69+
};
70+
71+
let boxed = Box::new_in([0u8; 1], OnceAllocRef(&once_alloc, 0));
72+
let _val = *boxed;
73+
}
74+
75+
fn main() {
76+
test1();
77+
test2();
78+
}

0 commit comments

Comments
 (0)