3535/// original `Src` will be forgotten, and the value of type `Dst` will be
3636/// returned.
3737///
38+ /// # `#![allow(shrink)]`
39+ ///
40+ /// If `#![allow(shrink)]` is provided, `transmute!` additionally supports
41+ /// transmutations that shrink the size of the referent; e.g.:
42+ ///
43+ /// ```
44+ /// # use zerocopy::transmute;
45+ /// let u: u32 = transmute!(#![allow(shrink)] 0u64);
46+ /// assert_eq!(u, 0u32);
47+ /// ```
48+ ///
3849/// # Examples
3950///
4051/// ```
5162/// This macro can be invoked in `const` contexts.
5263#[ macro_export]
5364macro_rules! transmute {
54- ( $e: expr) => { {
55- // NOTE: This must be a macro (rather than a function with trait bounds)
56- // because there's no way, in a generic context, to enforce that two
57- // types have the same size. `core::mem::transmute` uses compiler magic
58- // to enforce this so long as the types are concrete.
65+ // NOTE: This must be a macro (rather than a function with trait bounds)
66+ // because there's no way, in a generic context, to enforce that two types
67+ // have the same size. `core::mem::transmute` uses compiler magic to enforce
68+ // this so long as the types are concrete.
69+ ( #![ allow( shrink) ] $e: expr) => { {
70+ let mut e = $e;
71+ if false {
72+ // This branch, though never taken, ensures that the type of `e` is
73+ // `IntoBytes` and that the type of the outer macro invocation
74+ // expression is `FromBytes`.
75+
76+ fn transmute<Src , Dst >( src: Src ) -> Dst
77+ where
78+ Src : $crate:: IntoBytes ,
79+ Dst : $crate:: FromBytes ,
80+ {
81+ let _ = src;
82+ loop { }
83+ }
84+ loop { }
85+ #[ allow( unreachable_code) ]
86+ transmute( e)
87+ } else {
88+ use $crate:: util:: macro_util:: core_reexport:: mem:: ManuallyDrop ;
5989
90+ #[ repr( C , packed) ]
91+ union Transmute <Src , Dst > {
92+ src: ManuallyDrop <Src >,
93+ dst: ManuallyDrop <Dst >,
94+ }
95+
96+ // SAFETY: `Transmute` is a `reper(C)` union whose `src` field has
97+ // type `ManuallyDrop<Src>`. Thus, the `src` field starts at byte
98+ // offset 0 within `Transmute` [1]. `ManuallyDrop<T>` has the same
99+ // layout and bit validity as `T`, so it is sound to transmute `Src`
100+ // to `Transmute`.
101+ //
102+ // [1] https://doc.rust-lang.org/1.85.0/reference/type-layout.html#reprc-unions
103+ //
104+ // [2] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
105+ //
106+ // `ManuallyDrop<T>` is guaranteed to have the same layout and bit
107+ // validity as `T`
108+ let u: Transmute <_, _> = unsafe {
109+ // Clippy: We can't annotate the types; this macro is designed
110+ // to infer the types from the calling context.
111+ #[ allow( clippy:: missing_transmute_annotations) ]
112+ $crate:: util:: macro_util:: core_reexport:: mem:: transmute( e)
113+ } ;
114+
115+ if false {
116+ // SAFETY: This code is never executed.
117+ e = ManuallyDrop :: into_inner( unsafe { u. src } ) ;
118+ // Suppress the `unused_assignments` lint on the previous line.
119+ let _ = e;
120+ loop { }
121+ } else {
122+ // Per the safety comment on `let u` above, the `dst` field in
123+ // `Transmute` starts at byte offset 0, and has the same layout
124+ // and bit validity as `Dst`.
125+ //
126+ // Transmuting `Src` to `Transmute<Src, Dst>` above using
127+ // `core::mem::transmute` ensures that `size_of::<Src>() ==
128+ // size_of::<Transmute<Src, Dst>>()`. A `#[repr(C, packed)]`
129+ // union has the maximum size of all of its fields [1], so this
130+ // is equivalent to `size_of::<Src>() >= size_of::<Dst>()`.
131+ //
132+ // The outer `if`'s `false` branch ensures that `Src: IntoBytes`
133+ // and `Dst: FromBytes`. This, combined with the size bound,
134+ // ensures that this transmute is sound.
135+ //
136+ // Per https://doc.rust-lang.org/1.85.0/reference/type-layout.html#reprc-unions:
137+ //
138+ // The union will have a size of the maximum size of all of
139+ // its fields rounded to its alignment
140+ let dst = unsafe { u. dst } ;
141+ $crate:: util:: macro_util:: must_use( ManuallyDrop :: into_inner( dst) )
142+ }
143+ }
144+ } } ;
145+ ( $e: expr) => { {
60146 let e = $e;
61147 if false {
62148 // This branch, though never taken, ensures that the type of `e` is
63- // `IntoBytes` and that the type of this macro invocation expression
64- // is `FromBytes`.
65-
66- struct AssertIsIntoBytes <T : $crate:: IntoBytes >( T ) ;
67- let _ = AssertIsIntoBytes ( e) ;
149+ // `IntoBytes` and that the type of the outer macro invocation
150+ // expression is `FromBytes`.
68151
69- struct AssertIsFromBytes <U : $crate:: FromBytes >( U ) ;
70- #[ allow( unused, unreachable_code) ]
71- let u = AssertIsFromBytes ( loop { } ) ;
72- u. 0
152+ fn transmute<Src , Dst >( src: Src ) -> Dst
153+ where
154+ Src : $crate:: IntoBytes ,
155+ Dst : $crate:: FromBytes ,
156+ {
157+ let _ = src;
158+ loop { }
159+ }
160+ loop { }
161+ #[ allow( unreachable_code) ]
162+ transmute( e)
73163 } else {
74164 // SAFETY: `core::mem::transmute` ensures that the type of `e` and
75165 // the type of this macro invocation expression have the same size.
76166 // We know this transmute is safe thanks to the `IntoBytes` and
77167 // `FromBytes` bounds enforced by the `false` branch.
78- //
79- // We use this reexport of `core::mem::transmute` because we know it
80- // will always be available for crates which are using the 2015
81- // edition of Rust. By contrast, if we were to use
82- // `std::mem::transmute`, this macro would not work for such crates
83- // in `no_std` contexts, and if we were to use
84- // `core::mem::transmute`, this macro would not work in `std`
85- // contexts in which `core` was not manually imported. This is not a
86- // problem for 2018 edition crates.
87168 let u = unsafe {
88169 // Clippy: We can't annotate the types; this macro is designed
89170 // to infer the types from the calling context.
@@ -92,7 +173,7 @@ macro_rules! transmute {
92173 } ;
93174 $crate:: util:: macro_util:: must_use( u)
94175 }
95- } }
176+ } } ;
96177}
97178
98179/// Safely transmutes a mutable or immutable reference of one type to an
@@ -1046,6 +1127,10 @@ mod tests {
10461127 let x: [ u8 ; 8 ] = transmute ! ( array_of_arrays) ;
10471128 assert_eq ! ( x, array_of_u8s) ;
10481129
1130+ // Test that memory is transmuted as expected when shrinking.
1131+ let x: [ [ u8 ; 2 ] ; 3 ] = transmute ! ( #![ allow( shrink) ] array_of_u8s) ;
1132+ assert_eq ! ( x, [ [ 0u8 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] ] ) ;
1133+
10491134 // Test that the source expression's value is forgotten rather than
10501135 // dropped.
10511136 #[ derive( IntoBytes ) ]
@@ -1058,12 +1143,16 @@ mod tests {
10581143 }
10591144 #[ allow( clippy:: let_unit_value) ]
10601145 let _: ( ) = transmute ! ( PanicOnDrop ( ( ) ) ) ;
1146+ #[ allow( clippy:: let_unit_value) ]
1147+ let _: ( ) = transmute ! ( #![ allow( shrink) ] PanicOnDrop ( ( ) ) ) ;
10611148
10621149 // Test that `transmute!` is legal in a const context.
10631150 const ARRAY_OF_U8S : [ u8 ; 8 ] = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
10641151 const ARRAY_OF_ARRAYS : [ [ u8 ; 2 ] ; 4 ] = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
10651152 const X : [ [ u8 ; 2 ] ; 4 ] = transmute ! ( ARRAY_OF_U8S ) ;
10661153 assert_eq ! ( X , ARRAY_OF_ARRAYS ) ;
1154+ const X_SHRINK : [ [ u8 ; 2 ] ; 3 ] = transmute ! ( #![ allow( shrink) ] ARRAY_OF_U8S ) ;
1155+ assert_eq ! ( X_SHRINK , [ [ 0u8 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] ] ) ;
10671156
10681157 // Test that `transmute!` works with `!Immutable` types.
10691158 let x: usize = transmute ! ( UnsafeCell :: new( 1usize ) ) ;
0 commit comments