@@ -1674,6 +1674,140 @@ macro_rules! transmute {
1674
1674
} }
1675
1675
}
1676
1676
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
+
1677
1811
/// A typed reference derived from a byte slice.
1678
1812
///
1679
1813
/// A `Ref<B, T>` is a reference to a `T` which is stored in a byte slice, `B`.
@@ -3810,6 +3944,92 @@ mod tests {
3810
3944
assert_eq ! ( X , ARRAY_OF_ARRAYS ) ;
3811
3945
}
3812
3946
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
+
3813
4033
#[ test]
3814
4034
fn test_address ( ) {
3815
4035
// Test that the `Deref` and `DerefMut` implementations return a
0 commit comments