]> git.lizzy.rs Git - rust.git/blob - src/operator.rs
7be77771a7cafea4a745be212c9d0fdc75b7840e
[rust.git] / src / operator.rs
1 use rustc::ty;
2 use rustc::ty::layout::Primitive;
3 use rustc::mir;
4
5 use super::*;
6
7 use helpers::EvalContextExt as HelperEvalContextExt;
8
9 pub trait EvalContextExt<'tcx> {
10     fn ptr_op(
11         &self,
12         bin_op: mir::BinOp,
13         left: Scalar,
14         left_ty: ty::Ty<'tcx>,
15         right: Scalar,
16         right_ty: ty::Ty<'tcx>,
17     ) -> EvalResult<'tcx, Option<(Scalar, bool)>>;
18
19     fn ptr_int_arithmetic(
20         &self,
21         bin_op: mir::BinOp,
22         left: Pointer,
23         right: i128,
24         signed: bool,
25     ) -> EvalResult<'tcx, (Scalar, bool)>;
26 }
27
28 impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super::Evaluator<'tcx>> {
29     fn ptr_op(
30         &self,
31         bin_op: mir::BinOp,
32         left: Scalar,
33         left_ty: ty::Ty<'tcx>,
34         right: Scalar,
35         right_ty: ty::Ty<'tcx>,
36     ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
37         trace!("ptr_op: {:?} {:?} {:?}", left, bin_op, right);
38
39         use rustc::mir::BinOp::*;
40         use rustc::ty::layout::Integer::*;
41         let usize = Primitive::Int(match self.memory.pointer_size().bytes() {
42             1 => I8,
43             2 => I16,
44             4 => I32,
45             8 => I64,
46             16 => I128,
47             _ => unreachable!(),
48         }, false);
49         let isize = Primitive::Int(match self.memory.pointer_size().bytes() {
50             1 => I8,
51             2 => I16,
52             4 => I32,
53             8 => I64,
54             16 => I128,
55             _ => unreachable!(),
56         }, true);
57         let left_layout = self.layout_of(left_ty)?;
58         let left_kind = match left_layout.abi {
59             ty::layout::Abi::Scalar(ref scalar) => scalar.value,
60             _ => Err(EvalErrorKind::TypeNotPrimitive(left_ty))?,
61         };
62         let right_layout = self.layout_of(right_ty)?;
63         let right_kind = match right_layout.abi {
64             ty::layout::Abi::Scalar(ref scalar) => scalar.value,
65             _ => Err(EvalErrorKind::TypeNotPrimitive(right_ty))?,
66         };
67         match bin_op {
68             Offset if left_kind == Primitive::Pointer && right_kind == usize => {
69                 let pointee_ty = left_ty
70                     .builtin_deref(true)
71                     .expect("Offset called on non-ptr type")
72                     .ty;
73                 let ptr = self.pointer_offset(
74                     left,
75                     pointee_ty,
76                     right.to_bits(self.memory.pointer_size())? as i64,
77                 )?;
78                 Ok(Some((ptr, false)))
79             }
80             // These work on anything
81             Eq if left_kind == right_kind => {
82                 let result = match (left, right) {
83                     (Scalar::Bits { .. }, Scalar::Bits { .. }) => {
84                         left.to_bits(left_layout.size)? == right.to_bits(right_layout.size)?
85                     },
86                     // FIXME: Test if both allocations are still live *or* if they are in the same allocation? (same for Ne below)
87                     (Scalar::Ptr(left), Scalar::Ptr(right)) => left == right,
88                     // FIXME: We should probably error out when comparing anything but NULL with a pointer (same for Ne below)
89                     _ => false,
90                 };
91                 Ok(Some((Scalar::from_bool(result), false)))
92             }
93             Ne if left_kind == right_kind => {
94                 let result = match (left, right) {
95                     (Scalar::Bits { .. }, Scalar::Bits { .. }) => {
96                         left.to_bits(left_layout.size)? != right.to_bits(right_layout.size)?
97                     },
98                     (Scalar::Ptr(left), Scalar::Ptr(right)) => left != right,
99                     _ => true,
100                 };
101                 Ok(Some((Scalar::from_bool(result), false)))
102             }
103             // These need both pointers to be in the same allocation
104             Lt | Le | Gt | Ge | Sub
105                 if left_kind == right_kind &&
106                        (left_kind == Primitive::Pointer || left_kind == usize || left_kind == isize) &&
107                        left.is_ptr() && right.is_ptr() => {
108                 let left = left.to_ptr()?;
109                 let right = right.to_ptr()?;
110                 if left.alloc_id == right.alloc_id {
111                     let res = match bin_op {
112                         Lt => left.offset < right.offset,
113                         Le => left.offset <= right.offset,
114                         Gt => left.offset > right.offset,
115                         Ge => left.offset >= right.offset,
116                         Sub => {
117                             return self.binary_op(
118                                 Sub,
119                                 Scalar::Bits { bits: left.offset.bytes() as u128, size: self.memory.pointer_size().bytes() as u8 },
120                                 self.tcx.types.usize,
121                                 Scalar::Bits { bits: right.offset.bytes() as u128, size: self.memory.pointer_size().bytes() as u8 },
122                                 self.tcx.types.usize,
123                             ).map(Some)
124                         }
125                         _ => bug!("We already established it has to be one of these operators."),
126                     };
127                     Ok(Some((Scalar::from_bool(res), false)))
128                 } else {
129                     // Both are pointers, but from different allocations.
130                     err!(InvalidPointerMath)
131                 }
132             }
133             // These work if one operand is a pointer, the other an integer
134             Add | BitAnd | Sub
135                 if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
136                        left.is_ptr() && right.is_bits() => {
137                 // Cast to i128 is fine as we checked the kind to be ptr-sized
138                 self.ptr_int_arithmetic(
139                     bin_op,
140                     left.to_ptr()?,
141                     right.to_bits(self.memory.pointer_size())? as i128,
142                     left_kind == isize,
143                 ).map(Some)
144             }
145             Add | BitAnd
146                 if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
147                        left.is_bits() && right.is_ptr() => {
148                 // This is a commutative operation, just swap the operands
149                 self.ptr_int_arithmetic(
150                     bin_op,
151                     right.to_ptr()?,
152                     left.to_bits(self.memory.pointer_size())? as i128,
153                     left_kind == isize,
154                 ).map(Some)
155             }
156             _ => Ok(None),
157         }
158     }
159
160     fn ptr_int_arithmetic(
161         &self,
162         bin_op: mir::BinOp,
163         left: Pointer,
164         right: i128,
165         signed: bool,
166     ) -> EvalResult<'tcx, (Scalar, bool)> {
167         use rustc::mir::BinOp::*;
168
169         fn map_to_primval((res, over): (Pointer, bool)) -> (Scalar, bool) {
170             (Scalar::Ptr(res), over)
171         }
172
173         Ok(match bin_op {
174             Sub =>
175                 // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
176                 map_to_primval(left.overflowing_signed_offset(-right, self)),
177             Add if signed =>
178                 map_to_primval(left.overflowing_signed_offset(right, self)),
179             Add if !signed =>
180                 map_to_primval(left.overflowing_offset(Size::from_bytes(right as u64), self)),
181
182             BitAnd if !signed => {
183                 let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align.abi() - 1);
184                 let right = right as u64;
185                 let ptr_size = self.memory.pointer_size().bytes() as u8;
186                 if right & base_mask == base_mask {
187                     // Case 1: The base address bits are all preserved, i.e., right is all-1 there
188                     (Scalar::Ptr(Pointer::new(left.alloc_id, Size::from_bytes(left.offset.bytes() & right))), false)
189                 } else if right & base_mask == 0 {
190                     // Case 2: The base address bits are all taken away, i.e., right is all-0 there
191                     (Scalar::Bits { bits: (left.offset.bytes() & right) as u128, size: ptr_size }, false)
192                 } else {
193                     return err!(ReadPointerAsBytes);
194                 }
195             }
196
197             _ => {
198                 let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
199                 return err!(Unimplemented(msg));
200             }
201         })
202     }
203 }