| 1 | use core::future::Future; |
| 2 | use core::mem::MaybeUninit; |
| 3 | use core::pin::Pin; |
| 4 | use core::task::{Context, Poll}; |
| 5 | |
| 6 | /// A wrapper type that tells the compiler that the contents might not be valid. |
| 7 | /// |
| 8 | /// This is necessary mainly when `T` contains a reference. In that case, the |
| 9 | /// compiler will sometimes assume that the reference is always valid; in some |
| 10 | /// cases it will assume this even after the destructor of `T` runs. For |
| 11 | /// example, when a reference is used as a function argument, then the compiler |
| 12 | /// will assume that the reference is valid until the function returns, even if |
| 13 | /// the reference is destroyed during the function. When the reference is used |
| 14 | /// as part of a self-referential struct, that assumption can be false. Wrapping |
| 15 | /// the reference in this type prevents the compiler from making that |
| 16 | /// assumption. |
| 17 | /// |
| 18 | /// # Invariants |
| 19 | /// |
| 20 | /// The `MaybeUninit` will always contain a valid value until the destructor runs. |
| 21 | // |
| 22 | // Reference |
| 23 | // See <https://users.rust-lang.org/t/unsafe-code-review-semi-owning-weak-rwlock-t-guard/95706> |
| 24 | // |
| 25 | // TODO: replace this with an official solution once RFC #3336 or similar is available. |
| 26 | // <https://github.com/rust-lang/rfcs/pull/3336> |
| 27 | #[repr (transparent)] |
| 28 | pub(crate) struct MaybeDangling<T>(MaybeUninit<T>); |
| 29 | |
| 30 | impl<T> Drop for MaybeDangling<T> { |
| 31 | fn drop(&mut self) { |
| 32 | // Safety: `0` is always initialized. |
| 33 | unsafe { core::ptr::drop_in_place(self.0.as_mut_ptr()) }; |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | impl<T> MaybeDangling<T> { |
| 38 | pub(crate) fn new(inner: T) -> Self { |
| 39 | Self(MaybeUninit::new(val:inner)) |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | impl<F: Future> Future for MaybeDangling<F> { |
| 44 | type Output = F::Output; |
| 45 | |
| 46 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 47 | // Safety: `0` is always initialized. |
| 48 | let fut: Pin<&mut F> = unsafe { self.map_unchecked_mut(|this: &mut MaybeDangling| this.0.assume_init_mut()) }; |
| 49 | fut.poll(cx) |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | #[test ] |
| 54 | fn maybedangling_runs_drop() { |
| 55 | struct SetOnDrop<'a>(&'a mut bool); |
| 56 | |
| 57 | impl Drop for SetOnDrop<'_> { |
| 58 | fn drop(&mut self) { |
| 59 | *self.0 = true; |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | let mut success = false; |
| 64 | |
| 65 | drop(MaybeDangling::new(SetOnDrop(&mut success))); |
| 66 | assert!(success); |
| 67 | } |
| 68 | |