- Ok((ptr, false))
- }
- // These need both to be pointer, and fail if they are not in the same location
- Lt | Le | Gt | Ge | Sub if left.is_ptr() && right.is_ptr() => {
- let left = left.to_ptr().expect("we checked is_ptr");
- let right = right.to_ptr().expect("we checked is_ptr");
- if left.alloc_id == right.alloc_id {
- let res = match bin_op {
- Lt => left.offset < right.offset,
- Le => left.offset <= right.offset,
- Gt => left.offset > right.offset,
- Ge => left.offset >= right.offset,
- Sub => {
- // subtract the offsets
- let left_offset = Scalar::from_uint(left.offset.bytes(), self.memory().pointer_size());
- let right_offset = Scalar::from_uint(right.offset.bytes(), self.memory().pointer_size());
- let layout = self.layout_of(self.tcx.types.usize)?;
- return self.binary_op(
- Sub,
- ImmTy::from_scalar(left_offset, layout),
- ImmTy::from_scalar(right_offset, layout),
- )
- }
- _ => bug!("We already established it has to be one of these operators."),
- };
- Ok((Scalar::from_bool(res), false))
- } else {
- // Both are pointers, but from different allocations.
- err!(InvalidPointerMath)
- }
- }
- Gt | Ge if left.is_ptr() && right.is_bits() => {
- // "ptr >[=] integer" can be tested if the integer is small enough.
- let left = left.to_ptr().expect("we checked is_ptr");
- let right = right.to_bits(self.memory().pointer_size()).expect("we checked is_bits");
- let (_alloc_size, alloc_align) = self.memory()
- .get_size_and_align(left.alloc_id, AllocCheck::MaybeDead)
- .expect("alloc info with MaybeDead cannot fail");
- let min_ptr_val = u128::from(alloc_align.bytes()) + u128::from(left.offset.bytes());
- let result = match bin_op {
- Gt => min_ptr_val > right,
- Ge => min_ptr_val >= right,
- _ => bug!(),
- };
- if result {
- // Definitely true!
- Ok((Scalar::from_bool(true), false))
- } else {
- // Sorry, can't tell.
- err!(InvalidPointerMath)
- }
- }
- // These work if the left operand is a pointer, and the right an integer
- Add | BitAnd | Sub | Rem if left.is_ptr() && right.is_bits() => {
- // Cast to i128 is fine as we checked the kind to be ptr-sized
- self.ptr_int_arithmetic(
- bin_op,
- left.to_ptr().expect("we checked is_ptr"),
- right.to_bits(self.memory().pointer_size()).expect("we checked is_bits"),
- right_layout.abi.is_signed(),
- )
- }
- // Commutative operators also work if the integer is on the left
- Add | BitAnd if left.is_bits() && right.is_ptr() => {
- // This is a commutative operation, just swap the operands
- self.ptr_int_arithmetic(
- bin_op,
- right.to_ptr().expect("we checked is_ptr"),
- left.to_bits(self.memory().pointer_size()).expect("we checked is_bits"),
- left_layout.abi.is_signed(),
- )
- }
- // Nothing else works
- _ => err!(InvalidPointerMath),
- }
- }
-
- fn ptr_eq(
- &self,
- left: Scalar<Tag>,
- right: Scalar<Tag>,
- ) -> InterpResult<'tcx, bool> {
- let size = self.pointer_size();
- Ok(match (left, right) {
- (Scalar::Raw { .. }, Scalar::Raw { .. }) =>
- left.to_bits(size)? == right.to_bits(size)?,
- (Scalar::Ptr(left), Scalar::Ptr(right)) => {
- // Comparison illegal if one of them is out-of-bounds, *unless* they
- // are in the same allocation.
- if left.alloc_id == right.alloc_id {
- left.offset == right.offset
- } else {
- // Make sure both pointers are in-bounds.
- // This accepts one-past-the end. Thus, there is still technically
- // some non-determinism that we do not fully rule out when two
- // allocations sit right next to each other. The C/C++ standards are
- // somewhat fuzzy about this case, so pragmatically speaking I think
- // for now this check is "good enough".
- // FIXME: Once we support intptrcast, we could try to fix these holes.
- // Dead allocations in miri cannot overlap with live allocations, but
- // on read hardware this can easily happen. Thus for comparisons we require
- // both pointers to be live.
- if self.pointer_inbounds(left).is_ok() && self.pointer_inbounds(right).is_ok() {
- // Two in-bounds pointers in different allocations are different.
- false
- } else {
- return err!(InvalidPointerMath);
- }
- }