dungeon_cell/compile_time/
transmute.rs

1//! Transmute that bypasses the normal restrictions of being used on generics.
2
3use core::mem::ManuallyDrop;
4
5/// Transmute for const contexts.
6///
7/// This transmute bypasses basically all restrictions on the normal
8/// [`core::mem::transmute`]. As such, this function is extremely unsafe to use.
9/// It is only used because we can't use traits in const expressions to get the internals
10/// if associated types.
11///
12/// # Safety
13/// `T` must be transmutable to `U`. This means `U` must be the same layout as `T` or contained in
14/// the layout of `T`.
15pub const unsafe fn const_transmute<T, U>(value: T) -> U {
16    // Create union type that can store a `T` or a `U`.
17    // We can then use this to convert between them.
18    //
19    // The repr(C) layout forces no offset between `t` and `u` as talked about here
20    // https://rust-lang.github.io/unsafe-code-guidelines/layout/unions.html#c-compatible-layout-repr-c
21    #[repr(C)]
22    union Transmute<T, U> {
23        t: ManuallyDrop<T>,
24        u: ManuallyDrop<U>,
25    }
26
27    // Create the union in the `T` state.
28    let value = Transmute {
29        t: ManuallyDrop::new(value),
30    };
31
32    // Read from the union in the `U` state.
33    // SAFETY: This is safe because the caller has promised that `T` can be transmuted to `U`.
34    // The following reference link talks about repr(C) unions being used this way.
35    // https://doc.rust-lang.org/reference/items/unions.html#reading-and-writing-union-fields
36    ManuallyDrop::into_inner(unsafe { value.u })
37}
38
39#[cfg(test)]
40mod test {
41    use core::mem::MaybeUninit;
42
43    use super::*;
44
45    #[test]
46    fn transmuting_to_the_same_type_results_in_the_original_value() {
47        #[derive(PartialEq, Debug, Clone)]
48        struct MyType {
49            value: i32,
50        }
51
52        let original = MyType { value: 123 };
53        let to_be_transmuted = original.clone();
54
55        // SAFETY: `MyType` to `MyType` doesn't change anything.
56        let transmuted: MyType = unsafe { const_transmute(to_be_transmuted) };
57
58        assert_eq!(transmuted, original);
59    }
60
61    #[test]
62    fn const_transmute_can_be_wrapped_in_a_generic_const_function() {
63        // The normal transmute doesn't work if wrapped.
64        const fn _broken_wrapper<T, U>(_t: T) -> U {
65            // error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
66            //   --> src/const_transmute.rs:58:22
67            //    |
68            // 58 |             unsafe { core::mem::transmute(t) }
69            //    |                      ^^^^^^^^^^^^^^^^^^^^
70            //    |
71            //    = note: source type: `T` (this type does not have a fixed size)
72            //    = note: target type: `U` (this type does not have a fixed size)
73
74            // unsafe { core::mem::transmute(t) }
75            unimplemented!();
76        }
77
78        // Our transmute does work if wrapped.
79        const fn _working_wrapper<T, U>(t: T) -> U {
80            // SAFETY: This function isnt actually called.
81            unsafe { const_transmute(t) }
82        }
83    }
84
85    #[test]
86    fn transmuting_from_a_type_to_a_type_with_the_same_layout_is_sound() {
87        #[repr(C)]
88        struct MyTypeA {
89            value_a: i32,
90        }
91
92        #[derive(PartialEq, Debug)]
93        #[repr(C)]
94        struct MyTypeB {
95            value_b: i32,
96        }
97
98        let original = MyTypeA { value_a: 123 };
99
100        // SAFETY: `MyTypeA` and `MyTypeB` have the same layout because of repr(C).
101        let transmuted: MyTypeB = unsafe { const_transmute(original) };
102
103        assert_eq!(transmuted, MyTypeB { value_b: 123 });
104    }
105
106    #[test]
107    fn transmuting_from_a_transparent_wrapper_type_to_its_internal_type_is_sound(
108    ) {
109        #[repr(transparent)]
110        struct MyTypeA {
111            _inner: MyTypeB,
112        }
113
114        #[derive(PartialEq, Debug, Clone)]
115        struct MyTypeB {
116            value: i32,
117        }
118
119        let original = MyTypeB { value: 123 };
120        let to_be_transmuted = MyTypeA {
121            _inner: original.clone(),
122        };
123
124        // SAFETY: `MyTypeA` is repr transparent to `MyTypeB`.
125        let transmuted: MyTypeB = unsafe { const_transmute(to_be_transmuted) };
126
127        assert_eq!(transmuted, original);
128    }
129
130    #[test]
131    fn transmuting_from_a_type_to_a_longer_maybe_uninit_buffer_then_back_is_sound(
132    ) {
133        #[derive(PartialEq, Debug, Clone)]
134        struct MyType {
135            value: i32,
136        }
137
138        let original = MyType { value: 123 };
139        let to_be_transmuted = original.clone();
140
141        type Buffer = [MaybeUninit<u8>; core::mem::size_of::<MyType>() + 4];
142
143        // SAFETY: `Buffer` is 4 bytes longer than `MyType`.
144        let transmuted: Buffer = unsafe { const_transmute(to_be_transmuted) };
145
146        // SAFETY: We just put a `MyType` value into the buffer.
147        let transmuted_back: MyType = unsafe { const_transmute(transmuted) };
148
149        assert_eq!(transmuted_back, original);
150    }
151}