diff --git a/tests/pass/both_borrows/smallvec.rs b/tests/pass/both_borrows/smallvec.rs new file mode 100644 index 0000000000..f48815e37b --- /dev/null +++ b/tests/pass/both_borrows/smallvec.rs @@ -0,0 +1,99 @@ +//! This test represents a core part of `SmallVec`'s `extend_impl`. +//! What makes it interesting as a test is that it relies on Stacked Borrow's "quirk" +//! in a fundamental, hard-to-fix-without-full-trees way. + +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +use std::marker::PhantomData; +use std::mem::{ManuallyDrop, MaybeUninit}; +use std::ptr::NonNull; + +#[repr(C)] +pub union RawSmallVec { + inline: ManuallyDrop>, + heap: (NonNull, usize), +} + +impl RawSmallVec { + const fn new() -> Self { + Self::new_inline(MaybeUninit::uninit()) + } + + const fn new_inline(inline: MaybeUninit<[T; N]>) -> Self { + Self { inline: ManuallyDrop::new(inline) } + } + + const fn as_mut_ptr_inline(&mut self) -> *mut T { + (unsafe { &raw mut self.inline }) as *mut T + } + + const unsafe fn as_mut_ptr_heap(&mut self) -> *mut T { + self.heap.0.as_ptr() + } +} + +#[repr(transparent)] +#[derive(Clone, Copy)] +struct TaggedLen(usize); + +impl TaggedLen { + pub const fn new(len: usize, on_heap: bool, is_zst: bool) -> Self { + if is_zst { + debug_assert!(!on_heap); + TaggedLen(len) + } else { + debug_assert!(len < isize::MAX as usize); + TaggedLen((len << 1) | on_heap as usize) + } + } + + pub const fn on_heap(self, is_zst: bool) -> bool { + if is_zst { false } else { (self.0 & 1_usize) == 1 } + } + + pub const fn value(self, is_zst: bool) -> usize { + if is_zst { self.0 } else { self.0 >> 1 } + } +} + +#[repr(C)] +pub struct SmallVec { + len: TaggedLen, + raw: RawSmallVec, + _marker: PhantomData, +} + +impl SmallVec { + pub const fn new() -> SmallVec { + Self { + len: TaggedLen::new(0, false, Self::is_zst()), + raw: RawSmallVec::new(), + _marker: PhantomData, + } + } + + const fn is_zst() -> bool { + size_of::() == 0 + } + + pub const fn as_mut_ptr(&mut self) -> *mut T { + if self.len.on_heap(Self::is_zst()) { + // SAFETY: see above + unsafe { self.raw.as_mut_ptr_heap() } + } else { + self.raw.as_mut_ptr_inline() + } + } + + pub const fn len(&self) -> usize { + self.len.value(Self::is_zst()) + } +} + +fn main() { + let mut v = SmallVec::::new(); + let ptr = v.as_mut_ptr(); + let _len = v.len(); // this call incurs a reborrow which just barely does not invalidate `ptr` + unsafe { ptr.write(0) }; +}