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)
}