|
| 1 | +//! This module deals with interior mutability, which [undermines][1] the |
| 2 | +//! soundness of runtime uses of `const`-allocated heap objects. |
| 3 | +//! |
| 4 | +//! [1]: https://github.com/rust-lang/rust/pull/91884#discussion_r774659436 |
| 5 | +use core::fmt; |
| 6 | + |
| 7 | +// FIXME: Get rid of `Frozen` if one of the folllowing things happens: |
| 8 | +// (1) It's decided that interior mutability implies `!Copy`. |
| 9 | +// (2) A trait indicating the absence of interior mutability, such as |
| 10 | +// the one proposed by the now-closed [RFC 2944], is added to `core`. |
| 11 | +// |
| 12 | +// [RFC 2944]: https://github.com/rust-lang/rfcs/pull/2944 |
| 13 | +// |
| 14 | +// <https://github.com/rust-lang/rust/issues/25053#issuecomment-493742957>: |
| 15 | +// |
| 16 | +// > Basically, currently we have no interior-mutable-`Copy` type, but that is |
| 17 | +// > an accident. And we also have legit needs for `Copy` interior mutable |
| 18 | +// > types, which is irreconcilable with using `Copy` to exclude interior |
| 19 | +// > mutability. |
| 20 | + |
| 21 | +/// Erases interior mutability by preventing reference forming. |
| 22 | +#[repr(transparent)] |
| 23 | +pub struct Frozen<T: ?Sized>(T); |
| 24 | + |
| 25 | +impl<T: Copy> Frozen<T> { |
| 26 | + /// Get a copy of the contained `T`. |
| 27 | + #[inline] |
| 28 | + pub const fn get(&self) -> T { |
| 29 | + self.0 |
| 30 | + } |
| 31 | + |
| 32 | + /// Copy the referenced `[T]` to the CTFE heap. The resulting reference can |
| 33 | + /// be safely consumed at runtime. |
| 34 | + /// |
| 35 | + /// # Example |
| 36 | + /// |
| 37 | + /// ```rust |
| 38 | + /// use r3_core::utils::Frozen; |
| 39 | + /// const SLICE: &[Frozen<u8>] = Frozen::leak_slice(&[1, 2, 3]); |
| 40 | + /// assert_eq!(SLICE[1].get(), 2); |
| 41 | + /// ``` |
| 42 | + pub const fn leak_slice<'out>(x: &[T]) -> &'out [Frozen<T>] |
| 43 | + where |
| 44 | + T: 'out, |
| 45 | + { |
| 46 | + let size = core::mem::size_of::<T>() |
| 47 | + .checked_mul(x.len()) |
| 48 | + .expect("size overflow"); |
| 49 | + let align = core::mem::align_of::<T>(); |
| 50 | + unsafe { |
| 51 | + // Allocate a CTFE heap memory block |
| 52 | + let ptr = core::intrinsics::const_allocate(size, align).cast::<T>(); |
| 53 | + assert!( |
| 54 | + !ptr.guaranteed_eq(core::ptr::null_mut()), |
| 55 | + "heap allocation failed" |
| 56 | + ); |
| 57 | + |
| 58 | + // Copy the `[T]` onto it |
| 59 | + core::ptr::copy_nonoverlapping(x.as_ptr(), ptr, x.len()); |
| 60 | + |
| 61 | + // Reinterpret it as `[Frozen<T>]` (it's safe because of |
| 62 | + // `repr(transparent)`) |
| 63 | + let ptr = ptr.cast::<Frozen<T>>(); |
| 64 | + |
| 65 | + // Turn `ptr` into a alice reference |
| 66 | + core::slice::from_raw_parts(ptr, x.len()) |
| 67 | + } |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +impl<T: Copy> Copy for Frozen<T> {} |
| 72 | + |
| 73 | +impl<T: Copy> const Clone for Frozen<T> { |
| 74 | + #[inline] |
| 75 | + fn clone(&self) -> Self { |
| 76 | + // Don't use `T as Clone` because it could expose interior mutability. |
| 77 | + *self |
| 78 | + } |
| 79 | + |
| 80 | + #[inline] |
| 81 | + fn clone_from(&mut self, source: &Self) { |
| 82 | + *self = *source; |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +impl<T: Copy + ~const fmt::Debug> const fmt::Debug for Frozen<T> { |
| 87 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 88 | + self.get().fmt(f) |
| 89 | + } |
| 90 | +} |
0 commit comments