Skip to content

Commit ff3bbd2

Browse files
committed
handle Box with allocators
1 parent 984b46c commit ff3bbd2

File tree

2 files changed

+114
-9
lines changed

2 files changed

+114
-9
lines changed

src/stacked_borrows.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -976,27 +976,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
976976
// Raw pointers need to be enabled.
977977
ty::RawPtr(tym) if kind == RetagKind::Raw =>
978978
Some((RefKind::Raw { mutable: tym.mutbl == Mutability::Mut }, false)),
979-
// Boxes do not get a protector: protectors reflect that references outlive the call
980-
// they were passed in to; that's just not the case for boxes.
981-
ty::Adt(..) if ty.is_box() => Some((RefKind::Unique { two_phase: false }, false)),
979+
// Boxes are handled separately due to that allocator situation.
982980
_ => None,
983981
}
984982
}
985983

986984
// We need a visitor to visit all references. However, that requires
987-
// a `MPlaceTy` (or `OpTy), so we have a fast path for reference types that
985+
// a `MPlaceTy` (or `OpTy`), so we have a fast path for reference types that
988986
// avoids allocating.
989987

990-
if let Some((mutbl, protector)) = qualify(place.layout.ty, kind) {
988+
if let Some((ref_kind, protector)) = qualify(place.layout.ty, kind) {
991989
// Fast path.
992990
let val = this.read_immediate(&this.place_to_op(place)?)?;
993-
let val = this.retag_reference(&val, mutbl, protector)?;
991+
let val = this.retag_reference(&val, ref_kind, protector)?;
994992
this.write_immediate(*val, place)?;
995993
return Ok(());
996994
}
997995

998996
// If we don't want to recurse, we are already done.
999-
if !this.machine.stacked_borrows.as_mut().unwrap().get_mut().retag_fields {
997+
// EXCEPT if this is a `Box`, then we have to recurse because allocators.
998+
// (Yes this means we technically also recursively retag the allocator itself even if field
999+
// retagging is not enabled. *shrug*)
1000+
if !this.machine.stacked_borrows.as_mut().unwrap().get_mut().retag_fields
1001+
&& !place.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_box())
1002+
{
10001003
return Ok(());
10011004
}
10021005

@@ -1034,10 +1037,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
10341037
self.ecx
10351038
}
10361039

1040+
fn visit_box(&mut self, place: &MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
1041+
// Boxes do not get a protector: protectors reflect that references outlive the call
1042+
// they were passed in to; that's just not the case for boxes.
1043+
let (ref_kind, protector) = (RefKind::Unique { two_phase: false }, false);
1044+
1045+
let val = self.ecx.read_immediate(&place.into())?;
1046+
let val = self.ecx.retag_reference(&val, ref_kind, protector)?;
1047+
self.ecx.write_immediate(*val, &place.into())?;
1048+
Ok(())
1049+
}
1050+
10371051
fn visit_value(&mut self, place: &MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
1038-
if let Some((mutbl, protector)) = qualify(place.layout.ty, self.kind) {
1052+
if let Some((ref_kind, protector)) = qualify(place.layout.ty, self.kind) {
10391053
let val = self.ecx.read_immediate(&place.into())?;
1040-
let val = self.ecx.retag_reference(&val, mutbl, protector)?;
1054+
let val = self.ecx.retag_reference(&val, ref_kind, protector)?;
10411055
self.ecx.write_immediate(*val, &place.into())?;
10421056
} else if matches!(place.layout.ty.kind(), ty::RawPtr(..)) {
10431057
// Wide raw pointers *do* have fields and their types are strange.

tests/pass/box-custom-alloc.rs

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

0 commit comments

Comments
 (0)