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