moveit/
slot.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Explicit stack slots, which can be used for stack emplacement.
16//!
17//! A [`Slot`] is uninitialized storage on the stack that can be manipulated
18//! explicitly. Notionally, a [`Slot<T>`] represents a `let x: T;` in some
19//! function's stack.
20//!
21//! [`Slot`]s mut be created with the [`slot!()`] macro:
22//! ```
23//! # use moveit::{slot};
24//! slot!(storage);
25//! let mut x = storage.put(42);
26//! *x /= 2;
27//! assert_eq!(*x, 21);
28//! ```
29//! Unfortunately, due to the constrains of Rust today, it is not possible to
30//! produce a [`Slot`] as part of a larger expression; since it needs to expand
31//! to a `let` to bind the stack location, [`slot!()`] must be a statement, not
32//! an expression.
33//!
34//! [`Slot`]s can also be used to implement a sort of "guaranteed RVO":
35//! ```
36//! # use moveit::{slot, Slot, move_ref::MoveRef};
37//! fn returns_on_the_stack(val: i32, storage: Slot<i32>) -> Option<MoveRef<i32>> {
38//!   if val == 0 {
39//!     return None
40//!   }
41//!   Some(storage.put(val))
42//! }
43//!
44//! slot!(storage);
45//! let val = returns_on_the_stack(42, storage);
46//! assert_eq!(*val.unwrap(), 42);
47//! ```
48//!
49//! [`Slot`]s provide a natural location for emplacing values on the stack.
50//! The [`moveit!()`] macro is intended to make this operation
51//! straight-forward.
52//!
53//! # [`DroppingSlot`]
54//!
55//! [`DroppingSlot`] is a support type similar to [`Slot`] that is used for
56//! implementing [`DerefMove`], but which users should otherwise not construct
57//! themselves (despite it being otherwise perfectly safe to do so).
58
59use core::mem;
60use core::mem::MaybeUninit;
61use core::pin::Pin;
62use core::ptr;
63
64use crate::drop_flag::DropFlag;
65use crate::move_ref::MoveRef;
66use crate::new;
67use crate::new::New;
68use crate::new::TryNew;
69
70#[cfg(doc)]
71use {
72  crate::{move_ref::DerefMove, moveit, slot},
73  alloc::boxed::Box,
74};
75
76/// An empty slot on the stack into which a value could be emplaced.
77///
78/// The `'frame` lifetime refers to the lifetime of the stack frame this
79/// `Slot`'s storage is allocated on.
80///
81/// See [`slot!()`] and [the module documentation][self].
82pub struct Slot<'frame, T> {
83  ptr: &'frame mut MaybeUninit<T>,
84  drop_flag: DropFlag<'frame>,
85}
86
87impl<'frame, T> Slot<'frame, T> {
88  /// Creates a new `Slot` with the given pointer as its basis.
89  ///
90  /// To safely construct a `Slot`, use [`slot!()`].
91  ///
92  /// # Safety
93  ///
94  /// `ptr` must not be outlived by any other pointers to its allocation.
95  ///
96  /// `drop_flag`'s value must be dead, and must be a drop flag governing
97  /// the destruction of `ptr`'s storage in an appropriate manner as described
98  /// in [`moveit::drop_flag`][crate::drop_flag].
99  pub unsafe fn new_unchecked(
100    ptr: &'frame mut MaybeUninit<T>,
101    drop_flag: DropFlag<'frame>,
102  ) -> Self {
103    Self { ptr, drop_flag }
104  }
105
106  /// Put `val` into this slot, returning a new [`MoveRef`].
107  pub fn put(self, val: T) -> MoveRef<'frame, T> {
108    unsafe {
109      // SAFETY: Pinning is conserved by this operation.
110      Pin::into_inner_unchecked(self.pin(val))
111    }
112  }
113
114  /// Pin `val` into this slot, returning a new, pinned [`MoveRef`].
115  pub fn pin(self, val: T) -> Pin<MoveRef<'frame, T>> {
116    self.emplace(new::of(val))
117  }
118
119  /// Emplace `new` into this slot, returning a new, pinned [`MoveRef`].
120  pub fn emplace<N: New<Output = T>>(self, new: N) -> Pin<MoveRef<'frame, T>> {
121    match self.try_emplace(new) {
122      Ok(x) => x,
123      Err(e) => match e {},
124    }
125  }
126
127  /// Try to emplace `new` into this slot, returning a new, pinned [`MoveRef`].
128  pub fn try_emplace<N: TryNew<Output = T>>(
129    self,
130    new: N,
131  ) -> Result<Pin<MoveRef<'frame, T>>, N::Error> {
132    unsafe {
133      self.drop_flag.inc();
134      new.try_new(Pin::new_unchecked(self.ptr))?;
135      Ok(MoveRef::into_pin(MoveRef::new_unchecked(
136        self.ptr.assume_init_mut(),
137        self.drop_flag,
138      )))
139    }
140  }
141
142  /// Converts this into a slot for a pinned `T`.
143  ///
144  /// This is safe, since this `Slot` owns the referenced data, and
145  /// `Pin` is explicitly a `repr(transparent)` type.
146  pub fn into_pinned(self) -> Slot<'frame, Pin<T>> {
147    unsafe { self.cast() }
148  }
149
150  /// Converts this `Slot` from being a slot for a `T` to being a slot for
151  /// some other type `U`.
152  ///
153  /// ```
154  /// # use moveit::{Slot, MoveRef};
155  /// moveit::slot!(place: u32);
156  /// let foo: MoveRef<u16> = unsafe { place.cast::<u16>() }.put(42);
157  /// ```
158  ///
159  /// # Safety
160  ///
161  /// `T` must have at least the size and alignment as `U`.
162  pub unsafe fn cast<U>(self) -> Slot<'frame, U> {
163    debug_assert!(mem::size_of::<T>() >= mem::size_of::<U>());
164    debug_assert!(mem::align_of::<T>() >= mem::align_of::<U>());
165    Slot {
166      ptr: &mut *self.ptr.as_mut_ptr().cast(),
167      drop_flag: self.drop_flag,
168    }
169  }
170}
171
172impl<'frame, T> Slot<'frame, Pin<T>> {
173  /// Converts this into a slot for an unpinned `T`.
174  ///
175  /// This is safe, since this `Slot` owns the referenced data, and
176  /// `Pin` is explicitly a `repr(transparent)` type.
177  ///
178  /// Moreover, no actual unpinning is occurring: the referenced data must
179  /// be uninitialized, so it cannot have a pinned referent.
180  pub fn into_unpinned(self) -> Slot<'frame, T> {
181    unsafe { self.cast() }
182  }
183}
184
185/// Similar to a [`Slot`], but able to drop its contents.
186///
187/// A `DroppingSlot` wraps a [`Slot`], and will drop its contents if the
188/// [`Slot`]'s drop flag is dead at the time of the `DroppingSlot`'s
189/// destruction.
190///
191/// This type has an API similar to [`Slot`]'s, but rather than returning
192/// `MoveRef`s, which would own the contents of this slot, we return a `&mut T`
193/// and a [`DropFlag`], which the caller can assemble into an
194/// appropriately-shaped `MoveRef`. The drop flag will be one decrement away
195/// from being dead; callers should make sure to decremement it to trigger
196/// destruction.
197///
198/// `DroppingSlot` is intended to be used with [`DerefMove::deref_move()`],
199/// and will usually not be created by `moveit`'s users. However, [`slot!()`]
200/// provides `DroppingSlot` support, too. These slots will silently forget their
201/// contents if the drop flag is left untouched, rather than crash.
202pub struct DroppingSlot<'frame, T> {
203  ptr: &'frame mut MaybeUninit<T>,
204  drop_flag: DropFlag<'frame>,
205}
206
207impl<'frame, T> DroppingSlot<'frame, T> {
208  /// Creates a new `DroppingSlot` with the given pointer as its basis.
209  ///
210  /// To safely construct a `DroppingSlot`, use [`slot!()`].
211  ///
212  /// # Safety
213  ///
214  /// `ptr` must not be outlived by any other pointers to its allocation.
215  ///
216  /// `drop_flag`'s value must be dead, and must be a drop flag governing
217  /// the destruction of `ptr`'s storage in an appropriate manner as described
218  /// in [`moveit::drop_flag`][crate::drop_flag].
219  pub unsafe fn new_unchecked(
220    ptr: &'frame mut MaybeUninit<T>,
221    drop_flag: DropFlag<'frame>,
222  ) -> Self {
223    drop_flag.inc();
224    Self { ptr, drop_flag }
225  }
226
227  /// Put `val` into this slot, returning a reference to it.
228  pub fn put(self, val: T) -> (&'frame mut T, DropFlag<'frame>) {
229    ({ self.ptr }.write(val), self.drop_flag)
230  }
231
232  /// Pin `val` into this slot, returning a reference to it.
233  ///
234  /// # Safety
235  ///
236  /// This function pins the memory this slot wraps, but does not guarantee its
237  /// destructor is run; that is the caller's responsibility, by decrementing
238  /// the given [`DropFlag`].
239  pub unsafe fn pin(self, val: T) -> (Pin<&'frame mut T>, DropFlag<'frame>) {
240    self.emplace(new::of(val))
241  }
242
243  /// Emplace `new` into this slot, returning a reference to it.
244  ///
245  /// # Safety
246  ///
247  /// This function pins the memory this slot wraps, but does not guarantee its
248  /// destructor is run; that is the caller's responsibility, by decrementing
249  /// the given [`DropFlag`].
250  pub unsafe fn emplace<N: New<Output = T>>(
251    self,
252    new: N,
253  ) -> (Pin<&'frame mut T>, DropFlag<'frame>) {
254    match self.try_emplace(new) {
255      Ok((x, d)) => (x, d),
256      Err(e) => match e {},
257    }
258  }
259
260  /// Try to emplace `new` into this slot, returning a reference to it.
261  ///
262  /// # Safety
263  ///
264  /// This function pins the memory this slot wraps, but does not guarantee its
265  /// destructor is run; that is the caller's responsibility, by decrementing
266  /// the given [`DropFlag`].
267  pub unsafe fn try_emplace<N: TryNew<Output = T>>(
268    self,
269    new: N,
270  ) -> Result<(Pin<&'frame mut T>, DropFlag<'frame>), N::Error> {
271    self.drop_flag.inc();
272    new.try_new(Pin::new_unchecked(self.ptr))?;
273    Ok((
274      Pin::new_unchecked(self.ptr.assume_init_mut()),
275      self.drop_flag,
276    ))
277  }
278}
279
280#[doc(hidden)]
281#[allow(missing_docs)]
282pub mod __macro {
283  use super::*;
284  use crate::drop_flag::QuietFlag;
285  pub use core;
286
287  pub struct SlotDropper<T> {
288    val: MaybeUninit<T>,
289    drop_flag: QuietFlag,
290  }
291
292  impl<T> SlotDropper<T> {
293    #[allow(clippy::new_without_default)]
294    pub fn new() -> Self {
295      Self {
296        val: MaybeUninit::uninit(),
297        drop_flag: QuietFlag::new(),
298      }
299    }
300
301    // Workaround for `unsafe {}` unhygine wrt to lints.
302    //
303    // This function is still `unsafe`.
304    pub fn new_unchecked_hygine_hack(&mut self) -> DroppingSlot<T> {
305      unsafe {
306        DroppingSlot::new_unchecked(&mut self.val, self.drop_flag.flag())
307      }
308    }
309  }
310
311  impl<T> Drop for SlotDropper<T> {
312    fn drop(&mut self) {
313      if self.drop_flag.flag().is_dead() {
314        unsafe { ptr::drop_in_place(self.val.assume_init_mut()) }
315      }
316    }
317  }
318
319  // Workaround for `unsafe {}` unhygine wrt to lints.
320  //
321  // This function is still `unsafe`.
322  pub fn new_unchecked_hygine_hack<'frame, T>(
323    ptr: &'frame mut MaybeUninit<T>,
324    drop_flag: DropFlag<'frame>,
325  ) -> Slot<'frame, T> {
326    unsafe { Slot::new_unchecked(ptr, drop_flag) }
327  }
328}
329
330/// Constructs a new [`Slot`].
331///
332/// Because [`Slot`]s need to own data on the stack, but that data cannot
333/// move with the [`Slot`], it must be constructed using this macro. For
334/// example:
335/// ```
336/// moveit::slot!(x, y: bool);
337/// let x = x.put(5);
338/// let y = y.put(false);
339/// ```
340///
341/// This macro is especially useful for passing data into functions that want to
342/// emplace a value into the caller.
343///
344/// The `slot!(#[dropping] x)` syntax can be used to create a [`DroppingSlot`]
345/// instead. This should be a comparatively rare operation.
346///
347/// This macro can also be used without arguments to create a *temporary*
348/// [`Slot`]. Such types cannot be assigned to variables but can be used as
349/// part of a larger expression:
350///
351/// ```compile_fail
352/// # use moveit::Slot;
353/// let bad: Slot<i32> = moveit::slot!();
354/// bad.put(4);  // Borrow check error.
355/// ```
356///
357/// ```
358/// # use moveit::Slot;
359/// fn do_thing(x: Slot<i32>) { /* ... */ }
360/// do_thing(moveit::slot!())
361/// ```
362#[macro_export]
363macro_rules! slot {
364  () => {
365    $crate::slot::__macro::new_unchecked_hygine_hack(
366      &mut $crate::slot::__macro::core::mem::MaybeUninit::uninit(),
367      $crate::drop_flag::TrappedFlag::new().flag(),
368    )
369  };
370  (#[dropping]) => {
371    $crate::slot::__macro::SlotDropper::new().new_unchecked_hygine_hack()
372  };
373  ($($name:ident $(: $ty:ty)?),* $(,)*) => {$(
374    let mut uninit = $crate::slot::__macro::core::mem::MaybeUninit::<
375      $crate::slot!(@tyof $($ty)?)
376    >::uninit();let trap = $crate::drop_flag::TrappedFlag::new();
377    let $name = $crate::slot::__macro::new_unchecked_hygine_hack(
378      &mut uninit,
379      trap.flag()
380    );
381  )*};
382  (#[dropping] $($name:ident $(: $ty:ty)?),* $(,)*) => {$(
383    let mut uninit = $crate::slot::__macro::SlotDropper::<
384      $crate::slot!(@tyof $($ty)?)
385    >::new();
386    #[allow(unsafe_code, unused_unsafe)]
387    let $name = uninit.new_unchecked_hygine_hack();
388  )*};
389  (@tyof) => {_};
390  (@tyof $ty:ty) => {$ty};
391}