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