support computing the remainder of a ptr, if covered by alignment

This commit is contained in:
Ralf Jung 2018-08-14 20:00:18 +02:00
parent 93fef9a6a2
commit 2b40d39c1e
5 changed files with 53 additions and 4 deletions

View File

@ -130,8 +130,8 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
err!(InvalidPointerMath)
}
}
// These work if one operand is a pointer, the other an integer
Add | BitAnd | Sub
// These work if the left operand is a pointer, the right an integer
Add | BitAnd | Sub | Rem
if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
left.is_ptr() && right.is_bits() => {
// Cast to i128 is fine as we checked the kind to be ptr-sized
@ -142,6 +142,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
left_kind == isize,
).map(Some)
}
// Commutative operators also work if the integer is on the left
Add | BitAnd
if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
left.is_bits() && right.is_ptr() => {
@ -180,7 +181,8 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
map_to_primval(left.overflowing_offset(Size::from_bytes(right as u64), self)),
BitAnd if !signed => {
let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align.abi() - 1);
let ptr_base_align = self.memory.get(left.alloc_id)?.align.abi();
let base_mask : u64 = !(ptr_base_align - 1);
let right = right as u64;
let ptr_size = self.memory.pointer_size().bytes() as u8;
if right & base_mask == base_mask {
@ -194,6 +196,23 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
}
}
Rem if !signed => {
// Doing modulo a multiple of the alignment is allowed
let ptr_base_align = self.memory.get(left.alloc_id)?.align.abi();
let right = right as u64;
let ptr_size = self.memory.pointer_size().bytes() as u8;
if right == 1 {
// modulo 1 is always 0
(Scalar::Bits { bits: 0, size: ptr_size }, false)
} else if right % ptr_base_align == 0 {
// the base address would be cancelled out by the modulo operation, so we can
// just take the modulo of the offset
(Scalar::Bits { bits: (left.offset.bytes() % right) as u128, size: ptr_size }, false)
} else {
return err!(ReadPointerAsBytes);
}
}
_ => {
let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
return err!(Unimplemented(msg));

View File

@ -3,6 +3,6 @@ fn main() {
let y = &x;
let z = &y as *const &i32 as *const usize;
let ptr_bytes = unsafe { *z }; // the actual deref is fine, because we read the entire pointer at once
let _ = ptr_bytes % 432; //~ ERROR constant evaluation error
let _ = ptr_bytes / 432; //~ ERROR constant evaluation error
//~^ NOTE tried to access part of a pointer value as raw bytes
}

View File

@ -0,0 +1,5 @@
fn main() {
let val = 13usize;
let addr = &val as *const _ as usize;
let _ = addr & 13; //~ ERROR access part of a pointer value as raw bytes
}

View File

@ -0,0 +1,5 @@
fn main() {
let val = 13usize;
let addr = &val as *const _ as usize;
let _ = addr % 2; //~ ERROR access part of a pointer value as raw bytes
}

View File

@ -0,0 +1,20 @@
fn main() {
let v = [1i16, 2];
let x = &v[1] as *const i16 as usize;
// arithmetic
let _y = x + 4;
let _y = 4 + x;
let _y = x - 2;
// bit-operations, covered by alignment
assert_eq!(x & 1, 0);
assert_eq!(x & 0, 0);
assert_eq!(1 & (x+1), 1);
let _y = !1 & x;
let _y = !0 & x;
let _y = x & !1;
// remainder, covered by alignment
assert_eq!(x % 2, 0);
assert_eq!((x+1) % 2, 1);
// remainder with 1 is always 0
assert_eq!(x % 1, 0);
}