Skip to content

Commit 56b720e

Browse files
committed
handle Box with non-ZST allocator
1 parent 54a495c commit 56b720e

File tree

2 files changed

+89
-7
lines changed

2 files changed

+89
-7
lines changed

src/stacked_borrows.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
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::cmp;
76
use std::fmt;
87
use std::num::NonZeroU64;
98

9+
use log::trace;
10+
1011
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1112
use rustc_hir::Mutability;
1213
use rustc_middle::mir::RetagKind;
1314
use rustc_middle::ty::{
1415
self,
15-
layout::{HasParamEnv, LayoutOf},
16+
layout::{HasParamEnv, LayoutOf, TyAndLayout},
1617
};
1718
use rustc_span::DUMMY_SP;
1819
use rustc_target::abi::Size;
@@ -1086,8 +1087,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
10861087
// Determine mutability and whether to add a protector.
10871088
// Cannot use `builtin_deref` because that reports *immutable* for `Box`,
10881089
// making it useless.
1089-
fn qualify(ty: ty::Ty<'_>, kind: RetagKind) -> Option<(RefKind, bool)> {
1090-
match ty.kind() {
1090+
let qualify = |layout: TyAndLayout<'tcx>, kind: RetagKind| -> Option<(RefKind, bool)> {
1091+
match layout.ty.kind() {
10911092
// References are simple.
10921093
ty::Ref(_, _, Mutability::Mut) =>
10931094
Some((
@@ -1101,15 +1102,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11011102
Some((RefKind::Raw { mutable: tym.mutbl == Mutability::Mut }, false)),
11021103
// Boxes do not get a protector: protectors reflect that references outlive the call
11031104
// they were passed in to; that's just not the case for boxes.
1104-
ty::Adt(..) if ty.is_box() => Some((RefKind::Unique { two_phase: false }, false)),
1105+
// HACK: We only treat boxes with ZST allocators as 'noalias'.
1106+
// See https://github.com/rust-lang/rust/issues/95453.
1107+
ty::Adt(..) if layout.ty.is_box() && layout.field(this, 1).is_zst() =>
1108+
Some((RefKind::Unique { two_phase: false }, false)),
11051109
_ => None,
11061110
}
1107-
}
1111+
};
11081112

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

tests/pass/issue-95453.rs

Lines changed: 78 additions & 0 deletions
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)