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