2 use rustc::ty::layout::Primitive;
7 use helpers::EvalContextExt as HelperEvalContextExt;
9 pub trait EvalContextExt<'tcx> {
14 left_ty: ty::Ty<'tcx>,
16 right_ty: ty::Ty<'tcx>,
17 ) -> EvalResult<'tcx, Option<(Scalar, bool)>>;
19 fn ptr_int_arithmetic(
25 ) -> EvalResult<'tcx, (Scalar, bool)>;
28 impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super::Evaluator<'tcx>> {
33 left_ty: ty::Ty<'tcx>,
35 right_ty: ty::Ty<'tcx>,
36 ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
37 trace!("ptr_op: {:?} {:?} {:?}", left, bin_op, right);
39 use rustc::mir::BinOp::*;
40 use rustc::ty::layout::Integer::*;
41 let usize = Primitive::Int(match self.memory.pointer_size().bytes() {
49 let isize = Primitive::Int(match self.memory.pointer_size().bytes() {
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))?,
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))?,
68 Offset if left_kind == Primitive::Pointer && right_kind == usize => {
69 let pointee_ty = left_ty
71 .expect("Offset called on non-ptr type")
73 let ptr = self.pointer_offset(
76 right.to_bits(self.memory.pointer_size())? as i64,
78 Ok(Some((ptr, false)))
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)?
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)
91 Ok(Some((Scalar::from_bool(result), false)))
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)?
98 (Scalar::Ptr(left), Scalar::Ptr(right)) => left != right,
101 Ok(Some((Scalar::from_bool(result), false)))
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,
117 return self.binary_op(
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,
125 _ => bug!("We already established it has to be one of these operators."),
127 Ok(Some((Scalar::from_bool(res), false)))
129 // Both are pointers, but from different allocations.
130 err!(InvalidPointerMath)
133 // These work if one operand is a pointer, the other an integer
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(
141 right.to_bits(self.memory.pointer_size())? as i128,
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(
152 left.to_bits(self.memory.pointer_size())? as i128,
160 fn ptr_int_arithmetic(
166 ) -> EvalResult<'tcx, (Scalar, bool)> {
167 use rustc::mir::BinOp::*;
169 fn map_to_primval((res, over): (Pointer, bool)) -> (Scalar, bool) {
170 (Scalar::Ptr(res), over)
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)),
178 map_to_primval(left.overflowing_signed_offset(right, self)),
180 map_to_primval(left.overflowing_offset(Size::from_bytes(right as u64), self)),
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)
193 return err!(ReadPointerAsBytes);
198 let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
199 return err!(Unimplemented(msg));