@@ -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 {
@@ -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
+ /// ```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
+ loop { }
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,58 @@ mod tests {
3810
3944
assert_eq ! ( X , ARRAY_OF_ARRAYS ) ;
3811
3945
}
3812
3946
3947
+ #[ test]
3948
+ fn test_transmute_ref ( ) {
3949
+ // Test that memory is transmuted as expected.
3950
+ let array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
3951
+ let array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
3952
+ let x: & [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & array_of_u8s) ;
3953
+ assert_eq ! ( * x, array_of_arrays) ;
3954
+ let x: & [ u8 ; 8 ] = transmute_ref ! ( & array_of_arrays) ;
3955
+ assert_eq ! ( * x, array_of_u8s) ;
3956
+
3957
+ // Test that `transmute_ref!` is legal in a const context.
3958
+ const ARRAY_OF_U8S : [ u8 ; 8 ] = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
3959
+ const ARRAY_OF_ARRAYS : [ [ u8 ; 2 ] ; 4 ] = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
3960
+ #[ allow( clippy:: redundant_static_lifetimes) ]
3961
+ const X : & ' static [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & ARRAY_OF_U8S ) ;
3962
+ assert_eq ! ( * X , ARRAY_OF_ARRAYS ) ;
3963
+
3964
+ // Test that it's legal to transmute a reference while shrinking the
3965
+ // lifetime (note that `X` has the lifetime `'static`).
3966
+ let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
3967
+ assert_eq ! ( * x, ARRAY_OF_U8S ) ;
3968
+
3969
+ // Test that `transmute_ref!` supports decreasing alignment.
3970
+ let u = AU64 ( 0 ) ;
3971
+ let array = [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
3972
+ let x: & [ u8 ; 8 ] = transmute_ref ! ( & u) ;
3973
+ assert_eq ! ( * x, array) ;
3974
+
3975
+ // Test that a mutable reference can be turned into an immutable one.
3976
+ let mut x = 0u8 ;
3977
+ #[ allow( clippy:: useless_transmute) ]
3978
+ let y: & u8 = transmute_ref ! ( & mut x) ;
3979
+ assert_eq ! ( * y, 0 ) ;
3980
+ }
3981
+
3982
+ #[ test]
3983
+ fn test_macros_evaluate_args_once ( ) {
3984
+ let mut ctr = 0 ;
3985
+ let _: usize = transmute ! ( {
3986
+ ctr += 1 ;
3987
+ 0usize
3988
+ } ) ;
3989
+ assert_eq ! ( ctr, 1 ) ;
3990
+
3991
+ let mut ctr = 0 ;
3992
+ let _: & usize = transmute_ref ! ( {
3993
+ ctr += 1 ;
3994
+ & 0usize
3995
+ } ) ;
3996
+ assert_eq ! ( ctr, 1 ) ;
3997
+ }
3998
+
3813
3999
#[ test]
3814
4000
fn test_address ( ) {
3815
4001
// Test that the `Deref` and `DerefMut` implementations return a
0 commit comments