Skip to content

Commit 0340422

Browse files
committed
Support sized-to-unsized transmute_{ref,mut}!
Closes #2721 gherrit-pr-id: G73f67c103188ed404d0051bc140c4d0711fe0753
1 parent 0ded8bf commit 0340422

2 files changed

Lines changed: 62 additions & 5 deletions

File tree

src/macros.rs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ macro_rules! transmute {
184184
}};
185185
}
186186

187+
// TODO: Update these docs to mention sized -> unsized.
188+
187189
/// Safely transmutes a mutable or immutable reference of one type to an
188190
/// immutable reference of another type of the same size and compatible
189191
/// alignment.
@@ -351,13 +353,35 @@ macro_rules! transmute_ref {
351353
// SAFETY: The `if false` branch ensures that:
352354
// - `Src: IntoBytes + Immutable`
353355
// - `Dst: FromBytes + Immutable`
354-
unsafe {
355-
t.transmute_ref()
356+
if false {
357+
// This branch exists solely to force the compiler to infer the
358+
// type of `Dst` *before* it attempts to resolve the method call
359+
// to `transmute_ref` in the `else` branch.
360+
//
361+
// Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
362+
// compiler will eagerly select the inherent impl of
363+
// `transmute_ref` (which requires `Dst: Sized`) because inherent
364+
// methods take priority over trait methods. It does this before
365+
// it realizes `Dst` is `!Sized`, leading to a compile error when
366+
// it checks the bounds later.
367+
//
368+
// By calling this helper (which returns `&Dst`), we force `Dst`
369+
// to be fully resolved. By the time it gets to the `else`
370+
// branch, the compiler knows `Dst` is `!Sized`, properly
371+
// disqualifies the inherent method, and falls back to the trait
372+
// implementation.
373+
t.transmute_ref_inference_helper()
374+
} else {
375+
unsafe {
376+
t.transmute_ref()
377+
}
356378
}
357379
}
358380
}}
359381
}
360382

383+
// TODO: Update these docs to mention sized -> unsized.
384+
361385
/// Safely transmutes a mutable reference of one type to a mutable reference of
362386
/// another type of the same size and compatible alignment.
363387
///
@@ -503,7 +527,26 @@ macro_rules! transmute_mut {
503527
#[allow(unused)]
504528
use $crate::util::macro_util::TransmuteMutDst as _;
505529
let t = $crate::util::macro_util::Wrap::new(e);
506-
t.transmute_mut()
530+
if false {
531+
// This branch exists solely to force the compiler to infer the type
532+
// of `Dst` *before* it attempts to resolve the method call to
533+
// `transmute_mut` in the `else` branch.
534+
//
535+
// Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
536+
// compiler will eagerly select the inherent impl of `transmute_mut`
537+
// (which requires `Dst: Sized`) because inherent methods take
538+
// priority over trait methods. It does this before it realizes
539+
// `Dst` is `!Sized`, leading to a compile error when it checks the
540+
// bounds later.
541+
//
542+
// By calling this helper (which returns `&mut Dst`), we force `Dst`
543+
// to be fully resolved. By the time it gets to the `else` branch,
544+
// the compiler knows `Dst` is `!Sized`, properly disqualifies the
545+
// inherent method, and falls back to the trait implementation.
546+
t.transmute_mut_inference_helper()
547+
} else {
548+
t.transmute_mut()
549+
}
507550
}}
508551
}
509552

@@ -1241,6 +1284,13 @@ mod tests {
12411284
const X: &'static [[u8; 2]; 4] = transmute_ref!(&ARRAY_OF_U8S);
12421285
assert_eq!(*X, ARRAY_OF_ARRAYS);
12431286

1287+
// Test sized -> unsized transmutation.
1288+
let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
1289+
let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
1290+
let slice_of_arrays = &array_of_arrays[..];
1291+
let x: &[[u8; 2]] = transmute_ref!(&array_of_u8s);
1292+
assert_eq!(x, slice_of_arrays);
1293+
12441294
// Before 1.61.0, we can't define the `const fn transmute_ref` function
12451295
// that we do on and after 1.61.0.
12461296
#[cfg(no_zerocopy_generic_bounds_in_const_fn_1_61_0)]
@@ -1532,6 +1582,13 @@ mod tests {
15321582
let slice_dst_small = SliceDst::<U16, u8>::mut_from_bytes(&mut bytes[..]).unwrap();
15331583
let x: &mut SliceDst<U16, u8> = transmute_mut!(slice_dst_big);
15341584
assert_eq!(x, slice_dst_small);
1585+
1586+
// Test sized -> unsized transmutation.
1587+
let mut array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
1588+
let mut array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
1589+
let slice_of_arrays = &mut array_of_arrays[..];
1590+
let x: &mut [[u8; 2]] = transmute_mut!(&mut array_of_u8s);
1591+
assert_eq!(x, slice_of_arrays);
15351592
}
15361593

15371594
#[test]

src/util/macro_util.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ pub trait TransmuteRefDst<'a> {
933933

934934
impl<'a, Src: ?Sized, Dst: ?Sized> TransmuteRefDst<'a> for Wrap<&'a Src, &'a Dst>
935935
where
936-
Src: KnownLayout<PointerMetadata = usize> + IntoBytes + Immutable,
936+
Src: KnownLayout + IntoBytes + Immutable,
937937
Dst: KnownLayout<PointerMetadata = usize> + FromBytes + Immutable,
938938
{
939939
type Dst = Dst;
@@ -966,7 +966,7 @@ pub trait TransmuteMutDst<'a> {
966966

967967
impl<'a, Src: ?Sized, Dst: ?Sized> TransmuteMutDst<'a> for Wrap<&'a mut Src, &'a mut Dst>
968968
where
969-
Src: KnownLayout<PointerMetadata = usize> + FromBytes + IntoBytes,
969+
Src: KnownLayout + FromBytes + IntoBytes,
970970
Dst: KnownLayout<PointerMetadata = usize> + FromBytes + IntoBytes,
971971
{
972972
type Dst = Dst;

0 commit comments

Comments
 (0)