1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
//! Contains a proof type, demonstrating the layout equality of two types. //! //! This is relevant to allocated containers as well as other reuse of raw memory since layout //! equality guarantees certain types of soundness. For example, the memory allocated for a //! `Box<A>` can be reused for storing a type `B` exactly if those two types have the same layout. //! //! The module defines a number of helpers (`for_*`) that _guarantee_ construction. Also note that //! most of the methods are usable in `const` contexts. Albeit, in practice you might need to use a //! generic lifetime parameter in one of your proofs but this is not possible in constants. //! Instead, wait for `const {}` blocks to stabilize. use core::alloc::Layout; use core::mem::MaybeUninit; use core::marker::PhantomData; use core::ptr::NonNull; use alloc::boxed::Box; use alloc::vec::Vec; /// A proof type, showing two types `A` and `B` have the **same** layout. #[derive(Debug, PartialEq, Eq, Hash)] pub struct SameLayout<A, B>(PhantomData<(A, B)>); impl<A, B> SameLayout<A, B> { pub const fn new() -> Option<Self> { let layout_a = Layout::new::<A>(); let layout_b = Layout::new::<B>(); // Direct comparison requires `ops::Eq` which obviously is NOT yet const fn. // Also this is exactly what we required for allocators, as documented there. if layout_a.size() == layout_b.size() && layout_a.align() == layout_b.align() { Some(SameLayout(PhantomData)) } else { None } } /// 'Transmute' a vector by reusing its buffer. /// NOTE: This will _forget_ all elements. You must clear the vector first if they are /// important, or `set_len` on the result if you can guarantee that old elements are valid /// initializers for the new type. /// This affords more flexibility for the caller as they might want to use As as an initializer /// for Bs which would be invalid if we dropped them. Manually drain the vector if this is not /// desirable. pub fn forget_vec(self, vec: Vec<A>) -> Vec<B> { let mut vec = core::mem::ManuallyDrop::new(vec); let cap = vec.capacity(); let ptr = vec.as_mut_ptr(); // SAFETY: // - ptr was previously allocated with Vec. // - B has the same alignment and size as per our invariants. // - 0 is less than or equal to capacity. // - capacity is the capacity the Vec was allocated with. // - All elements (there are none) as initialized. unsafe { Vec::from_raw_parts(ptr as *mut B, 0, cap) } } /// 'Transmute' a box by reusing its buffer. /// NOTE: for the same flexibility as Vec, forget about the returned `A`. pub fn deinit_box(self, boxed: Box<A>) -> (A, Box<MaybeUninit<B>>) { let ptr = Box::into_raw(boxed); // SAFETY: just was a valid box.. let a = unsafe { core::ptr::read(ptr) }; // SAFETY: // - ptr was previously allocated with Box. // - The ptr is valid for reads and writes as it comes from a Box. // - B has the same alignment and size as per our invariants. // - Any instance of MaybeUninit is always valid. (a, unsafe { Box::from_raw(ptr as *mut MaybeUninit<B>) }) } } impl<A, B> Clone for SameLayout<A, B> { fn clone(&self) -> Self { SameLayout(self.0) } } impl<A, B> Copy for SameLayout<A, B> {} impl<A, B> SameLayout<A, B> { pub const fn array<const N: usize>(self) -> SameLayout<[A; N], [B; N]> { SameLayout(PhantomData) } /// Apply a transitive argument to construct a new relation proof. pub const fn chain<C>(self, _: SameLayout<B, C>) -> SameLayout<A, C> { SameLayout(PhantomData) } /// Use commutativity of equality. pub const fn transpose(self) -> SameLayout<B, A> { SameLayout(PhantomData) } } /// A proof that any type has the same layout as itself. pub const fn id<A>() -> SameLayout<A, A> { SameLayout(PhantomData) } /// A proof that any reference has same layout as a raw pointer. pub const fn for_ref<'a, A: ?Sized>() -> SameLayout<*const A, &'a A> { SameLayout(PhantomData) } /// A proof that any mutable reference has same layout as a raw pointer. /// FIXME: this is not const because of the very narrow formulation of https://github.com/rust-lang/rust/issues/57349 pub fn for_mut<'a, A: ?Sized>() -> SameLayout<*const A, &'a mut A> { SameLayout(PhantomData) } /// A proof that any option wrapped reference has same layout as an pure reference. pub const fn for_ref_opt<'a, A: ?Sized>() -> SameLayout<&'a A, Option<&'a A>> { SameLayout(PhantomData) } /// A proof that any option wrapped mutable reference has same layout as an pure reference. /// FIXME: this is not const because of the very narrow formulation of https://github.com/rust-lang/rust/issues/57349 pub fn for_mut_opt<'a, A: ?Sized>() -> SameLayout<&'a mut A, Option<&'a mut A>> { SameLayout(PhantomData) } /// A proof that sized pointers have same layout as any other sized pointer. pub const fn for_sized_ptr<A, B>() -> SameLayout<*const A, *const B> { SameLayout(PhantomData) } /// A proof that mutable pointer has the same layout as a const pointer. pub const fn for_ptr_mut<A: ?Sized>() -> SameLayout<*const A, *mut A> { SameLayout(PhantomData) } /// A proof that a non-null pointer has the same layout as a raw pointer. pub const fn for_non_null<A: ?Sized>() -> SameLayout<*const A, NonNull<A>> { SameLayout(PhantomData) } /// A proof that an option of a non-null pointer has the same layout as a raw pointer. pub const fn for_non_null_opt<A: ?Sized>() -> SameLayout<*const A, Option<NonNull<A>>> { SameLayout(PhantomData) } /// A proof that any box has same layout as a raw pointer. pub const fn for_box<A: ?Sized>() -> SameLayout<*const A, Box<A>> { SameLayout(PhantomData) } /// A proof that any optional box has same layout as a raw pointer. pub const fn for_box_opt<A: ?Sized>() -> SameLayout<*const A, Option<Box<A>>> { SameLayout(PhantomData) }