Skip to content

Commit 4689264

Browse files
committed
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 4d09d7c commit 4689264

File tree

3 files changed

+369
-138
lines changed

3 files changed

+369
-138
lines changed

src/macros.rs

+90-69
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,6 +981,18 @@ mod tests {
1038981
assert_eq!(x.into_inner(), 1);
1039982
}
1040983

984+
// A `Sized` type which doesn't implement `KnownLayout` (it is "not
985+
// `KnownLayout`", or `Nkl`).
986+
//
987+
// This permits us to test that `transmute_ref!` and `transmute_mut!` work
988+
// for types which are `Sized + !KnownLayout`. When we added support for
989+
// slice DSTs in #1924, this new support relied on `KnownLayout`, but we
990+
// need to make sure to remain backwards-compatible with code which ueses
991+
// these macros with types which are `!KnownLayout`.
992+
#[derive(FromBytes, IntoBytes, Immutable, PartialEq, Eq, Debug)]
993+
#[repr(transparent)]
994+
struct Nkl<T>(T);
995+
1041996
#[test]
1042997
fn test_transmute_ref() {
1043998
// Test that memory is transmuted as expected.
@@ -1055,6 +1010,39 @@ mod tests {
10551010
const X: &'static [[u8; 2]; 4] = transmute_ref!(&ARRAY_OF_U8S);
10561011
assert_eq!(*X, ARRAY_OF_ARRAYS);
10571012

1013+
// Call through a generic function to make sure our autoref
1014+
// specialization trick works even when types are generic.
1015+
const fn transmute_ref<T, U>(t: &T) -> &U
1016+
where
1017+
T: IntoBytes + Immutable,
1018+
U: FromBytes + Immutable,
1019+
{
1020+
transmute_ref!(t)
1021+
}
1022+
1023+
// Test that `transmute_ref!` supports non-`KnownLayout` `Sized` types.
1024+
const ARRAY_OF_NKL_U8S: Nkl<[u8; 8]> = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
1025+
const ARRAY_OF_NKL_ARRAYS: Nkl<[[u8; 2]; 4]> = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
1026+
const X_NKL: &Nkl<[[u8; 2]; 4]> = transmute_ref(&ARRAY_OF_NKL_U8S);
1027+
assert_eq!(*X_NKL, ARRAY_OF_NKL_ARRAYS);
1028+
1029+
// Test that `transmute_ref!` works on slice DSTs in and that memory is
1030+
// transmuted as expected.
1031+
#[derive(KnownLayout, Immutable, FromBytes, IntoBytes, PartialEq, Debug)]
1032+
#[repr(C)]
1033+
struct SliceDst<T> {
1034+
a: u8,
1035+
b: [T],
1036+
}
1037+
1038+
use crate::byteorder::native_endian::U16;
1039+
let slice_dst_of_u8s =
1040+
SliceDst::<[u8; 2]>::ref_from_bytes(&[0, 1, 2, 3, 4, 5, 6][..]).unwrap();
1041+
let slice_dst_of_u16s =
1042+
SliceDst::<U16>::ref_from_bytes(&[0, 1, 2, 3, 4, 5, 6][..]).unwrap();
1043+
let x: &SliceDst<U16> = transmute_ref!(slice_dst_of_u8s);
1044+
assert_eq!(x, slice_dst_of_u16s);
1045+
10581046
// Test that it's legal to transmute a reference while shrinking the
10591047
// lifetime (note that `X` has the lifetime `'static`).
10601048
let x: &[u8; 8] = transmute_ref!(X);
@@ -1198,6 +1186,15 @@ mod tests {
11981186
let x: &mut [u8; 8] = transmute_mut!(&mut array_of_arrays);
11991187
assert_eq!(*x, array_of_u8s);
12001188
}
1189+
1190+
// Test that `transmute_mut!` supports non-`KnownLayout` types.
1191+
let mut array_of_u8s = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
1192+
let mut array_of_arrays = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
1193+
let x: &mut Nkl<[[u8; 2]; 4]> = transmute_mut!(&mut array_of_u8s);
1194+
assert_eq!(*x, array_of_arrays);
1195+
let x: &mut Nkl<[u8; 8]> = transmute_mut!(&mut array_of_arrays);
1196+
assert_eq!(*x, array_of_u8s);
1197+
12011198
// Test that `transmute_mut!` supports decreasing alignment.
12021199
let mut u = AU64(0);
12031200
let array = [0, 0, 0, 0, 0, 0, 0, 0];
@@ -1209,6 +1206,30 @@ mod tests {
12091206
#[allow(clippy::useless_transmute)]
12101207
let y: &u8 = transmute_mut!(&mut x);
12111208
assert_eq!(*y, 0);
1209+
1210+
// Test that `transmute_mut!` works on slice DSTs in and that memory is
1211+
// transmuted as expected.
1212+
#[derive(KnownLayout, Immutable, FromBytes, IntoBytes, PartialEq, Debug)]
1213+
#[repr(C)]
1214+
struct SliceDst<T> {
1215+
a: u8,
1216+
b: [T],
1217+
}
1218+
1219+
use crate::byteorder::native_endian::U16;
1220+
let mut bytes = [0, 1, 2, 3, 4, 5, 6];
1221+
let slice_dst_of_u8s = SliceDst::<[u8; 2]>::mut_from_bytes(&mut bytes[..]).unwrap();
1222+
let mut bytes = [0, 1, 2, 3, 4, 5, 6];
1223+
let slice_dst_of_u16s = SliceDst::<U16>::mut_from_bytes(&mut bytes[..]).unwrap();
1224+
let x: &mut SliceDst<U16> = transmute_mut!(slice_dst_of_u8s);
1225+
assert_eq!(x, slice_dst_of_u16s);
1226+
1227+
// Test that `transmute_mut!` works on slices that memory is transmuted
1228+
// as expected.
1229+
let array_of_u16s: &mut [u16] = &mut [0u16, 1, 2];
1230+
let array_of_i16s: &mut [i16] = &mut [0i16, 1, 2];
1231+
let x: &mut [i16] = transmute_mut!(array_of_u16s);
1232+
assert_eq!(x, array_of_i16s);
12121233
}
12131234

12141235
#[test]

0 commit comments

Comments
 (0)