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