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}