diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index c08e4c896094b..04032847385e9 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -251,8 +251,28 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } "ptr_offset_from" => { - let a = self.read_immediate(args[0])?.to_scalar()?.to_ptr()?; - let b = self.read_immediate(args[1])?.to_scalar()?.to_ptr()?; + let isize_layout = self.layout_of(self.tcx.types.isize)?; + let a = self.read_immediate(args[0])?.to_scalar()?; + let b = self.read_immediate(args[1])?.to_scalar()?; + + // Special case: if both scalars are *equal integers* + // and not NULL, we pretend there is an allocation of size 0 right there, + // and their offset is 0. (There's never a valid object at NULL, making it an + // exception from the exception.) + // This is the dual to the special exception for offset-by-0 + // in the inbounds pointer offset operation (see the Miri code, `src/operator.rs`). + if a.is_bits() && b.is_bits() { + let a = a.to_usize(self)?; + let b = b.to_usize(self)?; + if a == b && a != 0 { + self.write_scalar(Scalar::from_int(0, isize_layout.size), dest)?; + return Ok(true); + } + } + + // General case: we need two pointers. + let a = self.force_ptr(a)?; + let b = self.force_ptr(b)?; if a.alloc_id != b.alloc_id { throw_ub_format!( "ptr_offset_from cannot compute offset of pointers into different \ @@ -266,7 +286,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { BinOp::Sub, a_offset, b_offset, )?; let pointee_layout = self.layout_of(substs.type_at(0))?; - let isize_layout = self.layout_of(self.tcx.types.isize)?; let val = ImmTy::from_scalar(val, isize_layout); let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout); self.exact_div(val, size, dest)?; diff --git a/src/test/ui/consts/offset_from.rs b/src/test/ui/consts/offset_from.rs index 4d6800681889c..8c1b27842628d 100644 --- a/src/test/ui/consts/offset_from.rs +++ b/src/test/ui/consts/offset_from.rs @@ -40,8 +40,14 @@ pub const OVERFLOW: isize = { unsafe { (base_ptr as *const u8).offset_from(field_ptr) } }; +pub const OFFSET_EQUAL_INTS: isize = { + let ptr = 1 as *const u8; + unsafe { ptr.offset_from(ptr) } +}; + fn main() { assert_eq!(OFFSET, 0); assert_eq!(OFFSET_2, 1); assert_eq!(OVERFLOW, -1); + assert_eq!(OFFSET_EQUAL_INTS, 0); } diff --git a/src/test/ui/consts/offset_from_ub.rs b/src/test/ui/consts/offset_from_ub.rs index 18b4d7271260f..c9030915620a8 100644 --- a/src/test/ui/consts/offset_from_ub.rs +++ b/src/test/ui/consts/offset_from_ub.rs @@ -25,13 +25,25 @@ pub const NOT_PTR: usize = { unsafe { (42 as *const u8).offset_from(&5u8) as usize } }; -pub const NOT_MULTIPLE_OF_SIZE: usize = { +pub const NOT_MULTIPLE_OF_SIZE: isize = { //~^ NOTE let data = [5u8, 6, 7]; let base_ptr = data.as_ptr(); let field_ptr = &data[1] as *const u8 as *const u16; - let offset = unsafe { field_ptr.offset_from(base_ptr as *const u16) }; - offset as usize + unsafe { field_ptr.offset_from(base_ptr as *const u16) } +}; + +pub const OFFSET_FROM_NULL: isize = { + //~^ NOTE + let ptr = 0 as *const u8; + unsafe { ptr.offset_from(ptr) } +}; + +pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC + //~^ NOTE + let ptr1 = 8 as *const u8; + let ptr2 = 16 as *const u8; + unsafe { ptr2.offset_from(ptr1) } }; fn main() {} diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr index 289128b4a170b..1bd09034bfc91 100644 --- a/src/test/ui/consts/offset_from_ub.stderr +++ b/src/test/ui/consts/offset_from_ub.stderr @@ -44,18 +44,55 @@ LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | exact_div: 1 cannot be divided by 2 without remainder - | inside call to `std::ptr::::offset_from` at $DIR/offset_from_ub.rs:33:27 + | inside call to `std::ptr::::offset_from` at $DIR/offset_from_ub.rs:33:14 | ::: $DIR/offset_from_ub.rs:28:1 | -LL | / pub const NOT_MULTIPLE_OF_SIZE: usize = { +LL | / pub const NOT_MULTIPLE_OF_SIZE: isize = { LL | | LL | | let data = [5u8, 6, 7]; LL | | let base_ptr = data.as_ptr(); -... | -LL | | offset as usize +LL | | let field_ptr = &data[1] as *const u8 as *const u16; +LL | | unsafe { field_ptr.offset_from(base_ptr as *const u16) } +LL | | }; + | |__- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL + | +LL | intrinsics::ptr_offset_from(self, origin) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | invalid use of NULL pointer + | inside call to `std::ptr::::offset_from` at $DIR/offset_from_ub.rs:39:14 + | + ::: $DIR/offset_from_ub.rs:36:1 + | +LL | / pub const OFFSET_FROM_NULL: isize = { +LL | | +LL | | let ptr = 0 as *const u8; +LL | | unsafe { ptr.offset_from(ptr) } +LL | | }; + | |__- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL + | +LL | intrinsics::ptr_offset_from(self, origin) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | a memory access tried to interpret some bytes as a pointer + | inside call to `std::ptr::::offset_from` at $DIR/offset_from_ub.rs:46:14 + | + ::: $DIR/offset_from_ub.rs:42:1 + | +LL | / pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC +LL | | +LL | | let ptr1 = 8 as *const u8; +LL | | let ptr2 = 16 as *const u8; +LL | | unsafe { ptr2.offset_from(ptr1) } LL | | }; | |__- -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors