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}