1
1
use std:: convert:: TryFrom ;
2
2
3
- use super :: { FnVal , ImmTy , Immediate , InterpCx , Machine , OpTy , PlaceTy } ;
4
3
use rustc_apfloat:: ieee:: { Double , Single } ;
5
4
use rustc_apfloat:: { Float , FloatConvert } ;
6
5
use rustc_ast:: ast:: FloatTy ;
@@ -12,25 +11,40 @@ use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable};
12
11
use rustc_span:: symbol:: sym;
13
12
use rustc_target:: abi:: { LayoutOf , Size , Variants } ;
14
13
14
+ use super :: { truncate, FnVal , ImmTy , Immediate , InterpCx , Machine , OpTy , PlaceTy } ;
15
+
15
16
impl < ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
16
17
pub fn cast (
17
18
& mut self ,
18
19
src : OpTy < ' tcx , M :: PointerTag > ,
19
- kind : CastKind ,
20
+ cast_kind : CastKind ,
21
+ cast_ty : Ty < ' tcx > ,
20
22
dest : PlaceTy < ' tcx , M :: PointerTag > ,
21
23
) -> InterpResult < ' tcx > {
22
24
use rustc_middle:: mir:: CastKind :: * ;
23
- match kind {
25
+ // FIXME: In which cases should we trigger UB when the source is uninit?
26
+ match cast_kind {
24
27
Pointer ( PointerCast :: Unsize ) => {
28
+ assert_eq ! (
29
+ cast_ty, dest. layout. ty,
30
+ "mismatch of cast type {} and place type {}" ,
31
+ cast_ty, dest. layout. ty
32
+ ) ;
25
33
self . unsize_into ( src, dest) ?;
26
34
}
27
35
28
- Misc | Pointer ( PointerCast :: MutToConstPointer | PointerCast :: ArrayToPointer ) => {
36
+ Misc => {
29
37
let src = self . read_immediate ( src) ?;
30
- let res = self . cast_immediate ( src, dest . layout ) ?;
38
+ let res = self . misc_cast ( src, cast_ty ) ?;
31
39
self . write_immediate ( res, dest) ?;
32
40
}
33
41
42
+ Pointer ( PointerCast :: MutToConstPointer | PointerCast :: ArrayToPointer ) => {
43
+ // These are NOPs, but can be wide pointers.
44
+ let v = self . read_immediate ( src) ?;
45
+ self . write_immediate ( * v, dest) ?;
46
+ }
47
+
34
48
Pointer ( PointerCast :: ReifyFnPointer ) => {
35
49
// The src operand does not matter, just its type
36
50
match src. layout . ty . kind {
@@ -61,12 +75,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
61
75
62
76
Pointer ( PointerCast :: UnsafeFnPointer ) => {
63
77
let src = self . read_immediate ( src) ?;
64
- match dest . layout . ty . kind {
78
+ match cast_ty . kind {
65
79
ty:: FnPtr ( _) => {
66
80
// No change to value
67
81
self . write_immediate ( * src, dest) ?;
68
82
}
69
- _ => bug ! ( "fn to unsafe fn cast on {:?}" , dest . layout . ty ) ,
83
+ _ => bug ! ( "fn to unsafe fn cast on {:?}" , cast_ty ) ,
70
84
}
71
85
}
72
86
@@ -95,21 +109,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
95
109
Ok ( ( ) )
96
110
}
97
111
98
- fn cast_immediate (
112
+ fn misc_cast (
99
113
& self ,
100
114
src : ImmTy < ' tcx , M :: PointerTag > ,
101
- dest_layout : TyAndLayout < ' tcx > ,
115
+ cast_ty : Ty < ' tcx > ,
102
116
) -> InterpResult < ' tcx , Immediate < M :: PointerTag > > {
103
117
use rustc_middle:: ty:: TyKind :: * ;
104
- trace ! ( "Casting {:?}: {:?} to {:?}" , * src, src. layout. ty, dest_layout . ty ) ;
118
+ trace ! ( "Casting {:?}: {:?} to {:?}" , * src, src. layout. ty, cast_ty ) ;
105
119
106
120
match src. layout . ty . kind {
107
121
// Floating point
108
122
Float ( FloatTy :: F32 ) => {
109
- return Ok ( self . cast_from_float ( src. to_scalar ( ) ?. to_f32 ( ) ?, dest_layout . ty ) . into ( ) ) ;
123
+ return Ok ( self . cast_from_float ( src. to_scalar ( ) ?. to_f32 ( ) ?, cast_ty ) . into ( ) ) ;
110
124
}
111
125
Float ( FloatTy :: F64 ) => {
112
- return Ok ( self . cast_from_float ( src. to_scalar ( ) ?. to_f64 ( ) ?, dest_layout . ty ) . into ( ) ) ;
126
+ return Ok ( self . cast_from_float ( src. to_scalar ( ) ?. to_f64 ( ) ?, cast_ty ) . into ( ) ) ;
113
127
}
114
128
// The rest is integer/pointer-"like", including fn ptr casts and casts from enums that
115
129
// are represented as integers.
@@ -124,69 +138,79 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
124
138
) ,
125
139
}
126
140
141
+ // # First handle non-scalar source values.
142
+
127
143
// Handle cast from a univariant (ZST) enum.
128
144
match src. layout . variants {
129
145
Variants :: Single { index } => {
130
146
if let Some ( discr) = src. layout . ty . discriminant_for_variant ( * self . tcx , index) {
131
147
assert ! ( src. layout. is_zst( ) ) ;
132
148
let discr_layout = self . layout_of ( discr. ty ) ?;
133
- return Ok ( self
134
- . cast_from_int_like ( discr. val , discr_layout, dest_layout)
135
- . into ( ) ) ;
149
+ return Ok ( self . cast_from_scalar ( discr. val , discr_layout, cast_ty) . into ( ) ) ;
136
150
}
137
151
}
138
152
Variants :: Multiple { .. } => { }
139
153
}
140
154
141
- // Handle casting the metadata away from a fat pointer.
142
- if src. layout . ty . is_unsafe_ptr ( )
143
- && dest_layout. ty . is_unsafe_ptr ( )
144
- && dest_layout. size != src. layout . size
145
- {
146
- assert_eq ! ( src. layout. size, 2 * self . memory. pointer_size( ) ) ;
147
- assert_eq ! ( dest_layout. size, self . memory. pointer_size( ) ) ;
148
- assert ! ( dest_layout. ty. is_unsafe_ptr( ) ) ;
149
- match * src {
150
- Immediate :: ScalarPair ( data, _) => return Ok ( data. into ( ) ) ,
151
- Immediate :: Scalar ( ..) => bug ! (
152
- "{:?} input to a fat-to-thin cast ({:?} -> {:?})" ,
153
- * src,
154
- src. layout. ty,
155
- dest_layout. ty
156
- ) ,
157
- } ;
158
- }
159
-
160
155
// Handle casting any ptr to raw ptr (might be a fat ptr).
161
- if src. layout . ty . is_any_ptr ( ) && dest_layout. ty . is_unsafe_ptr ( ) {
162
- // The only possible size-unequal case was handled above.
163
- assert_eq ! ( src. layout. size, dest_layout. size) ;
164
- return Ok ( * src) ;
156
+ if src. layout . ty . is_any_ptr ( ) && cast_ty. is_unsafe_ptr ( ) {
157
+ let dest_layout = self . layout_of ( cast_ty) ?;
158
+ if dest_layout. size == src. layout . size {
159
+ // Thin or fat pointer that just hast the ptr kind of target type changed.
160
+ return Ok ( * src) ;
161
+ } else {
162
+ // Casting the metadata away from a fat ptr.
163
+ assert_eq ! ( src. layout. size, 2 * self . memory. pointer_size( ) ) ;
164
+ assert_eq ! ( dest_layout. size, self . memory. pointer_size( ) ) ;
165
+ assert ! ( src. layout. ty. is_unsafe_ptr( ) ) ;
166
+ return match * src {
167
+ Immediate :: ScalarPair ( data, _) => Ok ( data. into ( ) ) ,
168
+ Immediate :: Scalar ( ..) => bug ! (
169
+ "{:?} input to a fat-to-thin cast ({:?} -> {:?})" ,
170
+ * src,
171
+ src. layout. ty,
172
+ cast_ty
173
+ ) ,
174
+ } ;
175
+ }
165
176
}
166
177
178
+ // # The remaining source values are scalar.
179
+
167
180
// For all remaining casts, we either
168
181
// (a) cast a raw ptr to usize, or
169
182
// (b) cast from an integer-like (including bool, char, enums).
170
183
// In both cases we want the bits.
171
184
let bits = self . force_bits ( src. to_scalar ( ) ?, src. layout . size ) ?;
172
- Ok ( self . cast_from_int_like ( bits, src. layout , dest_layout ) . into ( ) )
185
+ Ok ( self . cast_from_scalar ( bits, src. layout , cast_ty ) . into ( ) )
173
186
}
174
187
175
- pub ( super ) fn cast_from_int_like (
188
+ pub ( super ) fn cast_from_scalar (
176
189
& self ,
177
- v : u128 , // raw bits
190
+ v : u128 , // raw bits (there is no ScalarTy so we separate data+layout)
178
191
src_layout : TyAndLayout < ' tcx > ,
179
- dest_layout : TyAndLayout < ' tcx > ,
192
+ cast_ty : Ty < ' tcx > ,
180
193
) -> Scalar < M :: PointerTag > {
181
194
// Let's make sure v is sign-extended *if* it has a signed type.
182
- let signed = src_layout. abi . is_signed ( ) ;
195
+ let signed = src_layout. abi . is_signed ( ) ; // also checks that abi is `Scalar`.
183
196
let v = if signed { self . sign_extend ( v, src_layout) } else { v } ;
184
- trace ! ( "cast_from_int : {}, {}, {}" , v, src_layout. ty, dest_layout . ty ) ;
197
+ trace ! ( "cast_from_scalar : {}, {} -> {}" , v, src_layout. ty, cast_ty ) ;
185
198
use rustc_middle:: ty:: TyKind :: * ;
186
- match dest_layout . ty . kind {
199
+ match cast_ty . kind {
187
200
Int ( _) | Uint ( _) | RawPtr ( _) => {
188
- let v = self . truncate ( v, dest_layout) ;
189
- Scalar :: from_uint ( v, dest_layout. size )
201
+ let size = match cast_ty. kind {
202
+ // FIXME: Isn't there a helper for this? The same pattern occurs below.
203
+ Int ( t) => {
204
+ t. bit_width ( ) . map ( Size :: from_bits) . unwrap_or_else ( || self . pointer_size ( ) )
205
+ }
206
+ Uint ( t) => {
207
+ t. bit_width ( ) . map ( Size :: from_bits) . unwrap_or_else ( || self . pointer_size ( ) )
208
+ }
209
+ RawPtr ( _) => self . pointer_size ( ) ,
210
+ _ => bug ! ( ) ,
211
+ } ;
212
+ let v = truncate ( v, size) ;
213
+ Scalar :: from_uint ( v, size)
190
214
}
191
215
192
216
Float ( FloatTy :: F32 ) if signed => Scalar :: from_f32 ( Single :: from_i128 ( v as i128 ) . value ) ,
@@ -200,7 +224,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
200
224
}
201
225
202
226
// Casts to bool are not permitted by rustc, no need to handle them here.
203
- _ => bug ! ( "invalid int to {:?} cast" , dest_layout . ty ) ,
227
+ _ => bug ! ( "invalid int to {:?} cast" , cast_ty ) ,
204
228
}
205
229
}
206
230
@@ -283,7 +307,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
283
307
src : OpTy < ' tcx , M :: PointerTag > ,
284
308
dest : PlaceTy < ' tcx , M :: PointerTag > ,
285
309
) -> InterpResult < ' tcx > {
286
- trace ! ( "Unsizing {:?} into {:?}" , src, dest) ;
310
+ trace ! ( "Unsizing {:?} of type {} into {:?}" , * src, src . layout . ty , dest. layout . ty ) ;
287
311
match ( & src. layout . ty . kind , & dest. layout . ty . kind ) {
288
312
( & ty:: Ref ( _, s, _) , & ty:: Ref ( _, d, _) | & ty:: RawPtr ( TypeAndMut { ty : d, .. } ) )
289
313
| ( & ty:: RawPtr ( TypeAndMut { ty : s, .. } ) , & ty:: RawPtr ( TypeAndMut { ty : d, .. } ) ) => {
0 commit comments