Skip to main content

const_util/
promote.rs

1use core::mem::MaybeUninit;
2
3use crate::Const;
4use generic_upper_bound as gub;
5
6mod gen {
7    include!(concat!(env!("OUT_DIR"), "/for_each_align.rs"));
8    pub(crate) use for_each_align;
9}
10pub(crate) use gen::for_each_align;
11
12/// # Safety
13/// After promoting the bytes of `C::Type` in a sufficiently aligned [`MaybeUninit`] buffer,
14/// it must be valid to cast the result to `&'a C::Type`. In particular,
15/// `C::Type` must not contain interior mutability.
16///
17/// # Examples
18/// Correct usage:
19/// ```
20/// struct Value;
21/// impl const_util::Const for Value {
22///     type Type = i32;
23///     const VALUE: Self::Type = 1;
24/// }
25/// let promoted = unsafe { const_util::promote::promote::<Value>() };
26/// assert_eq!(promoted, &1);
27/// ```
28///
29/// Incorrect usage:
30/// ```ignore
31/// struct Value;
32/// impl const_util::Const for Value {
33///     type Type = std::cell::Cell<i32>;
34///     const VALUE: Self::Type = std::cell::Cell::new(1);
35/// }
36/// // UNDEFINED BEHAVIOR, even if unused
37/// let _promoted = unsafe { const_util::promote::promote::<Value>() };
38/// ```
39pub const unsafe fn promote<'a, C: Const>() -> &'a C::Type {
40    // SAFETY:
41    // The value was written to the first `size_of::<T>()` (MaybeUninit) bytes of the object,
42    // which is contained in a `SIZE >= size_of::<T>()` length MaybeUninit array at the start
43    // of the object, which can be casted back to `&'a T` by safety requirements. Sufficient
44    // alignment is also ensured.
45    unsafe { &*core::ptr::from_ref(gub::eval_with_upper_bound::<Promote<C>>()).cast() }
46}
47
48trait Anything {}
49impl<T> Anything for T {}
50
51struct Promote<C>(C);
52impl<C: Const> gub::AcceptUpperBound for Promote<C> {
53    type Output = &'static dyn Anything;
54    const DESIRED_GENERIC: usize = size_of::<C::Type>();
55    type Eval<const UPPER: usize> = PromoteSized<C, UPPER>;
56}
57
58#[repr(C)]
59struct AlignedBuf<Aligner, const SIZE: usize>([MaybeUninit<u8>; SIZE], Aligner);
60
61struct PromoteSized<C, const SIZE: usize>(C);
62impl<C: Const, const SIZE: usize> Const for PromoteSized<C, SIZE> {
63    type Type = &'static dyn Anything;
64    const VALUE: Self::Type = if gub::get_upper_bound::<Promote<C>>() == SIZE {
65        const fn const_to_buf<C: Const, const SIZE: usize>() -> [MaybeUninit<u8>; SIZE] {
66            use core::mem::ManuallyDrop;
67
68            #[repr(C)]
69            union Transmute<T, const SIZE: usize> {
70                val: ManuallyDrop<T>,
71                buf: [MaybeUninit<u8>; SIZE],
72            }
73
74            // SAFETY: This reads the first `min(SIZE, size_of::<T>())` bytes from T, leaving the rest
75            // uninitialized. MaybeUninit<u8> can hold arbitrary data, so this is valid.
76            unsafe {
77                Transmute {
78                    val: ManuallyDrop::new(crate::value_of::<C>()),
79                }
80                .buf
81            }
82        }
83        macro_rules! handle_aligns {
84            ($($align:literal)*) => {
85                $(if align_of::<C::Type>() == $align {
86                    #[repr(align($align))]
87                    struct Aligner;
88                    &AlignedBuf(const_to_buf::<C, SIZE>(), Aligner)
89                } else )* {
90                    panic!("Unknown alignment")
91                }
92            };
93        }
94        for_each_align!(handle_aligns)
95    } else {
96        &()
97    };
98}
99
100#[test]
101fn promote_adt() {
102    #[repr(align(256))]
103    #[derive(PartialEq, Eq, Debug)]
104    struct Inner([u32; 3]);
105
106    #[repr(C)]
107    #[derive(PartialEq, Eq, Debug)]
108    struct Composite {
109        f: Option<[fn() -> i32; 3]>,
110        a: Inner,
111        r: &'static u32,
112        p: *const u32,
113        b: bool,
114    }
115
116    fn f<const N: i32>() -> i32 {
117        N
118    }
119
120    struct Value;
121    impl Const for Value {
122        type Type = Composite;
123        const VALUE: Self::Type = Composite {
124            f: Some([f::<1>, f::<420>, f::<13>]),
125            a: Inner([3, 2, 1]),
126            r: &1,
127            p: std::ptr::without_provenance(42),
128            b: true,
129        };
130    }
131
132    // SAFETY: A MaybeUninit buffer can hold an instance of `Composite`,
133    // including provenance. It does not contain interior mutability.
134    let promoted = unsafe { promote::<Value>() };
135
136    assert_eq!(&Value::VALUE, promoted);
137}