pinned_aliasable/
lib.rs

1//! `Pin`-based stopgap for unboxed aliasable values in self-referential data structures.
2//!
3//! # Uniqueness
4//!
5//! For the sake of optimization, the Rust compiler likes to assume that all mutable references
6//! (`&mut`) are completely unique. This uniqueness gives it some extremely important guarantees
7//! that can be easily exploited for faster code, such as:
8//! - All reads from an `&mut` location are guaranteed to be the same if the reference is not
9//! written to in between.
10//! - Writes to the location are guaranteed to stay there unless explicitly overwritten with the
11//! same mutable reference.
12//! - No one is able to see the data stored behind the mutable reference while it exists without
13//! using that mutable reference.
14//!
15//! A simple example of where `&mut` uniqueness is useful is in this code:
16//!
17//! ```rust
18//! fn foo(x: &mut i32) -> i32 {
19//!     *x = 400;
20//!     do_some_other_stuff();
21//!     *x
22//! }
23//! # fn do_some_other_stuff() {}
24//! ```
25//!
26//! The compiler will optimize this function to always return the constant 400, instead of having
27//! to actually load the value stored behind `x` every time. It was only able to do this because `x`
28//! is a unique pointer; if it wasn't, it could be possible that `do_some_other_stuff` would mutate
29//! it in between and always returning the constant would result in unexpected behaviour.
30//!
31//! # Self-referential types
32//!
33//! However, this assumption starts to run into problems when using self-referential types. What
34//! if, instead of being a simple integer, `x` was a type that held a reference to itself? Although
35//! it isn't immediately obvious, the uniqueness guarantee is actually violated here: the
36//! self-reference held in `x` aliases with the `&mut` to `x`, meaning the mutable reference _is no
37//! longer unique_! And this issue isn't just theoretical, it causes miscompilations in the wild.
38//! For example this code, which was based off [an actual soundness issue in
39//! the `owning-ref` crate](https://github.com/Kimundi/owning-ref-rs/issues/49):
40//!
41//! ```no_run
42//! use std::cell::Cell;
43//!
44//! struct Helper {
45//!     reference: &'static Cell<u8>,
46//!     owner: Box<Cell<u8>>,
47//! }
48//! fn helper(x: Helper) -> u8 {
49//!     x.owner.set(10);
50//!     x.reference.set(20);
51//!     x.owner.get()
52//! }
53//!
54//! let owner = Box::new(Cell::new(0));
55//! let reference = unsafe { &*(&*owner as *const Cell<u8>) };
56//! let x = Helper { reference, owner };
57//! println!("{}", helper(x));
58//! ```
59//!
60//! When run in release mode, this program prints out `10` instead of the expected value of `20`.
61//! This is because inside `helper`, the optimizer sees that we have unique access to the
62//! `Cell<u8>` (`Box`es, like `&mut`s, are seen as unique pointers), and so it assumes that any
63//! writes to that location will never be overwritten. But because we violated the optimizer's
64//! expectations, we ended up with a nonsensical result.
65//!
66//! So what's the solution to this? Well, as it stands, there isn't one - at least not one that's
67//! both sound and doesn't sacrifice performance. It is possible to use a different kind of smart
68//! pointer than `Box`, one that doesn't allow the compiler to assume its pointer is unique, and
69//! that _would_ work for the above case with almost no performance impact - but in cases where the
70//! self-referenced value is not boxed in the first place it's a much tougher choice to make.
71//!
72//! It is very likely Rust eventually will have a solution to this, it's a well known bug that
73//! needs to be fixed. In terms of what this solution will look like, it will most likely take the
74//! shape of a `Aliasable<T>` wrapper type that exists in libcore and gives the guarantee that any
75//! `&mut` references to the value will _not_ be considered to be unique, so that one
76//! `&mut Aliasable<T>` and either one `&mut T` or any number of `&T`s can coexist (but not two
77//! `&mut T`s or two `&mut Aliasable<T>`s; the regular borrowing rules still apply). Unfortunately,
78//! this type doesn't exist today and there aren't even any concrete proposals for it yet. So what
79//! can we do in the meantime?
80//!
81//! # A Solution
82//!
83//! Although it isn't possible to create sound self-referential types, as it turns out it _is_
84//! possible to create unsound self-referential types _that we know won't miscompile_. This is
85//! because to ensure that async blocks (which generate self-referential types) do not miscompile
86//! in today's Rust, a temporary loophole was added to the `&mut` uniqueness rule: it only applies
87//! when the referenced type doesn't implement `Unpin`. Thus to create these self-referential types
88//! we simply have to make sure that they are `!Unpin`, and everything will work as expected.
89//!
90//! However, doing this manually and upholding all the invariants that come with it is a pain, not
91//! to mention the migration effort that will be required in future once Rust does support true
92//! self-referential types. So that's where this crate comes in. It provides a type `Aliasable<T>`
93//! which both abstracts the work of making the container type `!Unpin` and should be forward
94//! compatible with the hypothetical libcore equivalent. As soon as `Aliasable<T>` _does_ get
95//! added to the language itself, I will be able to publish a new version of this crate internally
96//! based on it and yank all previous versions, which would then be unsound and obsolete.
97//!
98//! And that's it! Although this crate is tiny, it is really useful for defining any kind of
99//! self-referential type because you no longer have to worry so much about whether you can cause
100//! miscompilations.
101//!
102//! However, there is one important detail to be aware of. Remember how above I said that `Box`es
103//! are also treated as always-unique pointers? This is true, and unfortunately they don't get the
104//! same loophole that `&mut` does. This means you have to be very careful when working with boxed
105//! `Aliasable<T>`s - make sure that any functions that take them by value always delegate to a
106//! second function that takes them by unique or shared reference, so Rust doesn't assume your
107//! pointer to it is unique.
108//!
109//! # Examples
110//!
111//! A boxed slice that also stores a subslice of itself:
112//!
113//! ```rust
114//! use core::pin::Pin;
115//! use core::ptr::NonNull;
116//! use core::slice::SliceIndex;
117//! use core::cell::UnsafeCell;
118//!
119//! use pin_project::pin_project;
120//! use pin_utils::pin_mut;
121//! use pinned_aliasable::Aliasable;
122//!
123//! #[pin_project]
124//! pub struct OwningSlice<T: 'static> {
125//!     // In a real implementation you would avoid the `T: 'static` bound by using some kind of
126//!     // raw pointer here.
127//!     slice: Option<&'static mut [T]>,
128//!     #[pin]
129//!     data: Aliasable<UnsafeCell<Box<[T]>>>,
130//! }
131//! impl<T: 'static> From<Box<[T]>> for OwningSlice<T> {
132//!     fn from(data: Box<[T]>) -> Self {
133//!         Self {
134//!             slice: None,
135//!             data: Aliasable::new(UnsafeCell::new(data)),
136//!         }
137//!     }
138//! }
139//! impl<T> OwningSlice<T> {
140//!     pub fn slice(self: Pin<&mut Self>, range: impl SliceIndex<[T], Output = [T]>) {
141//!         let mut this = self.project();
142//!         let current_slice = this.slice.take().unwrap_or_else(|| {
143//!             unsafe { &mut **this.data.as_ref().get_extended().get() }
144//!         });
145//!         *this.slice = Some(&mut current_slice[range]);
146//!     }
147//!     pub fn get(self: Pin<&Self>) -> &[T] {
148//!         let this = self.project_ref();
149//!         this.slice.as_deref().unwrap_or_else(|| unsafe { &**this.data.get().get() })
150//!     }
151//!     pub fn get_mut(self: Pin<&mut Self>) -> &mut [T] {
152//!         let this = self.project();
153//!         let data = this.data.as_ref();
154//!         this.slice.as_deref_mut().unwrap_or_else(|| unsafe { &mut **data.get().get() })
155//!     }
156//! }
157//!
158//! let slice = OwningSlice::from(vec![1, 2, 3, 4, 5].into_boxed_slice());
159//! pin_mut!(slice);
160//! assert_eq!(slice.as_ref().get(), &[1, 2, 3, 4, 5]);
161//!
162//! slice.as_mut().slice(1..);
163//! assert_eq!(slice.as_ref().get(), &[2, 3, 4, 5]);
164//!
165//! slice.as_mut().slice(2..=3);
166//! assert_eq!(slice.as_ref().get(), &[4, 5]);
167//!
168//! slice.as_mut().slice(0..0);
169//! assert_eq!(slice.as_ref().get(), &[]);
170//! ```
171//!
172//! A pair type:
173//!
174//! ```rust
175//! use core::pin::Pin;
176//! use core::cell::Cell;
177//!
178//! use pin_project::{pin_project, pinned_drop};
179//! use pin_utils::pin_mut;
180//! use pinned_aliasable::Aliasable;
181//!
182//! #[pin_project(PinnedDrop)]
183//! pub struct Pair(#[pin] Aliasable<PairInner>);
184//!
185//! struct PairInner {
186//!     value: u64,
187//!     other: Cell<Option<&'static PairInner>>,
188//! }
189//!
190//! #[pinned_drop]
191//! impl PinnedDrop for Pair {
192//!     fn drop(self: Pin<&mut Self>) {
193//!         if let Some(other) = self.project().0.as_ref().get().other.get() {
194//!             other.other.set(None);
195//!         }
196//!     }
197//! }
198//!
199//! impl Pair {
200//!     pub fn new(value: u64) -> Self {
201//!         Self(Aliasable::new(PairInner {
202//!             value,
203//!             other: Cell::new(None),
204//!         }))
205//!     }
206//!     pub fn get(self: Pin<&Self>) -> u64 {
207//!         self.project_ref().0.get().other.get().unwrap().value
208//!     }
209//! }
210//!
211//! pub fn link_up(left: Pin<&Pair>, right: Pin<&Pair>) {
212//!     let left = unsafe { left.project_ref().0.get_extended() };
213//!     let right = unsafe { right.project_ref().0.get_extended() };
214//!     left.other.set(Some(right));
215//!     right.other.set(Some(left));
216//! }
217//!
218//! fn main() {
219//!     let pair_1 = Pair::new(10);
220//!     let pair_2 = Pair::new(20);
221//!     pin_mut!(pair_1);
222//!     pin_mut!(pair_2);
223//!
224//!     link_up(pair_1.as_ref(), pair_2.as_ref());
225//!
226//!     assert_eq!(pair_1.as_ref().get(), 20);
227//!     assert_eq!(pair_2.as_ref().get(), 10);
228//! }
229//! ```
230#![no_std]
231#![warn(
232    clippy::pedantic,
233    rust_2018_idioms,
234    missing_docs,
235    unused_qualifications,
236    missing_debug_implementations,
237    explicit_outlives_requirements,
238    unused_lifetimes,
239    unsafe_op_in_unsafe_fn
240)]
241#![allow(clippy::items_after_statements)]
242
243use core::fmt::{self, Debug, Formatter};
244use core::marker::PhantomPinned;
245use core::pin::Pin;
246
247/// An unboxed aliasable value.
248#[derive(Default)]
249pub struct Aliasable<T> {
250    val: T,
251    _pinned: PhantomPinned,
252}
253
254impl<T> Aliasable<T> {
255    /// Create a new `Aliasable` that stores `val`.
256    #[must_use]
257    #[inline]
258    pub fn new(val: T) -> Self {
259        Self {
260            val,
261            _pinned: PhantomPinned,
262        }
263    }
264
265    /// Get a shared reference to the value inside the `Aliasable`.
266    ///
267    /// This method takes [`Pin`]`<&Self>` instead of `&self` to enforce that all parent containers
268    /// are `!`[`Unpin`], and thus won't be annotated with `noalias`.
269    ///
270    /// This crate intentionally does not provide a method to get an `&mut T`, because the value
271    /// may be shared. To obtain an `&mut T` you should use an interior mutable container such as a
272    /// mutex or [`UnsafeCell`](core::cell::UnsafeCell).
273    #[must_use]
274    #[inline]
275    pub fn get(self: Pin<&Self>) -> &T {
276        &self.get_ref().val
277    }
278
279    /// Get a shared reference to the value inside the `Aliasable` with an extended lifetime.
280    ///
281    /// # Safety
282    ///
283    /// The reference must not be held for longer than the `Aliasable` exists.
284    #[must_use]
285    #[inline]
286    pub unsafe fn get_extended<'a>(self: Pin<&Self>) -> &'a T {
287        unsafe { &*(self.get() as *const T) }
288    }
289
290    /// Consume the `Aliasable`, returning its inner value.
291    ///
292    /// If [`get`] has already been called and the type is now pinned, obtaining the owned
293    /// `Aliasable<T>` required to call this function requires breaking the pinning guarantee (as
294    /// the `Aliasable<T>` is moved). However, this is sound as long as the `Aliasable<T>` isn't
295    /// actually aliased at that point in time.
296    ///
297    /// [`get`]: Self::get
298    #[must_use]
299    pub fn into_inner(self) -> T {
300        self.val
301    }
302}
303
304impl<T> Debug for Aliasable<T> {
305    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
306        f.pad("Aliasable")
307    }
308}
309
310#[cfg(test)]
311mod tests {
312    extern crate alloc;
313
314    use alloc::boxed::Box;
315    use core::cell::{Cell, UnsafeCell};
316    use core::ops::DerefMut;
317    use core::pin::Pin;
318
319    use pin_project::pin_project;
320
321    use super::Aliasable;
322
323    #[test]
324    fn miri_is_happy() {
325        #[pin_project]
326        struct SelfRef {
327            #[pin]
328            value: Aliasable<UnsafeCell<i32>>,
329            reference: Option<&'static mut i32>,
330        }
331
332        let self_ref = SelfRef {
333            value: Aliasable::new(UnsafeCell::new(1)),
334            reference: None,
335        };
336        pin_utils::pin_mut!(self_ref);
337        let projected = self_ref.as_mut().project();
338        *projected.reference = Some(unsafe { &mut *projected.value.as_ref().get_extended().get() });
339
340        fn helper(self_ref: Pin<&mut SelfRef>) {
341            let projected = self_ref.project();
342            {
343                let reference = projected.reference.take().unwrap();
344                *reference = 2;
345            }
346            assert_eq!(unsafe { *projected.value.as_ref().get().get() }, 2);
347        }
348
349        helper(self_ref);
350    }
351
352    #[test]
353    fn self_ref() {
354        #[pin_project]
355        struct SelfRef {
356            reference: Option<&'static Cell<i32>>,
357            #[pin]
358            value: Aliasable<Cell<i32>>,
359        }
360
361        let mut self_ref = Box::pin(SelfRef {
362            value: Aliasable::new(Cell::new(0)),
363            reference: None,
364        });
365        let projected = self_ref.as_mut().project();
366        *projected.reference = Some(unsafe { projected.value.as_ref().get_extended() });
367
368        #[inline(never)]
369        fn helper(mut self_ref: Pin<impl DerefMut<Target = SelfRef>>) -> i32 {
370            let projected = self_ref.as_mut().project();
371            projected.value.as_ref().get().set(10);
372            projected.reference.unwrap().set(20);
373            projected.value.as_ref().get().get()
374        }
375
376        assert_eq!(helper(self_ref.as_mut()), 20);
377        assert_eq!(helper(self_ref), 20);
378    }
379
380    #[test]
381    fn external_ref() {
382        let mut value = Box::pin(Aliasable::new(Cell::new(0)));
383        let reference = unsafe { value.as_ref().get_extended() };
384
385        #[inline(never)]
386        #[allow(clippy::needless_pass_by_value)]
387        fn helper(
388            value: Pin<impl DerefMut<Target = Aliasable<Cell<i32>>>>,
389            reference: &Cell<i32>,
390        ) -> i32 {
391            value.as_ref().get().set(10);
392            reference.set(20);
393            value.as_ref().get().get()
394        }
395
396        assert_eq!(helper(value.as_mut(), reference), 20);
397        // This currently miscompiles in release mode because `Box`es are never given `noalias`.
398        // See the last paragraph of the crate documentation.
399        //assert_eq!(helper(value, reference), 20);
400    }
401}