]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/cast.rs
e69e7a522ab73e62bfdc7d5014db72f3cb7392a5
[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::{Scalar, EvalResult, Pointer, 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_scalar(
12         &self,
13         val: Scalar,
14         src_ty: Ty<'tcx>,
15         dest_ty: Ty<'tcx>,
16     ) -> EvalResult<'tcx, Scalar> {
17         use rustc::ty::TypeVariants::*;
18         trace!("Casting {:?}: {:?} to {:?}", val, src_ty, dest_ty);
19
20         match val {
21             Scalar::Bits { defined: 0, .. } => Ok(val),
22             Scalar::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty),
23             Scalar::Bits { bits, .. } => {
24                 // TODO(oli-obk): check defined bits here
25                 match src_ty.sty {
26                     TyFloat(fty) => self.cast_from_float(bits, fty, dest_ty),
27                     _ => self.cast_from_int(bits, src_ty, dest_ty),
28                 }
29             }
30         }
31     }
32
33     fn cast_from_int(
34         &self,
35         v: u128,
36         src_ty: Ty<'tcx>,
37         dest_ty: Ty<'tcx>,
38     ) -> EvalResult<'tcx, Scalar> {
39         let signed = self.layout_of(src_ty)?.abi.is_signed();
40         let v = if signed {
41             self.sign_extend(v, src_ty)?
42         } else {
43             v
44         };
45         trace!("cast_from_int: {}, {}, {}", v, src_ty, dest_ty);
46         use rustc::ty::TypeVariants::*;
47         match dest_ty.sty {
48             TyInt(_) | TyUint(_) => {
49                 let v = self.truncate(v, dest_ty)?;
50                 Ok(Scalar::Bits {
51                     bits: v,
52                     defined: self.layout_of(dest_ty).unwrap().size.bits() as u8,
53                 })
54             }
55
56             TyFloat(FloatTy::F32) if signed => Ok(Scalar::Bits {
57                 bits: Single::from_i128(v as i128).value.to_bits(),
58                 defined: 32,
59             }),
60             TyFloat(FloatTy::F64) if signed => Ok(Scalar::Bits {
61                 bits: Double::from_i128(v as i128).value.to_bits(),
62                 defined: 64,
63             }),
64             TyFloat(FloatTy::F32) => Ok(Scalar::Bits {
65                 bits: Single::from_u128(v).value.to_bits(),
66                 defined: 32,
67             }),
68             TyFloat(FloatTy::F64) => Ok(Scalar::Bits {
69                 bits: Double::from_u128(v).value.to_bits(),
70                 defined: 64,
71             }),
72
73             TyChar if v as u8 as u128 == v => Ok(Scalar::Bits { bits: v, defined: 32 }),
74             TyChar => err!(InvalidChar(v)),
75
76             // No alignment check needed for raw pointers.  But we have to truncate to target ptr size.
77             TyRawPtr(_) => {
78                 Ok(Scalar::Bits {
79                     bits: self.memory.truncate_to_ptr(v).0 as u128,
80                     defined: self.memory.pointer_size().bits() as u8,
81                 })
82             },
83
84             // Casts to bool are not permitted by rustc, no need to handle them here.
85             _ => err!(Unimplemented(format!("int to {:?} cast", dest_ty))),
86         }
87     }
88
89     fn cast_from_float(&self, bits: u128, fty: FloatTy, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, Scalar> {
90         use rustc::ty::TypeVariants::*;
91         use rustc_apfloat::FloatConvert;
92         match dest_ty.sty {
93             // float -> uint
94             TyUint(t) => {
95                 let width = t.bit_width().unwrap_or(self.memory.pointer_size().bits() as usize);
96                 match fty {
97                     FloatTy::F32 => Ok(Scalar::Bits {
98                         bits: Single::from_bits(bits).to_u128(width).value,
99                         defined: width as u8,
100                     }),
101                     FloatTy::F64 => Ok(Scalar::Bits {
102                         bits: Double::from_bits(bits).to_u128(width).value,
103                         defined: width as u8,
104                     }),
105                 }
106             },
107             // float -> int
108             TyInt(t) => {
109                 let width = t.bit_width().unwrap_or(self.memory.pointer_size().bits() as usize);
110                 match fty {
111                     FloatTy::F32 => Ok(Scalar::Bits {
112                         bits: Single::from_bits(bits).to_i128(width).value as u128,
113                         defined: width as u8,
114                     }),
115                     FloatTy::F64 => Ok(Scalar::Bits {
116                         bits: Double::from_bits(bits).to_i128(width).value as u128,
117                         defined: width as u8,
118                     }),
119                 }
120             },
121             // f64 -> f32
122             TyFloat(FloatTy::F32) if fty == FloatTy::F64 => {
123                 Ok(Scalar::Bits {
124                     bits: Single::to_bits(Double::from_bits(bits).convert(&mut false).value),
125                     defined: 32,
126                 })
127             },
128             // f32 -> f64
129             TyFloat(FloatTy::F64) if fty == FloatTy::F32 => {
130                 Ok(Scalar::Bits {
131                     bits: Double::to_bits(Single::from_bits(bits).convert(&mut false).value),
132                     defined: 64,
133                 })
134             },
135             // identity cast
136             TyFloat(FloatTy:: F64) => Ok(Scalar::Bits {
137                 bits,
138                 defined: 64,
139             }),
140             TyFloat(FloatTy:: F32) => Ok(Scalar::Bits {
141                 bits,
142                 defined: 32,
143             }),
144             _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))),
145         }
146     }
147
148     fn cast_from_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Scalar> {
149         use rustc::ty::TypeVariants::*;
150         match ty.sty {
151             // Casting to a reference or fn pointer is not permitted by rustc, no need to support it here.
152             TyRawPtr(_) |
153             TyInt(IntTy::Isize) |
154             TyUint(UintTy::Usize) => Ok(ptr.into()),
155             TyInt(_) | TyUint(_) => err!(ReadPointerAsBytes),
156             _ => err!(Unimplemented(format!("ptr to {:?} cast", ty))),
157         }
158     }
159 }