|
32 | 32 |
|
33 | 33 | use crate::alloc::Layout; |
34 | 34 | use crate::clone::TrivialClone; |
| 35 | +use crate::cmp::Ordering; |
35 | 36 | use crate::marker::{Destruct, DiscriminantKind}; |
36 | 37 | use crate::panic::const_assert; |
37 | 38 | use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; |
@@ -1088,6 +1089,102 @@ pub const unsafe fn transmute_copy<Src, Dst>(src: &Src) -> Dst { |
1088 | 1089 | } |
1089 | 1090 | } |
1090 | 1091 |
|
| 1092 | +/// Like [`transmute`], but only initializes the "common prefix" of the first |
| 1093 | +/// `min(size_of::<Src>(), size_of::<Dst>())` bytes of the destination from the |
| 1094 | +/// corresponding bytes of the source. |
| 1095 | +/// |
| 1096 | +/// This is equivalent to a "union cast" through a `union` with `#[repr(C)]`. |
| 1097 | +/// |
| 1098 | +/// That means some size mismatches are not UB, like `[T; 2]` to `[T; 1]`. |
| 1099 | +/// Increasing size is usually UB from being insufficiently initialized -- like |
| 1100 | +/// `u8` to `u32` -- but isn't always. For example, going from `u8` to |
| 1101 | +/// `#[repr(C, align(4))] AlignedU8(u8);` is sound. |
| 1102 | +/// |
| 1103 | +/// Prefer normal `transmute` where possible, for the extra checking, since |
| 1104 | +/// both do exactly the same thing at runtime, if they both compile. |
| 1105 | +/// |
| 1106 | +/// # Safety |
| 1107 | +/// |
| 1108 | +/// If `size_of::<Src>() >= size_of::<Dst>()`, the first `size_of::<Dst>()` bytes |
| 1109 | +/// of `src` must be be *valid* when interpreted as a `Dst`. (In this case, the |
| 1110 | +/// preconditions are the same as for `transmute_copy(&ManuallyDrop::new(src))`.) |
| 1111 | +/// |
| 1112 | +/// If `size_of::<Src>() <= size_of::<Dst>()`, the bytes of `src` padded with |
| 1113 | +/// uninitialized bytes afterwards up to a total size of `size_of::<Dst>()` |
| 1114 | +/// must be *valid* when interpreted as a `Dst`. |
| 1115 | +/// |
| 1116 | +/// In both cases, any safety preconditions of the `Dst` type must also be upheld. |
| 1117 | +/// |
| 1118 | +/// # Examples |
| 1119 | +/// |
| 1120 | +/// ``` |
| 1121 | +/// #![feature(transmute_prefix)] |
| 1122 | +/// use std::mem::transmute_prefix; |
| 1123 | +/// |
| 1124 | +/// assert_eq!(unsafe { transmute_prefix::<[i32; 4], [i32; 2]>([1, 2, 3, 4]) }, [1, 2]); |
| 1125 | +/// |
| 1126 | +/// let expected = if cfg!(target_endian = "little") { 0x34 } else { 0x12 }; |
| 1127 | +/// assert_eq!(unsafe { transmute_prefix::<u16, u8>(0x1234) }, expected); |
| 1128 | +/// |
| 1129 | +/// // Would be UB because the destination is incompletely initialized. |
| 1130 | +/// // transmute_prefix::<u8, u16>(123) |
| 1131 | +/// |
| 1132 | +/// // OK because the destination is allowed to be partially initialized. |
| 1133 | +/// let _: std::mem::MaybeUninit<u16> = unsafe { transmute_prefix(123_u8) }; |
| 1134 | +/// ``` |
| 1135 | +#[unstable(feature = "transmute_prefix", issue = "155079")] |
| 1136 | +pub const unsafe fn transmute_prefix<Src, Dst>(src: Src) -> Dst { |
| 1137 | + #[repr(C)] |
| 1138 | + union Transmute<A, B> { |
| 1139 | + a: ManuallyDrop<A>, |
| 1140 | + b: ManuallyDrop<B>, |
| 1141 | + } |
| 1142 | + |
| 1143 | + match const { Ord::cmp(&Src::SIZE, &Dst::SIZE) } { |
| 1144 | + // SAFETY: When Dst is bigger, the union is the size of Dst |
| 1145 | + Ordering::Less => unsafe { |
| 1146 | + let a = transmute_neo(src); |
| 1147 | + intrinsics::transmute_unchecked(Transmute::<Src, Dst> { a }) |
| 1148 | + }, |
| 1149 | + // SAFETY: When they're the same size, we can use the MIR primitive |
| 1150 | + Ordering::Equal => unsafe { intrinsics::transmute_unchecked::<Src, Dst>(src) }, |
| 1151 | + // SAFETY: When Src is bigger, the union is the size of Src |
| 1152 | + Ordering::Greater => unsafe { |
| 1153 | + let u: Transmute<Src, Dst> = intrinsics::transmute_unchecked(src); |
| 1154 | + transmute_neo(u.b) |
| 1155 | + }, |
| 1156 | + } |
| 1157 | +} |
| 1158 | + |
| 1159 | +/// New version of `transmute`, exposed under this name so it can be iterated upon |
| 1160 | +/// without risking breakage to uses of "real" transmute. |
| 1161 | +/// |
| 1162 | +/// It will not be stabilized under this name. |
| 1163 | +/// |
| 1164 | +/// # Examples |
| 1165 | +/// |
| 1166 | +/// ``` |
| 1167 | +/// #![feature(transmute_neo)] |
| 1168 | +/// use std::mem::transmute_neo; |
| 1169 | +/// |
| 1170 | +/// assert_eq!(unsafe { transmute_neo::<f32, u32>(0.0) }, 0); |
| 1171 | +/// ``` |
| 1172 | +/// |
| 1173 | +/// ```compile_fail,E0080 |
| 1174 | +/// #![feature(transmute_neo)] |
| 1175 | +/// use std::mem::transmute_neo; |
| 1176 | +/// |
| 1177 | +/// unsafe { transmute_neo::<u32, u16>(123) }; |
| 1178 | +/// ``` |
| 1179 | +#[unstable(feature = "transmute_neo", issue = "155079")] |
| 1180 | +pub const unsafe fn transmute_neo<Src, Dst>(src: Src) -> Dst { |
| 1181 | + const { assert!(Src::SIZE == Dst::SIZE) }; |
| 1182 | + |
| 1183 | + // SAFETY: the const-assert just checked that they're the same size, |
| 1184 | + // and any other safety invariants need to be upheld by the caller. |
| 1185 | + unsafe { intrinsics::transmute_unchecked(src) } |
| 1186 | +} |
| 1187 | + |
1091 | 1188 | /// Opaque type representing the discriminant of an enum. |
1092 | 1189 | /// |
1093 | 1190 | /// See the [`discriminant`] function in this module for more information. |
|
0 commit comments