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