@@ -210,10 +210,11 @@ macro_rules! transmute {
210210///
211211/// # Size compatibility
212212///
213- /// `transmute_ref!` supports transmuting between `Sized` types or between
214- /// unsized (i.e., `?Sized`) types. It supports any transmutation that preserves
215- /// the number of bytes of the referent, even if doing so requires updating the
216- /// metadata stored in an unsized "fat" reference:
213+ /// `transmute_ref!` supports transmuting between `Sized` types, between unsized
214+ /// (i.e., `?Sized`) types, and from a `Sized` type to an unsized type. It
215+ /// supports any transmutation that preserves the number of bytes of the
216+ /// referent, even if doing so requires updating the metadata stored in an
217+ /// unsized "fat" reference:
217218///
218219/// ```
219220/// # use zerocopy::transmute_ref;
@@ -348,11 +349,32 @@ macro_rules! transmute_ref {
348349 } else {
349350 use $crate:: util:: macro_util:: TransmuteRefDst ;
350351 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
351- // SAFETY: The `if false` branch ensures that:
352- // - `Src: IntoBytes + Immutable`
353- // - `Dst: FromBytes + Immutable`
354- unsafe {
355- t. transmute_ref( )
352+
353+ if false {
354+ // This branch exists solely to force the compiler to infer the
355+ // type of `Dst` *before* it attempts to resolve the method call
356+ // to `transmute_ref` in the `else` branch.
357+ //
358+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
359+ // compiler will eagerly select the inherent impl of
360+ // `transmute_ref` (which requires `Dst: Sized`) because inherent
361+ // methods take priority over trait methods. It does this before
362+ // it realizes `Dst` is `!Sized`, leading to a compile error when
363+ // it checks the bounds later.
364+ //
365+ // By calling this helper (which returns `&Dst`), we force `Dst`
366+ // to be fully resolved. By the time it gets to the `else`
367+ // branch, the compiler knows `Dst` is `!Sized`, properly
368+ // disqualifies the inherent method, and falls back to the trait
369+ // implementation.
370+ t. transmute_ref_inference_helper( )
371+ } else {
372+ // SAFETY: The outer `if false` branch ensures that:
373+ // - `Src: IntoBytes + Immutable`
374+ // - `Dst: FromBytes + Immutable`
375+ unsafe {
376+ t. transmute_ref( )
377+ }
356378 }
357379 }
358380 } }
@@ -383,10 +405,11 @@ macro_rules! transmute_ref {
383405///
384406/// # Size compatibility
385407///
386- /// `transmute_mut!` supports transmuting between `Sized` types or between
387- /// unsized (i.e., `?Sized`) types. It supports any transmutation that preserves
388- /// the number of bytes of the referent, even if doing so requires updating the
389- /// metadata stored in an unsized "fat" reference:
408+ /// `transmute_mut!` supports transmuting between `Sized` types, between unsized
409+ /// (i.e., `?Sized`) types, and from a `Sized` type to an unsized type. It
410+ /// supports any transmutation that preserves the number of bytes of the
411+ /// referent, even if doing so requires updating the metadata stored in an
412+ /// unsized "fat" reference:
390413///
391414/// ```
392415/// # use zerocopy::transmute_mut;
@@ -503,7 +526,26 @@ macro_rules! transmute_mut {
503526 #[ allow( unused) ]
504527 use $crate:: util:: macro_util:: TransmuteMutDst as _;
505528 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
506- t. transmute_mut( )
529+ if false {
530+ // This branch exists solely to force the compiler to infer the type
531+ // of `Dst` *before* it attempts to resolve the method call to
532+ // `transmute_mut` in the `else` branch.
533+ //
534+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
535+ // compiler will eagerly select the inherent impl of `transmute_mut`
536+ // (which requires `Dst: Sized`) because inherent methods take
537+ // priority over trait methods. It does this before it realizes
538+ // `Dst` is `!Sized`, leading to a compile error when it checks the
539+ // bounds later.
540+ //
541+ // By calling this helper (which returns `&mut Dst`), we force `Dst`
542+ // to be fully resolved. By the time it gets to the `else` branch,
543+ // the compiler knows `Dst` is `!Sized`, properly disqualifies the
544+ // inherent method, and falls back to the trait implementation.
545+ t. transmute_mut_inference_helper( )
546+ } else {
547+ t. transmute_mut( )
548+ }
507549 } }
508550}
509551
@@ -1196,6 +1238,13 @@ mod tests {
11961238 const X : & ' static [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & ARRAY_OF_U8S ) ;
11971239 assert_eq ! ( * X , ARRAY_OF_ARRAYS ) ;
11981240
1241+ // Test sized -> unsized transmutation.
1242+ let array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1243+ let array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
1244+ let slice_of_arrays = & array_of_arrays[ ..] ;
1245+ let x: & [ [ u8 ; 2 ] ] = transmute_ref ! ( & array_of_u8s) ;
1246+ assert_eq ! ( x, slice_of_arrays) ;
1247+
11991248 // Before 1.61.0, we can't define the `const fn transmute_ref` function
12001249 // that we do on and after 1.61.0.
12011250 #[ cfg( no_zerocopy_generic_bounds_in_const_fn_1_61_0) ]
@@ -1445,6 +1494,13 @@ mod tests {
14451494 let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
14461495 let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
14471496 assert_eq ! ( x, slice_dst_small) ;
1497+
1498+ // Test sized -> unsized transmutation.
1499+ let mut array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1500+ let mut array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
1501+ let slice_of_arrays = & mut array_of_arrays[ ..] ;
1502+ let x: & mut [ [ u8 ; 2 ] ] = transmute_mut ! ( & mut array_of_u8s) ;
1503+ assert_eq ! ( x, slice_of_arrays) ;
14481504 }
14491505
14501506 #[ test]
0 commit comments