@@ -1674,6 +1674,140 @@ macro_rules! transmute {
16741674 } }
16751675}
16761676
1677+ /// Safely transmutes a mutable or immutable reference of one type to an
1678+ /// immutable reference of another type of the same size.
1679+ ///
1680+ /// The expression `$e` must have a concrete type, `&T` or `&mut T`, where `T:
1681+ /// Sized + AsBytes`. The `transmute_ref!` expression must also have a concrete
1682+ /// type, `&U` (`U` is inferred from the calling context), where `U: Sized +
1683+ /// FromBytes`. It must be the case that `align_of::<T>() >= align_of::<U>()`.
1684+ ///
1685+ /// The lifetime of the input type, `&T` or `&mut T`, must be the same as or
1686+ /// outlive the lifetime of the output type, `&U`.
1687+ ///
1688+ /// # Alignment increase error message
1689+ ///
1690+ /// Because of limitations on macros, the error message generated when
1691+ /// `transmute_ref!` is used to transmute from a type of lower alignment to a
1692+ /// type of higher alignment is somewhat confusing. For example, the following
1693+ /// code:
1694+ ///
1695+ /// ```rust,compile_fail
1696+ /// const INCREASE_ALIGNMENT: &u16 = zerocopy::transmute_ref!(&[0u8; 2]);
1697+ /// ```
1698+ ///
1699+ /// ...generates the following error:
1700+ ///
1701+ /// ```text
1702+ /// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
1703+ /// --> src/lib.rs:1524:34
1704+ /// |
1705+ /// 5 | const INCREASE_ALIGNMENT: &u16 = zerocopy::transmute_ref!(&[0u8; 2]);
1706+ /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1707+ /// |
1708+ /// = note: source type: `MaxAlignsOf<[u8; 2], u16>` (16 bits)
1709+ /// = note: target type: `AlignOf<[u8; 2]>` (8 bits)
1710+ /// = note: this error originates in the macro `zerocopy::transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info)
1711+ /// ```
1712+ ///
1713+ /// This is saying that `max(align_of::<T>(), align_of::<U>()) !=
1714+ /// align_of::<T>()`, which is equivalent to `align_of::<T>() <
1715+ /// align_of::<U>()`.
1716+ #[ macro_export]
1717+ macro_rules! transmute_ref {
1718+ ( $e: expr) => { {
1719+ // NOTE: This must be a macro (rather than a function with trait bounds)
1720+ // because there's no way, in a generic context, to enforce that two
1721+ // types have the same size or alignment.
1722+
1723+ // Reborrow so that mutable references are supported too.
1724+ //
1725+ // In the rest of the comments, we refer only to `&T` since this
1726+ // reborrow ensures that `e` is an immutable reference.
1727+ let e = & * $e;
1728+
1729+ #[ allow( unused, clippy:: diverging_sub_expression) ]
1730+ if false {
1731+ // This branch, though never taken, ensures that the type of `e` is
1732+ // `&T` where `T: 't + Sized + AsBytes`, that the type of this macro
1733+ // expression is `&U` where `U: 'u + Sized + FromBytes`, and that
1734+ // `'t` outlives `'u`.
1735+ const fn transmute<' u, ' t: ' u, T : ' t + Sized + $crate:: AsBytes , U : ' u + Sized + $crate:: FromBytes >( _t: & ' t T ) -> & ' u U {
1736+ unreachable!( )
1737+ }
1738+ transmute( e)
1739+ } else if false {
1740+ // This branch, though never taken, ensures that `size_of::<T>() ==
1741+ // size_of::<U>()`.
1742+
1743+ // `t` is inferred to have type `T` because it's assigned to `e` (of
1744+ // type `&T`) as `&t`.
1745+ let mut t = unreachable!( ) ;
1746+ e = & t;
1747+
1748+ // `u` is inferred to have type `U` because it's used as `&u` as the
1749+ // value returned from this branch.
1750+ //
1751+ // SAFETY: This code is never run.
1752+ let u = unsafe { $crate:: macro_util:: core_reexport:: mem:: transmute( t) } ;
1753+ & u
1754+ } else if false {
1755+ // This branch, though never taken, ensures that the alignment of
1756+ // `T` is greater than or equal to to the alignment of `U`.
1757+
1758+ // `t` is inferred to have type `T` because it's assigned to `e` (of
1759+ // type `&T`) as `&t`.
1760+ let mut t = unreachable!( ) ;
1761+ e = & t;
1762+
1763+ // `u` is inferred to have type `U` because it's used as `&u` as the
1764+ // value returned from this branch.
1765+ let mut u = unreachable!( ) ;
1766+
1767+ // `max_aligns` is inferred to have type `MaxAlignsOf<T, U>` because
1768+ // of the inferred types of `t` and `u`.
1769+ let max_aligns = $crate:: macro_util:: MaxAlignsOf :: new( t, u) ;
1770+ // The type wildcard in this bound is inferred to be `T` because
1771+ // `align_of.into_t()` is assigned to `t` (which has type `T`).
1772+ //
1773+ // This transmute will only compile successfully if
1774+ // `max(align_of::<T>(), align_of::<U>()) == align_of::<T>()` - in
1775+ // other words, if `align_of::<T>() >= align_of::<U>()`.
1776+ //
1777+ // SAFETY: This code is never run.
1778+ let align_of: $crate:: macro_util:: AlignOf <_> = unsafe { $crate:: macro_util:: core_reexport:: mem:: transmute( max_aligns) } ;
1779+ t = align_of. into_t( ) ;
1780+
1781+ & u
1782+ } else {
1783+ // SAFETY:
1784+ // - We know that the input and output types are both `Sized` (ie,
1785+ // thin) references thanks to the trait bounds on `transmute`
1786+ // above, and thanks to the fact that transmute takes and returns
1787+ // references.
1788+ // - We know that it is sound to view the target type of the input
1789+ // reference (`T`) as the target type of the output reference
1790+ // (`U`) because `T: AsBytes` and `U: FromBytes` (guaranteed by
1791+ // trait bounds on `transmute`) and because `size_of::<T>() ==
1792+ // size_of::<U>()` (guaranteed by the first `core::mem::transmute`
1793+ // above).
1794+ // - We know that alignment is not increased thanks to the second
1795+ // `core::mem::transmute` above (the one which transmutes
1796+ // `MaxAlignsOf` into `AlignOf`).
1797+ //
1798+ // We use this reexport of `core::mem::transmute` because we know it
1799+ // will always be available for crates which are using the 2015
1800+ // edition of Rust. By contrast, if we were to use
1801+ // `std::mem::transmute`, this macro would not work for such crates
1802+ // in `no_std` contexts, and if we were to use
1803+ // `core::mem::transmute`, this macro would not work in `std`
1804+ // contexts in which `core` was not manually imported. This is not a
1805+ // problem for 2018 edition crates.
1806+ unsafe { $crate:: macro_util:: core_reexport:: mem:: transmute( e) }
1807+ }
1808+ } }
1809+ }
1810+
16771811/// A typed reference derived from a byte slice.
16781812///
16791813/// A `Ref<B, T>` is a reference to a `T` which is stored in a byte slice, `B`.
@@ -3810,6 +3944,92 @@ mod tests {
38103944 assert_eq ! ( X , ARRAY_OF_ARRAYS ) ;
38113945 }
38123946
3947+ #[ test]
3948+ fn test_align_of ( ) {
3949+ macro_rules! test {
3950+ ( $ty: ty) => {
3951+ assert_eq!( mem:: size_of:: <macro_util:: AlignOf <$ty>>( ) , mem:: align_of:: <$ty>( ) ) ;
3952+ } ;
3953+ }
3954+
3955+ test ! ( ( ) ) ;
3956+ test ! ( u8 ) ;
3957+ test ! ( AU64 ) ;
3958+ test ! ( [ AU64 ; 2 ] ) ;
3959+ }
3960+
3961+ #[ test]
3962+ fn test_max_aligns_of ( ) {
3963+ macro_rules! test {
3964+ ( $t: ty, $u: ty) => {
3965+ assert_eq!(
3966+ mem:: size_of:: <macro_util:: MaxAlignsOf <$t, $u>>( ) ,
3967+ core:: cmp:: max( mem:: align_of:: <$t>( ) , mem:: align_of:: <$u>( ) )
3968+ ) ;
3969+ } ;
3970+ }
3971+
3972+ test ! ( u8 , u8 ) ;
3973+ test ! ( u8 , AU64 ) ;
3974+ test ! ( AU64 , u8 ) ;
3975+ }
3976+
3977+ #[ test]
3978+ fn test_typed_align_check ( ) {
3979+ // Test that the type-based alignment check used in `transmute_ref!`
3980+ // behaves as expected.
3981+
3982+ macro_rules! assert_t_align_gteq_u_align {
3983+ ( $t: ty, $u: ty, $gteq: expr) => {
3984+ assert_eq!(
3985+ mem:: size_of:: <macro_util:: MaxAlignsOf <$t, $u>>( )
3986+ == mem:: size_of:: <macro_util:: AlignOf <$t>>( ) ,
3987+ $gteq
3988+ ) ;
3989+ } ;
3990+ }
3991+
3992+ assert_t_align_gteq_u_align ! ( u8 , u8 , true ) ;
3993+ assert_t_align_gteq_u_align ! ( AU64 , AU64 , true ) ;
3994+ assert_t_align_gteq_u_align ! ( AU64 , u8 , true ) ;
3995+ assert_t_align_gteq_u_align ! ( u8 , AU64 , false ) ;
3996+ }
3997+
3998+ #[ test]
3999+ fn test_transmute_ref ( ) {
4000+ // Test that memory is transmuted as expected.
4001+ let array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
4002+ let array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
4003+ let x: & [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & array_of_u8s) ;
4004+ assert_eq ! ( * x, array_of_arrays) ;
4005+ let x: & [ u8 ; 8 ] = transmute_ref ! ( & array_of_arrays) ;
4006+ assert_eq ! ( * x, array_of_u8s) ;
4007+
4008+ // Test that `transmute_ref!` is legal in a const context.
4009+ const ARRAY_OF_U8S : [ u8 ; 8 ] = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
4010+ const ARRAY_OF_ARRAYS : [ [ u8 ; 2 ] ; 4 ] = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
4011+ #[ allow( clippy:: redundant_static_lifetimes) ]
4012+ const X : & ' static [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & ARRAY_OF_U8S ) ;
4013+ assert_eq ! ( * X , ARRAY_OF_ARRAYS ) ;
4014+
4015+ // Test that it's legal to transmute a reference while shrinking the
4016+ // lifetime (note that `X` has the lifetime `'static`).
4017+ let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
4018+ assert_eq ! ( * x, ARRAY_OF_U8S ) ;
4019+
4020+ // Test that `transmute_ref!` supports decreasing alignment.
4021+ let u = AU64 ( 0 ) ;
4022+ let array = [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
4023+ let x: & [ u8 ; 8 ] = transmute_ref ! ( & u) ;
4024+ assert_eq ! ( * x, array) ;
4025+
4026+ // Test that a mutable reference can be turned into an immutable one.
4027+ let mut x = 0u8 ;
4028+ #[ allow( clippy:: useless_transmute) ]
4029+ let y: & u8 = transmute_ref ! ( & mut x) ;
4030+ assert_eq ! ( * y, 0 ) ;
4031+ }
4032+
38134033 #[ test]
38144034 fn test_address ( ) {
38154035 // Test that the `Deref` and `DerefMut` implementations return a
0 commit comments