|
| 1 | +//! This test represents a core part of `SmallVec`'s `extend_impl`. |
| 2 | +//! What makes it interesting as a test is that it relies on Stacked Borrow's "quirk" |
| 3 | +//! in a fundamental, hard-to-fix-without-full-trees way. |
| 4 | +
|
| 5 | +//@revisions: stack tree |
| 6 | +//@[tree]compile-flags: -Zmiri-tree-borrows |
| 7 | + |
| 8 | +use std::marker::PhantomData; |
| 9 | +use std::mem::{ManuallyDrop, MaybeUninit}; |
| 10 | +use std::ptr::NonNull; |
| 11 | + |
| 12 | +#[repr(C)] |
| 13 | +pub union RawSmallVec<T, const N: usize> { |
| 14 | + inline: ManuallyDrop<MaybeUninit<[T; N]>>, |
| 15 | + heap: (NonNull<T>, usize), |
| 16 | +} |
| 17 | + |
| 18 | +impl<T, const N: usize> RawSmallVec<T, N> { |
| 19 | + const fn new() -> Self { |
| 20 | + Self::new_inline(MaybeUninit::uninit()) |
| 21 | + } |
| 22 | + |
| 23 | + const fn new_inline(inline: MaybeUninit<[T; N]>) -> Self { |
| 24 | + Self { inline: ManuallyDrop::new(inline) } |
| 25 | + } |
| 26 | + |
| 27 | + const fn as_mut_ptr_inline(&mut self) -> *mut T { |
| 28 | + (unsafe { &raw mut self.inline }) as *mut T |
| 29 | + } |
| 30 | + |
| 31 | + const unsafe fn as_mut_ptr_heap(&mut self) -> *mut T { |
| 32 | + self.heap.0.as_ptr() |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +#[repr(transparent)] |
| 37 | +#[derive(Clone, Copy)] |
| 38 | +struct TaggedLen(usize); |
| 39 | + |
| 40 | +impl TaggedLen { |
| 41 | + pub const fn new(len: usize, on_heap: bool, is_zst: bool) -> Self { |
| 42 | + if is_zst { |
| 43 | + debug_assert!(!on_heap); |
| 44 | + TaggedLen(len) |
| 45 | + } else { |
| 46 | + debug_assert!(len < isize::MAX as usize); |
| 47 | + TaggedLen((len << 1) | on_heap as usize) |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + pub const fn on_heap(self, is_zst: bool) -> bool { |
| 52 | + if is_zst { false } else { (self.0 & 1_usize) == 1 } |
| 53 | + } |
| 54 | + |
| 55 | + pub const fn value(self, is_zst: bool) -> usize { |
| 56 | + if is_zst { self.0 } else { self.0 >> 1 } |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +#[repr(C)] |
| 61 | +pub struct SmallVec<T, const N: usize> { |
| 62 | + len: TaggedLen, |
| 63 | + raw: RawSmallVec<T, N>, |
| 64 | + _marker: PhantomData<T>, |
| 65 | +} |
| 66 | + |
| 67 | +impl<T, const N: usize> SmallVec<T, N> { |
| 68 | + pub const fn new() -> SmallVec<T, N> { |
| 69 | + Self { |
| 70 | + len: TaggedLen::new(0, false, Self::is_zst()), |
| 71 | + raw: RawSmallVec::new(), |
| 72 | + _marker: PhantomData, |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + const fn is_zst() -> bool { |
| 77 | + size_of::<T>() == 0 |
| 78 | + } |
| 79 | + |
| 80 | + pub const fn as_mut_ptr(&mut self) -> *mut T { |
| 81 | + if self.len.on_heap(Self::is_zst()) { |
| 82 | + // SAFETY: see above |
| 83 | + unsafe { self.raw.as_mut_ptr_heap() } |
| 84 | + } else { |
| 85 | + self.raw.as_mut_ptr_inline() |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + pub const fn len(&self) -> usize { |
| 90 | + self.len.value(Self::is_zst()) |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +fn main() { |
| 95 | + let mut v = SmallVec::<i32, 4>::new(); |
| 96 | + let ptr = v.as_mut_ptr(); |
| 97 | + let _len = v.len(); // this call incurs a reborrow which just barely does not invalidate `ptr` |
| 98 | + unsafe { ptr.write(0) }; |
| 99 | +} |
0 commit comments