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