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}