@@ -1463,6 +1463,96 @@ macro_rules! transmute {
14631463 } }
14641464}
14651465
1466+ /// A type whose size is equal to `mem::align_of::<T>()`.
1467+ ///
1468+ /// This type is used internally in `transmute_ref!`, and so needs to be
1469+ /// exported. It is not intended for direct use by users of zerocopy, and so is
1470+ /// `#[doc(hidden)]`.
1471+ #[ doc( hidden) ]
1472+ #[ allow( missing_debug_implementations) ]
1473+ #[ repr( C ) ]
1474+ pub struct SizeIsAlign < T > {
1475+ // This field ensures that:
1476+ // - The size is always at least 1 (the minimum possible alignment).
1477+ // - If the alignment is greater than 1, Rust has to round up to the next
1478+ // multiple of it in order to make sure that `SizeIsAlign`'s size is a
1479+ // multiple of that alignment. Without this field, its size could be 0,
1480+ // which is a valid multiple of any alignment.
1481+ _u : u8 ,
1482+ _a : [ T ; 0 ] ,
1483+ }
1484+
1485+ impl < T > SizeIsAlign < T > {
1486+ #[ doc( hidden) ]
1487+ pub fn new ( _t : T ) -> SizeIsAlign < T > {
1488+ SizeIsAlign { _u : 0 , _a : [ ] }
1489+ }
1490+
1491+ #[ doc( hidden) ]
1492+ pub fn into_t ( self ) -> T {
1493+ unreachable ! ( )
1494+ }
1495+ }
1496+
1497+ /// Safely transmutes an immutable reference of one type to an immutable
1498+ /// reference of another type of the same size and alignment.
1499+ ///
1500+ /// The expression `$e` must have a concrete type, `&T`, where `T: Sized +
1501+ /// AsBytes`. The `transmute_ref!` expression must also have a concrete type,
1502+ /// `&U` (`U` is inferred from the calling context), where `U: Sized +
1503+ /// FromBytes`.
1504+ ///
1505+ /// The lifetime of the input type, `&T`, must be the same as or outlive the
1506+ /// lifetime of the output type, `&U`.
1507+ #[ macro_export]
1508+ macro_rules! transmute_ref {
1509+ ( $e: expr) => { {
1510+ // NOTE: This must be a macro (rather than a function with trait bounds)
1511+ // because there's no way, in a generic context, to enforce that two
1512+ // types have the same size or alignment. `core::mem::transmute` uses
1513+ // compiler magic to enforce size equality so long as the types are
1514+ // concrete. We use `SizeIsAlign` to create a type whose size is equal
1515+ // to the alignment of another type so that we can use `transmute` to
1516+ // check alignment as well.
1517+
1518+ let e = $e;
1519+ #[ allow( unused, clippy:: diverging_sub_expression) ]
1520+ if false {
1521+ // This branch, though never taken, ensures that the type of `e` is
1522+ // `&T` where `T: 't + Sized + AsBytes`, that the type of this macro
1523+ // expression is `&U` where `U: 'u + Sized + FromBytes`, and that
1524+ // `'t` outlives `'u`.
1525+ const fn transmute<' u, ' t: ' u, T : ' t + Sized + $crate:: AsBytes , U : ' u + Sized + $crate:: FromBytes >( _t: & ' t T ) -> & ' u U {
1526+ unreachable!( )
1527+ }
1528+ transmute( e)
1529+ } else if false {
1530+ // This branch, though never taken, ensures that the alignment of
1531+ // `T` is equal to the alignment of `U`.
1532+ let target = unreachable!( ) ;
1533+ e = & target;
1534+
1535+ // SAFETY: This code is never executed.
1536+ let ret: $crate:: SizeIsAlign <_> = unsafe { $crate:: __real_transmute( $crate:: SizeIsAlign :: new( target) ) } ;
1537+ & ret. into_t( )
1538+ } else {
1539+ // SAFETY: `core::mem::transmute` ensures that the type of `e` and
1540+ // the type of this macro invocation expression have the same size.
1541+ // We know this transmute is safe thanks to the `AsBytes` and
1542+ // `FromBytes` bounds enforced by the `false` branch.
1543+ //
1544+ // We use `$crate::__real_transmute` because we know it will always
1545+ // be available for crates which are using the 2015 edition of Rust.
1546+ // By contrast, if we were to use `std::mem::transmute`, this macro
1547+ // would not work for such crates in `no_std` contexts, and if we
1548+ // were to use `core::mem::transmute`, this macro would not work in
1549+ // `std` contexts in which `core` was not manually imported. This is
1550+ // not a problem for 2018 edition crates.
1551+ unsafe { $crate:: __real_transmute( e) }
1552+ }
1553+ } }
1554+ }
1555+
14661556/// A length- and alignment-checked reference to a byte slice which can safely
14671557/// be reinterpreted as another type.
14681558///
@@ -3195,6 +3285,43 @@ mod tests {
31953285 assert_eq ! ( X , ARRAY_OF_ARRAYS ) ;
31963286 }
31973287
3288+ #[ test]
3289+ fn test_size_is_align ( ) {
3290+ macro_rules! test {
3291+ ( $ty: ty) => {
3292+ assert_eq!( mem:: size_of:: <SizeIsAlign <$ty>>( ) , mem:: align_of:: <$ty>( ) ) ;
3293+ } ;
3294+ }
3295+
3296+ test ! ( ( ) ) ;
3297+ test ! ( u8 ) ;
3298+ test ! ( AU64 ) ;
3299+ test ! ( [ AU64 ; 2 ] ) ;
3300+ }
3301+
3302+ #[ test]
3303+ fn test_transmute_ref ( ) {
3304+ // Test that memory is transmuted as expected.
3305+ let array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
3306+ let array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
3307+ let x: & [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & array_of_u8s) ;
3308+ assert_eq ! ( * x, array_of_arrays) ;
3309+ let x: & [ u8 ; 8 ] = transmute_ref ! ( & array_of_arrays) ;
3310+ assert_eq ! ( * x, array_of_u8s) ;
3311+
3312+ // Test that `transmute_ref!` is legal in a const context.
3313+ const ARRAY_OF_U8S : [ u8 ; 8 ] = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
3314+ const ARRAY_OF_ARRAYS : [ [ u8 ; 2 ] ; 4 ] = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
3315+ #[ allow( clippy:: redundant_static_lifetimes) ]
3316+ const X : & ' static [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & ARRAY_OF_U8S ) ;
3317+ assert_eq ! ( * X , ARRAY_OF_ARRAYS ) ;
3318+
3319+ // Test that it's legal to transmute a reference while shrinking the
3320+ // lifetime (note that `X` has the lifetime `'static`).
3321+ let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
3322+ assert_eq ! ( * x, ARRAY_OF_U8S ) ;
3323+ }
3324+
31983325 #[ test]
31993326 fn test_address ( ) {
32003327 // Test that the `Deref` and `DerefMut` implementations return a
0 commit comments