Using Miri, I found an interesting violation of my latest Stacked Borrows rules in the bytes crate:
impl Inner {
/// Pointer to the start of the inline buffer
unsafe fn inline_ptr(&self) -> *mut u8 {
(self as *const Inner as *mut Inner as *mut u8)
.offset(INLINE_DATA_OFFSET)
}
/// Return a mutable slice for the handle's view into the shared buffer
/// including potentially uninitialized bytes.
unsafe fn as_raw(&mut self) -> &mut [u8] {
debug_assert!(!self.is_static());
if self.is_inline() {
slice::from_raw_parts_mut(self.inline_ptr(), INLINE_CAP)
} else {
slice::from_raw_parts_mut(self.ptr, self.cap)
}
}
}
// Elsewhere
let mut inner: Inner = mem::uninitialized();
// Set inline mask
inner.arc = AtomicPtr::new(KIND_INLINE as *mut Shared);
inner.set_inline_len(len);
inner.as_raw()[0..len].copy_from_slice(src);
What happens here is that Inner::inline_ptr takes a shared reference. It turns that into a *mut, which then is then returned to Inner::as_raw. That one creates an &mut, and then writes into it using copy_from_slice.
IOW, this function writes into a pointer that was created from a shared reference. Miri flags this as violation of the aliasing rules. The fix is to make inline_ptr take &mut.
What makes this interesting is that my usual answer for āwhat is interior mutabilityā has been āwriting to a location where a live shared reference to the same location existsā, but that is not the case here: The &self is long dead by the time the write happens. What happens here, instead, is that creating a raw pointer from a shared reference makes the location readable but not writeable with raw pointers.
What do yāall think, who is right ā Miri or the code? I think what Miri does makes sense here, but I could also imagine tweaks to the model that allow this code. But do we want to allow it?
Cc @carllerche