51
51
/// This macro can be invoked in `const` contexts.
52
52
#[ macro_export]
53
53
macro_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.
59
-
60
- let e = $e;
54
+ // NOTE: This must be a macro (rather than a function with trait bounds)
55
+ // because there's no way, in a generic context, to enforce that two types
56
+ // have the same size. `core::mem::transmute` uses compiler magic to enforce
57
+ // this so long as the types are concrete.
58
+ ( #![ allow( shrink) ] $e: expr) => { {
59
+ let mut e = $e;
61
60
if false {
62
- // 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`.
61
+ $crate:: __transmute_inner!( @assert_into_bytes_from_bytes e)
62
+ } else {
63
+ use $crate:: util:: macro_util:: core_reexport:: mem:: ManuallyDrop ;
64
+
65
+ #[ repr( C ) ]
66
+ union Transmute <Src , Dst > {
67
+ src: ManuallyDrop <Src >,
68
+ dst: ManuallyDrop <Dst >,
69
+ }
65
70
66
- struct AssertIsIntoBytes <T : $crate:: IntoBytes >( T ) ;
67
- let _ = AssertIsIntoBytes ( e) ;
71
+ // TODO: Update this safety comment.
72
+ //
73
+ // SAFETY: `core::mem::transmute` ensures that the type of `e` and
74
+ // the type of this macro invocation expression have the same size.
75
+ // We know this transmute is safe thanks to the `IntoBytes` and
76
+ // `FromBytes` bounds enforced by the `false` branch.
77
+ let u: Transmute <_, _> = unsafe {
78
+ // Clippy: We can't annotate the types; this macro is designed
79
+ // to infer the types from the calling context.
80
+ #[ allow( clippy:: missing_transmute_annotations, unnecessary_transmutes) ]
81
+ $crate:: util:: macro_util:: core_reexport:: mem:: transmute( e)
82
+ } ;
68
83
69
- struct AssertIsFromBytes <U : $crate:: FromBytes >( U ) ;
70
- #[ allow( unused, unreachable_code) ]
71
- let u = AssertIsFromBytes ( loop { } ) ;
72
- u. 0
84
+ if false {
85
+ // SAFETY: This code is never executed.
86
+ e = ManuallyDrop :: into_inner( unsafe { u. src } ) ;
87
+ // Suppress the `unused_assignments` lint on the previous line.
88
+ let _ = e;
89
+ loop { }
90
+ } else {
91
+ // TODO: Safety comment
92
+ let dst = unsafe { u. dst } ;
93
+ $crate:: util:: macro_util:: must_use( ManuallyDrop :: into_inner( dst) )
94
+ }
95
+ }
96
+ } } ;
97
+ ( $e: expr) => { {
98
+ let e = $e;
99
+ if false {
100
+ $crate:: __transmute_inner!( @assert_into_bytes_from_bytes e)
73
101
} else {
74
102
// SAFETY: `core::mem::transmute` ensures that the type of `e` and
75
103
// the type of this macro invocation expression have the same size.
76
104
// We know this transmute is safe thanks to the `IntoBytes` and
77
105
// `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.
87
106
let u = unsafe {
88
107
// Clippy: We can't annotate the types; this macro is designed
89
108
// to infer the types from the calling context.
@@ -92,7 +111,29 @@ macro_rules! transmute {
92
111
} ;
93
112
$crate:: util:: macro_util:: must_use( u)
94
113
}
95
- } }
114
+ } } ;
115
+ }
116
+
117
+ #[ macro_export]
118
+ #[ doc( hidden) ]
119
+ macro_rules! __transmute_inner {
120
+ ( @assert_into_bytes_from_bytes $e: expr) => { {
121
+ // This branch, though never taken, ensures that the type of `e` is
122
+ // `IntoBytes` and that the type of the outer macro invocation
123
+ // expression is `FromBytes`.
124
+
125
+ fn transmute<Src , Dst >( src: Src ) -> Dst
126
+ where
127
+ Src : $crate:: IntoBytes ,
128
+ Dst : $crate:: FromBytes ,
129
+ {
130
+ let _ = src;
131
+ loop { }
132
+ }
133
+ loop { }
134
+ #[ allow( unreachable_code) ]
135
+ transmute( $e)
136
+ } } ;
96
137
}
97
138
98
139
/// Safely transmutes a mutable or immutable reference of one type to an
@@ -1109,6 +1150,10 @@ mod tests {
1109
1150
let x: [ u8 ; 8 ] = transmute ! ( array_of_arrays) ;
1110
1151
assert_eq ! ( x, array_of_u8s) ;
1111
1152
1153
+ // Test that memory is transmuted as expected when shrinking.
1154
+ let x: [ [ u8 ; 2 ] ; 3 ] = transmute ! ( #![ allow( shrink) ] array_of_u8s) ;
1155
+ assert_eq ! ( x, [ [ 0u8 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] ] ) ;
1156
+
1112
1157
// Test that the source expression's value is forgotten rather than
1113
1158
// dropped.
1114
1159
#[ derive( IntoBytes ) ]
@@ -1121,12 +1166,16 @@ mod tests {
1121
1166
}
1122
1167
#[ allow( clippy:: let_unit_value) ]
1123
1168
let _: ( ) = transmute ! ( PanicOnDrop ( ( ) ) ) ;
1169
+ #[ allow( clippy:: let_unit_value) ]
1170
+ let _: ( ) = transmute ! ( #![ allow( shrink) ] PanicOnDrop ( ( ) ) ) ;
1124
1171
1125
1172
// Test that `transmute!` is legal in a const context.
1126
1173
const ARRAY_OF_U8S : [ u8 ; 8 ] = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1127
1174
const ARRAY_OF_ARRAYS : [ [ u8 ; 2 ] ; 4 ] = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
1128
1175
const X : [ [ u8 ; 2 ] ; 4 ] = transmute ! ( ARRAY_OF_U8S ) ;
1129
1176
assert_eq ! ( X , ARRAY_OF_ARRAYS ) ;
1177
+ const X_SHRINK : [ [ u8 ; 2 ] ; 3 ] = transmute ! ( #![ allow( shrink) ] ARRAY_OF_U8S ) ;
1178
+ assert_eq ! ( X_SHRINK , [ [ 0u8 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] ] ) ;
1130
1179
1131
1180
// Test that `transmute!` works with `!Immutable` types.
1132
1181
let x: usize = transmute ! ( UnsafeCell :: new( 1usize ) ) ;
0 commit comments