]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/cast.rs
Auto merge of #50409 - KiChjang:issue-50343, r=nikomatsakis
[rust.git] / src / librustc_mir / interpret / cast.rs
1 use rustc::ty::Ty;
2 use rustc::ty::layout::LayoutOf;
3 use syntax::ast::{FloatTy, IntTy, UintTy};
4
5 use rustc_apfloat::ieee::{Single, Double};
6 use super::{EvalContext, Machine};
7 use rustc::mir::interpret::{PrimVal, EvalResult, MemoryPointer, PointerArithmetic};
8 use rustc_apfloat::Float;
9
10 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
11     pub(super) fn cast_primval(
12         &self,
13         val: PrimVal,
14         src_ty: Ty<'tcx>,
15         dest_ty: Ty<'tcx>,
16     ) -> EvalResult<'tcx, PrimVal> {
17         use rustc::ty::TypeVariants::*;
18         trace!("Casting {:?}: {:?} to {:?}", val, src_ty, dest_ty);
19
20         match val {
21             PrimVal::Undef => Ok(PrimVal::Undef),
22             PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty),
23             PrimVal::Bytes(b) => {
24                 match src_ty.sty {
25                     TyFloat(fty) => self.cast_from_float(b, fty, dest_ty),
26                     _ => self.cast_from_int(b, src_ty, dest_ty),
27                 }
28             }
29         }
30     }
31
32     fn cast_from_int(
33         &self,
34         v: u128,
35         src_ty: Ty<'tcx>,
36         dest_ty: Ty<'tcx>,
37     ) -> EvalResult<'tcx, PrimVal> {
38         let signed = self.layout_of(src_ty)?.abi.is_signed();
39         let v = if signed {
40             self.sign_extend(v, src_ty)?
41         } else {
42             v
43         };
44         trace!("cast_from_int: {}, {}, {}", v, src_ty, dest_ty);
45         use rustc::ty::TypeVariants::*;
46         match dest_ty.sty {
47             TyInt(_) | TyUint(_) => {
48                 let v = self.truncate(v, dest_ty)?;
49                 Ok(PrimVal::Bytes(v))
50             }
51
52             TyFloat(FloatTy::F32) if signed => Ok(PrimVal::Bytes(Single::from_i128(v as i128).value.to_bits())),
53             TyFloat(FloatTy::F64) if signed => Ok(PrimVal::Bytes(Double::from_i128(v as i128).value.to_bits())),
54             TyFloat(FloatTy::F32) => Ok(PrimVal::Bytes(Single::from_u128(v).value.to_bits())),
55             TyFloat(FloatTy::F64) => Ok(PrimVal::Bytes(Double::from_u128(v).value.to_bits())),
56
57             TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
58             TyChar => err!(InvalidChar(v)),
59
60             // No alignment check needed for raw pointers.  But we have to truncate to target ptr size.
61             TyRawPtr(_) => {
62                 Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128))
63             },
64
65             // Casts to bool are not permitted by rustc, no need to handle them here.
66             _ => err!(Unimplemented(format!("int to {:?} cast", dest_ty))),
67         }
68     }
69
70     fn cast_from_float(&self, bits: u128, fty: FloatTy, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
71         use rustc::ty::TypeVariants::*;
72         use rustc_apfloat::FloatConvert;
73         match dest_ty.sty {
74             // float -> uint
75             TyUint(t) => {
76                 let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8);
77                 match fty {
78                     FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(bits).to_u128(width).value)),
79                     FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(bits).to_u128(width).value)),
80                 }
81             },
82             // float -> int
83             TyInt(t) => {
84                 let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8);
85                 match fty {
86                     FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(bits).to_i128(width).value)),
87                     FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(bits).to_i128(width).value)),
88                 }
89             },
90             // f64 -> f32
91             TyFloat(FloatTy::F32) if fty == FloatTy::F64 => {
92                 Ok(PrimVal::Bytes(Single::to_bits(Double::from_bits(bits).convert(&mut false).value)))
93             },
94             // f32 -> f64
95             TyFloat(FloatTy::F64) if fty == FloatTy::F32 => {
96                 Ok(PrimVal::Bytes(Double::to_bits(Single::from_bits(bits).convert(&mut false).value)))
97             },
98             // identity cast
99             TyFloat(_) => Ok(PrimVal::Bytes(bits)),
100             _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))),
101         }
102     }
103
104     fn cast_from_ptr(&self, ptr: MemoryPointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
105         use rustc::ty::TypeVariants::*;
106         match ty.sty {
107             // Casting to a reference or fn pointer is not permitted by rustc, no need to support it here.
108             TyRawPtr(_) |
109             TyInt(IntTy::Isize) |
110             TyUint(UintTy::Usize) => Ok(PrimVal::Ptr(ptr)),
111             TyInt(_) | TyUint(_) => err!(ReadPointerAsBytes),
112             _ => err!(Unimplemented(format!("ptr to {:?} cast", ty))),
113         }
114     }
115 }