Skip to content

Commit 9e9502a

Browse files
joshlfjswrenn
authored andcommitted
Teach transmute_{ref,mut}! to handle slice DSTs
TODO: - Figure out how to make this backwards-compatible (since current macros work with `T: Sized`, but `T: Sized` does not imply `T: KnownLayout`) - Make it so that `Sized` bounds are still enforced on older toolchains that don't support slice DST transmutes - Write safety comments - Get rid of now-unused macros and functions in util::macro_util Makes progress on #1817 gherrit-pr-id: Ib4bc62202e0b3b09d155333b525087f7aa8f02c2
1 parent b544516 commit 9e9502a

File tree

3 files changed

+351
-171
lines changed

3 files changed

+351
-171
lines changed

src/macros.rs

+101-103
Original file line numberDiff line numberDiff line change
@@ -182,22 +182,15 @@ macro_rules! transmute_ref {
182182
// this macro expression is `&U` where `U: 'u + Sized + FromBytes +
183183
// Immutable`, and that `'t` outlives `'u`.
184184

185-
struct AssertSrcIsSized<'a, T: ::core::marker::Sized>(&'a T);
186185
struct AssertSrcIsIntoBytes<'a, T: ?::core::marker::Sized + $crate::IntoBytes>(&'a T);
187186
struct AssertSrcIsImmutable<'a, T: ?::core::marker::Sized + $crate::Immutable>(&'a T);
188-
struct AssertDstIsSized<'a, T: ::core::marker::Sized>(&'a T);
189187
struct AssertDstIsFromBytes<'a, U: ?::core::marker::Sized + $crate::FromBytes>(&'a U);
190188
struct AssertDstIsImmutable<'a, T: ?::core::marker::Sized + $crate::Immutable>(&'a T);
191189

192-
let _ = AssertSrcIsSized(e);
193190
let _ = AssertSrcIsIntoBytes(e);
194191
let _ = AssertSrcIsImmutable(e);
195192

196193
if true {
197-
#[allow(unused, unreachable_code)]
198-
let u = AssertDstIsSized(loop {});
199-
u.0
200-
} else if true {
201194
#[allow(unused, unreachable_code)]
202195
let u = AssertDstIsFromBytes(loop {});
203196
u.0
@@ -206,36 +199,13 @@ macro_rules! transmute_ref {
206199
let u = AssertDstIsImmutable(loop {});
207200
u.0
208201
}
209-
} else if false {
210-
// This branch, though never taken, ensures that `size_of::<T>() ==
211-
// size_of::<U>()` and that that `align_of::<T>() >=
212-
// align_of::<U>()`.
213-
214-
// `t` is inferred to have type `T` because it's assigned to `e` (of
215-
// type `&T`) as `&t`.
216-
let mut t = loop {};
217-
e = &t;
218-
219-
// `u` is inferred to have type `U` because it's used as `&u` as the
220-
// value returned from this branch.
221-
let u;
222-
223-
$crate::assert_size_eq!(t, u);
224-
$crate::assert_align_gt_eq!(t, u);
225-
226-
&u
227202
} else {
228-
// SAFETY: For source type `Src` and destination type `Dst`:
229-
// - We know that `Src: IntoBytes + Immutable` and `Dst: FromBytes +
230-
// Immutable` thanks to the uses of `AssertSrcIsIntoBytes`,
231-
// `AssertSrcIsImmutable`, `AssertDstIsFromBytes`, and
232-
// `AssertDstIsImmutable` above.
233-
// - We know that `size_of::<Src>() == size_of::<Dst>()` thanks to
234-
// the use of `assert_size_eq!` above.
235-
// - We know that `align_of::<Src>() >= align_of::<Dst>()` thanks to
236-
// the use of `assert_align_gt_eq!` above.
237-
let u = unsafe { $crate::util::macro_util::transmute_ref(e) };
238-
$crate::util::macro_util::must_use(u)
203+
use $crate::util::macro_util::TransmuteRefDst;
204+
let t = $crate::util::macro_util::Wrap(e);
205+
// SAFETY: todo
206+
unsafe {
207+
t.transmute_ref()
208+
}
239209
}
240210
}}
241211
}
@@ -334,26 +304,18 @@ macro_rules! transmute_mut {
334304
// writing, mutable references are banned), the error message
335305
// appears to originate in the user's code rather than in the
336306
// internals of this macro.
337-
struct AssertSrcIsSized<'a, T: ::core::marker::Sized>(&'a T);
338307
struct AssertSrcIsFromBytes<'a, T: ?::core::marker::Sized + $crate::FromBytes>(&'a T);
339308
struct AssertSrcIsIntoBytes<'a, T: ?::core::marker::Sized + $crate::IntoBytes>(&'a T);
340-
struct AssertDstIsSized<'a, T: ::core::marker::Sized>(&'a T);
341309
struct AssertDstIsFromBytes<'a, T: ?::core::marker::Sized + $crate::FromBytes>(&'a T);
342310
struct AssertDstIsIntoBytes<'a, T: ?::core::marker::Sized + $crate::IntoBytes>(&'a T);
343311

344312
if true {
345-
let _ = AssertSrcIsSized(&*e);
346-
} else if true {
347313
let _ = AssertSrcIsFromBytes(&*e);
348314
} else {
349315
let _ = AssertSrcIsIntoBytes(&*e);
350316
}
351317

352318
if true {
353-
#[allow(unused, unreachable_code)]
354-
let u = AssertDstIsSized(loop {});
355-
&mut *u.0
356-
} else if true {
357319
#[allow(unused, unreachable_code)]
358320
let u = AssertDstIsFromBytes(loop {});
359321
&mut *u.0
@@ -362,32 +324,13 @@ macro_rules! transmute_mut {
362324
let u = AssertDstIsIntoBytes(loop {});
363325
&mut *u.0
364326
}
365-
} else if false {
366-
// This branch, though never taken, ensures that `size_of::<T>() ==
367-
// size_of::<U>()` and that that `align_of::<T>() >=
368-
// align_of::<U>()`.
369-
370-
// `t` is inferred to have type `T` because it's assigned to `e` (of
371-
// type `&mut T`) as `&mut t`.
372-
let mut t = loop {};
373-
e = &mut t;
374-
375-
// `u` is inferred to have type `U` because it's used as `&mut u` as
376-
// the value returned from this branch.
377-
let u;
378-
379-
$crate::assert_size_eq!(t, u);
380-
$crate::assert_align_gt_eq!(t, u);
381-
382-
&mut u
383327
} else {
384-
// SAFETY: For source type `Src` and destination type `Dst`:
385-
// - We know that `size_of::<Src>() == size_of::<Dst>()` thanks to
386-
// the use of `assert_size_eq!` above.
387-
// - We know that `align_of::<Src>() >= align_of::<Dst>()` thanks to
388-
// the use of `assert_align_gt_eq!` above.
389-
let u = unsafe { $crate::util::macro_util::transmute_mut(e) };
390-
$crate::util::macro_util::must_use(u)
328+
use $crate::util::macro_util::TransmuteMutDst;
329+
let t = $crate::util::macro_util::Wrap(e);
330+
// SAFETY: todo
331+
unsafe {
332+
t.transmute_mut()
333+
}
391334
}
392335
}}
393336
}
@@ -1038,40 +981,71 @@ mod tests {
1038981
assert_eq!(x.into_inner(), 1);
1039982
}
1040983

1041-
#[test]
1042-
fn test_transmute_ref() {
1043-
// Test that memory is transmuted as expected.
1044-
let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
1045-
let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
1046-
let x: &[[u8; 2]; 4] = transmute_ref!(&array_of_u8s);
1047-
assert_eq!(*x, array_of_arrays);
1048-
let x: &[u8; 8] = transmute_ref!(&array_of_arrays);
1049-
assert_eq!(*x, array_of_u8s);
1050-
1051-
// Test that `transmute_ref!` is legal in a const context.
1052-
const ARRAY_OF_U8S: [u8; 8] = [0u8, 1, 2, 3, 4, 5, 6, 7];
1053-
const ARRAY_OF_ARRAYS: [[u8; 2]; 4] = [[0, 1], [2, 3], [4, 5], [6, 7]];
1054-
#[allow(clippy::redundant_static_lifetimes)]
1055-
const X: &'static [[u8; 2]; 4] = transmute_ref!(&ARRAY_OF_U8S);
1056-
assert_eq!(*X, ARRAY_OF_ARRAYS);
1057-
1058-
// Test that it's legal to transmute a reference while shrinking the
1059-
// lifetime (note that `X` has the lifetime `'static`).
1060-
let x: &[u8; 8] = transmute_ref!(X);
1061-
assert_eq!(*x, ARRAY_OF_U8S);
1062-
1063-
// Test that `transmute_ref!` supports decreasing alignment.
1064-
let u = AU64(0);
1065-
let array = [0, 0, 0, 0, 0, 0, 0, 0];
1066-
let x: &[u8; 8] = transmute_ref!(&u);
1067-
assert_eq!(*x, array);
1068-
1069-
// Test that a mutable reference can be turned into an immutable one.
1070-
let mut x = 0u8;
1071-
#[allow(clippy::useless_transmute)]
1072-
let y: &u8 = transmute_ref!(&mut x);
1073-
assert_eq!(*y, 0);
1074-
}
984+
// #[test]
985+
// fn test_transmute_ref() {
986+
// // Test that memory is transmuted as expected.
987+
// let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
988+
// let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
989+
// let x: &[[u8; 2]; 4] = transmute_ref!(&array_of_u8s);
990+
// assert_eq!(*x, array_of_arrays);
991+
// let x: &[u8; 8] = transmute_ref!(&array_of_arrays);
992+
// assert_eq!(*x, array_of_u8s);
993+
994+
// // Test that `transmute_ref!` is legal in a const context.
995+
// const ARRAY_OF_U8S: [u8; 8] = [0u8, 1, 2, 3, 4, 5, 6, 7];
996+
// const ARRAY_OF_ARRAYS: [[u8; 2]; 4] = [[0, 1], [2, 3], [4, 5], [6, 7]];
997+
// #[allow(clippy::redundant_static_lifetimes)]
998+
// const X: &'static [[u8; 2]; 4] = transmute_ref!(&ARRAY_OF_U8S);
999+
// assert_eq!(*X, ARRAY_OF_ARRAYS);
1000+
1001+
// // Test that `transmute_ref!` works on slice DSTs in and that memory is
1002+
// // transmuted as expected.
1003+
// #[derive(KnownLayout, Immutable, FromBytes, IntoBytes, PartialEq, Debug)]
1004+
// #[repr(C)]
1005+
// struct SliceDst<T> {
1006+
// a: u8,
1007+
// b: [T],
1008+
// }
1009+
1010+
// use crate::byteorder::native_endian::U16;
1011+
// let slice_dst_of_u8s =
1012+
// SliceDst::<[u8; 2]>::ref_from_bytes(&[0, 1, 2, 3, 4, 5, 6][..]).unwrap();
1013+
// let slice_dst_of_u16s =
1014+
// SliceDst::<U16>::ref_from_bytes(&[0, 1, 2, 3, 4, 5, 6][..]).unwrap();
1015+
// let x: &SliceDst<U16> = transmute_ref!(slice_dst_of_u8s);
1016+
// assert_eq!(x, slice_dst_of_u16s);
1017+
1018+
// // Test that `transmute_ref!` works on slices in a const context and
1019+
// // that memory is transmuted as expected.
1020+
// const SLICE_OF_U16S: &[u16] = &[0u16, 1, 2];
1021+
// const SLICE_OF_I16S: &[i16] = &[0i16, 1, 2];
1022+
// let y: &[i16] = transmute_ref!(SLICE_OF_U16S);
1023+
// assert_eq!(Y, SLICE_OF_I16S);
1024+
1025+
// // Test that `transmute_ref!` works on slice DSTs in a const context. We
1026+
// // have no way of synthesizing slice DST values in a const context, so
1027+
// // this just tests that it compiles.
1028+
// const fn _transmute_slice_dst_ref(from: &SliceDst<[u8; 2]>) -> &SliceDst<U16> {
1029+
// transmute_ref!(from)
1030+
// }
1031+
1032+
// // Test that it's legal to transmute a reference while shrinking the
1033+
// // lifetime (note that `X` has the lifetime `'static`).
1034+
// let x: &[u8; 8] = transmute_ref!(X);
1035+
// assert_eq!(*x, ARRAY_OF_U8S);
1036+
1037+
// // Test that `transmute_ref!` supports decreasing alignment.
1038+
// let u = AU64(0);
1039+
// let array = [0, 0, 0, 0, 0, 0, 0, 0];
1040+
// let x: &[u8; 8] = transmute_ref!(&u);
1041+
// assert_eq!(*x, array);
1042+
1043+
// // Test that a mutable reference can be turned into an immutable one.
1044+
// let mut x = 0u8;
1045+
// #[allow(clippy::useless_transmute)]
1046+
// let y: &u8 = transmute_ref!(&mut x);
1047+
// assert_eq!(*y, 0);
1048+
// }
10751049

10761050
#[test]
10771051
fn test_try_transmute() {
@@ -1209,6 +1183,30 @@ mod tests {
12091183
#[allow(clippy::useless_transmute)]
12101184
let y: &u8 = transmute_mut!(&mut x);
12111185
assert_eq!(*y, 0);
1186+
1187+
// Test that `transmute_mut!` works on slice DSTs in and that memory is
1188+
// transmuted as expected.
1189+
#[derive(KnownLayout, Immutable, FromBytes, IntoBytes, PartialEq, Debug)]
1190+
#[repr(C)]
1191+
struct SliceDst<T> {
1192+
a: u8,
1193+
b: [T],
1194+
}
1195+
1196+
use crate::byteorder::native_endian::U16;
1197+
let mut bytes = [0, 1, 2, 3, 4, 5, 6];
1198+
let slice_dst_of_u8s = SliceDst::<[u8; 2]>::mut_from_bytes(&mut bytes[..]).unwrap();
1199+
let mut bytes = [0, 1, 2, 3, 4, 5, 6];
1200+
let slice_dst_of_u16s = SliceDst::<U16>::mut_from_bytes(&mut bytes[..]).unwrap();
1201+
let x: &mut SliceDst<U16> = transmute_mut!(slice_dst_of_u8s);
1202+
assert_eq!(x, slice_dst_of_u16s);
1203+
1204+
// Test that `transmute_mut!` works on slices that memory is transmuted
1205+
// as expected.
1206+
let array_of_u16s: &mut [u16] = &mut [0u16, 1, 2];
1207+
let array_of_i16s: &mut [i16] = &mut [0i16, 1, 2];
1208+
let x: &mut [i16] = transmute_mut!(array_of_u16s);
1209+
assert_eq!(x, array_of_i16s);
12121210
}
12131211

12141212
#[test]

0 commit comments

Comments
 (0)