]>
Commit | Line | Data |
---|---|---|
ba9703b0 | 1 | use std::convert::TryFrom; |
ea8adc8c | 2 | |
dfeec247 XL |
3 | use rustc_apfloat::ieee::{Double, Single}; |
4 | use rustc_apfloat::{Float, FloatConvert}; | |
ba9703b0 XL |
5 | use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; |
6 | use rustc_middle::mir::CastKind; | |
7 | use rustc_middle::ty::adjustment::PointerCast; | |
c295e0f8 | 8 | use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout}; |
5869c6ff | 9 | use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut}; |
c295e0f8 | 10 | use rustc_target::abi::{Integer, Variants}; |
f9f354fc | 11 | |
3dfed10e | 12 | use super::{ |
29967ef6 | 13 | util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, |
3dfed10e | 14 | }; |
b7449926 | 15 | |
ba9703b0 | 16 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
b7449926 | 17 | pub fn cast( |
8faf50e0 | 18 | &mut self, |
6a06907d | 19 | src: &OpTy<'tcx, M::PointerTag>, |
f9f354fc XL |
20 | cast_kind: CastKind, |
21 | cast_ty: Ty<'tcx>, | |
6a06907d | 22 | dest: &PlaceTy<'tcx, M::PointerTag>, |
dc9dc135 | 23 | ) -> InterpResult<'tcx> { |
ba9703b0 | 24 | use rustc_middle::mir::CastKind::*; |
f9f354fc XL |
25 | // FIXME: In which cases should we trigger UB when the source is uninit? |
26 | match cast_kind { | |
48663c56 | 27 | Pointer(PointerCast::Unsize) => { |
f9f354fc XL |
28 | let cast_ty = self.layout_of(cast_ty)?; |
29 | self.unsize_into(src, cast_ty, dest)?; | |
8faf50e0 XL |
30 | } |
31 | ||
f9f354fc | 32 | Misc => { |
a1dfa0c6 | 33 | let src = self.read_immediate(src)?; |
6a06907d | 34 | let res = self.misc_cast(&src, cast_ty)?; |
416331ca | 35 | self.write_immediate(res, dest)?; |
8faf50e0 XL |
36 | } |
37 | ||
f9f354fc XL |
38 | Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => { |
39 | // These are NOPs, but can be wide pointers. | |
40 | let v = self.read_immediate(src)?; | |
41 | self.write_immediate(*v, dest)?; | |
42 | } | |
43 | ||
48663c56 | 44 | Pointer(PointerCast::ReifyFnPointer) => { |
b7449926 | 45 | // The src operand does not matter, just its type |
1b1a35ee | 46 | match *src.layout.ty.kind() { |
b7449926 | 47 | ty::FnDef(def_id, substs) => { |
e1599b0c | 48 | // All reifications must be monomorphic, bail out otherwise. |
3dfed10e | 49 | ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; |
e1599b0c | 50 | |
e74abb32 XL |
51 | let instance = ty::Instance::resolve_for_fn_ptr( |
52 | *self.tcx, | |
53 | self.param_env, | |
54 | def_id, | |
55 | substs, | |
dfeec247 XL |
56 | ) |
57 | .ok_or_else(|| err_inval!(TooGeneric))?; | |
e74abb32 | 58 | |
416331ca | 59 | let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); |
136023e0 | 60 | self.write_pointer(fn_ptr, dest)?; |
8faf50e0 | 61 | } |
f035d41b | 62 | _ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty), |
8faf50e0 XL |
63 | } |
64 | } | |
65 | ||
48663c56 | 66 | Pointer(PointerCast::UnsafeFnPointer) => { |
a1dfa0c6 | 67 | let src = self.read_immediate(src)?; |
1b1a35ee | 68 | match cast_ty.kind() { |
b7449926 XL |
69 | ty::FnPtr(_) => { |
70 | // No change to value | |
a1dfa0c6 | 71 | self.write_immediate(*src, dest)?; |
8faf50e0 | 72 | } |
f035d41b | 73 | _ => span_bug!(self.cur_span(), "fn to unsafe fn cast on {:?}", cast_ty), |
8faf50e0 XL |
74 | } |
75 | } | |
76 | ||
48663c56 | 77 | Pointer(PointerCast::ClosureFnPointer(_)) => { |
b7449926 | 78 | // The src operand does not matter, just its type |
1b1a35ee | 79 | match *src.layout.ty.kind() { |
b7449926 | 80 | ty::Closure(def_id, substs) => { |
e1599b0c | 81 | // All reifications must be monomorphic, bail out otherwise. |
3dfed10e | 82 | ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; |
e1599b0c | 83 | |
8faf50e0 XL |
84 | let instance = ty::Instance::resolve_closure( |
85 | *self.tcx, | |
86 | def_id, | |
87 | substs, | |
88 | ty::ClosureKind::FnOnce, | |
89 | ); | |
416331ca | 90 | let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); |
136023e0 | 91 | self.write_pointer(fn_ptr, dest)?; |
8faf50e0 | 92 | } |
f035d41b | 93 | _ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty), |
8faf50e0 XL |
94 | } |
95 | } | |
96 | } | |
97 | Ok(()) | |
98 | } | |
99 | ||
ee023bcb | 100 | pub fn misc_cast( |
ea8adc8c | 101 | &self, |
6a06907d | 102 | src: &ImmTy<'tcx, M::PointerTag>, |
f9f354fc | 103 | cast_ty: Ty<'tcx>, |
416331ca | 104 | ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { |
ba9703b0 | 105 | use rustc_middle::ty::TyKind::*; |
f9f354fc | 106 | trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty); |
ea8adc8c | 107 | |
1b1a35ee | 108 | match src.layout.ty.kind() { |
dc9dc135 | 109 | // Floating point |
dfeec247 | 110 | Float(FloatTy::F32) => { |
f9f354fc | 111 | return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into()); |
dfeec247 XL |
112 | } |
113 | Float(FloatTy::F64) => { | |
f9f354fc | 114 | return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into()); |
dfeec247 | 115 | } |
416331ca XL |
116 | // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that |
117 | // are represented as integers. | |
dfeec247 XL |
118 | _ => assert!( |
119 | src.layout.ty.is_bool() | |
120 | || src.layout.ty.is_char() | |
121 | || src.layout.ty.is_enum() | |
122 | || src.layout.ty.is_integral() | |
123 | || src.layout.ty.is_any_ptr(), | |
124 | "Unexpected cast from type {:?}", | |
125 | src.layout.ty | |
126 | ), | |
416331ca XL |
127 | } |
128 | ||
f9f354fc XL |
129 | // # First handle non-scalar source values. |
130 | ||
29967ef6 | 131 | // Handle cast from a ZST enum (0 or 1 variants). |
416331ca | 132 | match src.layout.variants { |
ba9703b0 | 133 | Variants::Single { index } => { |
29967ef6 XL |
134 | if src.layout.abi.is_uninhabited() { |
135 | // This is dead code, because an uninhabited enum is UB to | |
136 | // instantiate. | |
137 | throw_ub!(Unreachable); | |
138 | } | |
dfeec247 | 139 | if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) { |
416331ca | 140 | assert!(src.layout.is_zst()); |
ba9703b0 | 141 | let discr_layout = self.layout_of(discr.ty)?; |
ee023bcb | 142 | return Ok(self.cast_from_int_like(discr.val, discr_layout, cast_ty).into()); |
ea8adc8c XL |
143 | } |
144 | } | |
ba9703b0 | 145 | Variants::Multiple { .. } => {} |
416331ca XL |
146 | } |
147 | ||
416331ca | 148 | // Handle casting any ptr to raw ptr (might be a fat ptr). |
f9f354fc XL |
149 | if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() { |
150 | let dest_layout = self.layout_of(cast_ty)?; | |
151 | if dest_layout.size == src.layout.size { | |
152 | // Thin or fat pointer that just hast the ptr kind of target type changed. | |
6a06907d | 153 | return Ok(**src); |
f9f354fc XL |
154 | } else { |
155 | // Casting the metadata away from a fat ptr. | |
156 | assert_eq!(src.layout.size, 2 * self.memory.pointer_size()); | |
157 | assert_eq!(dest_layout.size, self.memory.pointer_size()); | |
158 | assert!(src.layout.ty.is_unsafe_ptr()); | |
6a06907d | 159 | return match **src { |
f9f354fc | 160 | Immediate::ScalarPair(data, _) => Ok(data.into()), |
f035d41b XL |
161 | Immediate::Scalar(..) => span_bug!( |
162 | self.cur_span(), | |
f9f354fc XL |
163 | "{:?} input to a fat-to-thin cast ({:?} -> {:?})", |
164 | *src, | |
165 | src.layout.ty, | |
166 | cast_ty | |
167 | ), | |
168 | }; | |
169 | } | |
ea8adc8c | 170 | } |
416331ca | 171 | |
ee023bcb | 172 | // # The remaining source values are scalar and "int-like". |
f9f354fc | 173 | |
416331ca XL |
174 | // For all remaining casts, we either |
175 | // (a) cast a raw ptr to usize, or | |
176 | // (b) cast from an integer-like (including bool, char, enums). | |
177 | // In both cases we want the bits. | |
136023e0 | 178 | let bits = src.to_scalar()?.to_bits(src.layout.size)?; |
ee023bcb | 179 | Ok(self.cast_from_int_like(bits, src.layout, cast_ty).into()) |
ea8adc8c XL |
180 | } |
181 | ||
ee023bcb | 182 | fn cast_from_int_like( |
ea8adc8c | 183 | &self, |
f9f354fc | 184 | v: u128, // raw bits (there is no ScalarTy so we separate data+layout) |
ba9703b0 | 185 | src_layout: TyAndLayout<'tcx>, |
f9f354fc XL |
186 | cast_ty: Ty<'tcx>, |
187 | ) -> Scalar<M::PointerTag> { | |
dc9dc135 | 188 | // Let's make sure v is sign-extended *if* it has a signed type. |
f9f354fc | 189 | let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`. |
dfeec247 | 190 | let v = if signed { self.sign_extend(v, src_layout) } else { v }; |
f9f354fc | 191 | trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty); |
ba9703b0 | 192 | use rustc_middle::ty::TyKind::*; |
1b1a35ee | 193 | match *cast_ty.kind() { |
dc9dc135 | 194 | Int(_) | Uint(_) | RawPtr(_) => { |
1b1a35ee | 195 | let size = match *cast_ty.kind() { |
5869c6ff XL |
196 | Int(t) => Integer::from_int_ty(self, t).size(), |
197 | Uint(t) => Integer::from_uint_ty(self, t).size(), | |
f9f354fc XL |
198 | RawPtr(_) => self.pointer_size(), |
199 | _ => bug!(), | |
200 | }; | |
29967ef6 | 201 | let v = size.truncate(v); |
f9f354fc | 202 | Scalar::from_uint(v, size) |
0531ce1d | 203 | } |
ea8adc8c | 204 | |
f9f354fc XL |
205 | Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value), |
206 | Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value), | |
207 | Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value), | |
208 | Float(FloatTy::F64) => Scalar::from_f64(Double::from_u128(v).value), | |
ea8adc8c | 209 | |
b7449926 XL |
210 | Char => { |
211 | // `u8` to `char` cast | |
f9f354fc | 212 | Scalar::from_u32(u8::try_from(v).unwrap().into()) |
dfeec247 | 213 | } |
ea8adc8c | 214 | |
0531ce1d | 215 | // Casts to bool are not permitted by rustc, no need to handle them here. |
f035d41b | 216 | _ => span_bug!(self.cur_span(), "invalid int to {:?} cast", cast_ty), |
ea8adc8c XL |
217 | } |
218 | } | |
219 | ||
f9f354fc | 220 | fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::PointerTag> |
dfeec247 XL |
221 | where |
222 | F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>, | |
dc9dc135 | 223 | { |
ba9703b0 | 224 | use rustc_middle::ty::TyKind::*; |
1b1a35ee | 225 | match *dest_ty.kind() { |
0531ce1d | 226 | // float -> uint |
b7449926 | 227 | Uint(t) => { |
5869c6ff | 228 | let size = Integer::from_uint_ty(self, t).size(); |
ba9703b0 XL |
229 | // `to_u128` is a saturating cast, which is what we need |
230 | // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). | |
f9f354fc | 231 | let v = f.to_u128(size.bits_usize()).value; |
b7449926 | 232 | // This should already fit the bit width |
f9f354fc | 233 | Scalar::from_uint(v, size) |
dfeec247 | 234 | } |
0531ce1d | 235 | // float -> int |
b7449926 | 236 | Int(t) => { |
5869c6ff | 237 | let size = Integer::from_int_ty(self, t).size(); |
ba9703b0 XL |
238 | // `to_i128` is a saturating cast, which is what we need |
239 | // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). | |
f9f354fc XL |
240 | let v = f.to_i128(size.bits_usize()).value; |
241 | Scalar::from_int(v, size) | |
dfeec247 | 242 | } |
dc9dc135 | 243 | // float -> f32 |
f9f354fc | 244 | Float(FloatTy::F32) => Scalar::from_f32(f.convert(&mut false).value), |
dc9dc135 | 245 | // float -> f64 |
f9f354fc | 246 | Float(FloatTy::F64) => Scalar::from_f64(f.convert(&mut false).value), |
dc9dc135 | 247 | // That's it. |
f035d41b | 248 | _ => span_bug!(self.cur_span(), "invalid float to {:?} cast", dest_ty), |
ea8adc8c XL |
249 | } |
250 | } | |
251 | ||
b7449926 XL |
252 | fn unsize_into_ptr( |
253 | &mut self, | |
6a06907d XL |
254 | src: &OpTy<'tcx, M::PointerTag>, |
255 | dest: &PlaceTy<'tcx, M::PointerTag>, | |
b7449926 | 256 | // The pointee types |
e74abb32 | 257 | source_ty: Ty<'tcx>, |
f9f354fc | 258 | cast_ty: Ty<'tcx>, |
dc9dc135 | 259 | ) -> InterpResult<'tcx> { |
b7449926 | 260 | // A<Struct> -> A<Trait> conversion |
416331ca | 261 | let (src_pointee_ty, dest_pointee_ty) = |
f9f354fc | 262 | self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env); |
b7449926 | 263 | |
1b1a35ee | 264 | match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) { |
b7449926 | 265 | (&ty::Array(_, length), &ty::Slice(_)) => { |
60c5eb7d | 266 | let ptr = self.read_immediate(src)?.to_scalar()?; |
b7449926 | 267 | // u64 cast is from usize to u64, which is always good |
f035d41b XL |
268 | let val = |
269 | Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self); | |
a1dfa0c6 | 270 | self.write_immediate(val, dest) |
b7449926 | 271 | } |
94222f64 | 272 | (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { |
a1dfa0c6 | 273 | let val = self.read_immediate(src)?; |
94222f64 XL |
274 | if data_a.principal_def_id() == data_b.principal_def_id() { |
275 | return self.write_immediate(*val, dest); | |
276 | } | |
277 | // trait upcasting coercion | |
278 | let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( | |
279 | src_pointee_ty, | |
280 | dest_pointee_ty, | |
281 | )); | |
282 | ||
283 | if let Some(entry_idx) = vptr_entry_idx { | |
284 | let entry_idx = u64::try_from(entry_idx).unwrap(); | |
285 | let (old_data, old_vptr) = val.to_scalar_pair()?; | |
286 | let old_vptr = self.scalar_to_ptr(old_vptr); | |
287 | let new_vptr = self | |
288 | .read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?; | |
289 | self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) | |
290 | } else { | |
291 | self.write_immediate(*val, dest) | |
292 | } | |
b7449926 XL |
293 | } |
294 | (_, &ty::Dynamic(ref data, _)) => { | |
295 | // Initial cast from sized to dyn trait | |
0bf4aa26 | 296 | let vtable = self.get_vtable(src_pointee_ty, data.principal())?; |
60c5eb7d | 297 | let ptr = self.read_immediate(src)?.to_scalar()?; |
136023e0 | 298 | let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx); |
a1dfa0c6 | 299 | self.write_immediate(val, dest) |
b7449926 XL |
300 | } |
301 | ||
f035d41b XL |
302 | _ => { |
303 | span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty) | |
304 | } | |
b7449926 XL |
305 | } |
306 | } | |
307 | ||
308 | fn unsize_into( | |
309 | &mut self, | |
6a06907d | 310 | src: &OpTy<'tcx, M::PointerTag>, |
f9f354fc | 311 | cast_ty: TyAndLayout<'tcx>, |
6a06907d | 312 | dest: &PlaceTy<'tcx, M::PointerTag>, |
dc9dc135 | 313 | ) -> InterpResult<'tcx> { |
f9f354fc | 314 | trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty); |
1b1a35ee | 315 | match (&src.layout.ty.kind(), &cast_ty.ty.kind()) { |
f9f354fc XL |
316 | (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) |
317 | | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { | |
5099ac24 | 318 | self.unsize_into_ptr(src, dest, *s, *c) |
b7449926 XL |
319 | } |
320 | (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { | |
321 | assert_eq!(def_a, def_b); | |
322 | if def_a.is_box() || def_b.is_box() { | |
323 | if !def_a.is_box() || !def_b.is_box() { | |
f035d41b XL |
324 | span_bug!( |
325 | self.cur_span(), | |
326 | "invalid unsizing between {:?} -> {:?}", | |
327 | src.layout.ty, | |
328 | cast_ty.ty | |
329 | ); | |
b7449926 XL |
330 | } |
331 | return self.unsize_into_ptr( | |
332 | src, | |
333 | dest, | |
334 | src.layout.ty.boxed_ty(), | |
f9f354fc | 335 | cast_ty.ty.boxed_ty(), |
b7449926 XL |
336 | ); |
337 | } | |
338 | ||
339 | // unsizing of generic struct with pointer fields | |
340 | // Example: `Arc<T>` -> `Arc<Trait>` | |
341 | // here we need to increase the size of every &T thin ptr field to a fat ptr | |
342 | for i in 0..src.layout.fields.count() { | |
94222f64 | 343 | let cast_ty_field = cast_ty.field(self, i); |
f9f354fc | 344 | if cast_ty_field.is_zst() { |
b7449926 XL |
345 | continue; |
346 | } | |
ba9703b0 | 347 | let src_field = self.operand_field(src, i)?; |
f9f354fc XL |
348 | let dst_field = self.place_field(dest, i)?; |
349 | if src_field.layout.ty == cast_ty_field.ty { | |
6a06907d | 350 | self.copy_op(&src_field, &dst_field)?; |
b7449926 | 351 | } else { |
6a06907d | 352 | self.unsize_into(&src_field, cast_ty_field, &dst_field)?; |
b7449926 XL |
353 | } |
354 | } | |
355 | Ok(()) | |
356 | } | |
f035d41b XL |
357 | _ => span_bug!( |
358 | self.cur_span(), | |
359 | "unsize_into: invalid conversion: {:?} -> {:?}", | |
360 | src.layout, | |
361 | dest.layout | |
362 | ), | |
b7449926 XL |
363 | } |
364 | } | |
ea8adc8c | 365 | } |