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