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 
59 use core::mem;
60 use core::mem::MaybeUninit;
61 use core::pin::Pin;
62 use core::ptr;
63 
64 use crate::drop_flag::DropFlag;
65 use crate::move_ref::MoveRef;
66 use crate::new;
67 use crate::new::New;
68 use crate::new::TryNew;
69 
70 #[cfg(doc)]
71 use {
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].
82 pub struct Slot<'frame, T> {
83   ptr: &'frame mut MaybeUninit<T>,
84   drop_flag: DropFlag<'frame>,
85 }
86 
87 impl<'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].
new_unchecked( ptr: &'frame mut MaybeUninit<T>, drop_flag: DropFlag<'frame>, ) -> Self99   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`].
put(self, val: T) -> MoveRef<'frame, T>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`].
pin(self, val: T) -> Pin<MoveRef<'frame, T>>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`].
emplace<N: New<Output = T>>(self, new: N) -> Pin<MoveRef<'frame, T>>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`].
try_emplace<N: TryNew<Output = T>>( self, new: N, ) -> Result<Pin<MoveRef<'frame, T>>, N::Error>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.
into_pinned(self) -> Slot<'frame, Pin<T>>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`.
cast<U>(self) -> Slot<'frame, 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 
172 impl<'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.
into_unpinned(self) -> Slot<'frame, T>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.
202 pub struct DroppingSlot<'frame, T> {
203   ptr: &'frame mut MaybeUninit<T>,
204   drop_flag: DropFlag<'frame>,
205 }
206 
207 impl<'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].
new_unchecked( ptr: &'frame mut MaybeUninit<T>, drop_flag: DropFlag<'frame>, ) -> Self219   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.
put(self, val: T) -> (&'frame mut T, DropFlag<'frame>)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`].
pin(self, val: T) -> (Pin<&'frame mut T>, DropFlag<'frame>)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`].
emplace<N: New<Output = T>>( self, new: N, ) -> (Pin<&'frame mut T>, DropFlag<'frame>)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`].
try_emplace<N: TryNew<Output = T>>( self, new: N, ) -> Result<(Pin<&'frame mut T>, DropFlag<'frame>), N::Error>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)]
282 pub 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)]
new() -> Self294     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`.
new_unchecked_hygine_hack(&mut self) -> DroppingSlot<T>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> {
drop(&mut self)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`.
new_unchecked_hygine_hack<'frame, T>( ptr: &'frame mut MaybeUninit<T>, drop_flag: DropFlag<'frame>, ) -> Slot<'frame, T>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]
363 macro_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 }
392