about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs13
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs21
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs39
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_typeck/src/check/intrinsic.rs3
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/src/slice.rs2
-rw-r--r--library/alloc/src/vec/drain.rs2
-rw-r--r--library/alloc/src/vec/in_place_collect.rs2
-rw-r--r--library/alloc/src/vec/in_place_drop.rs2
-rw-r--r--library/alloc/src/vec/into_iter.rs2
-rw-r--r--library/core/src/intrinsics.rs13
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/core/src/ptr/const_ptr.rs77
-rw-r--r--library/core/src/ptr/mut_ptr.rs72
-rw-r--r--library/core/src/slice/raw.rs4
-rw-r--r--src/test/codegen/intrinsics/offset_from.rs36
-rw-r--r--src/test/ui/consts/offset_from.rs9
-rw-r--r--src/test/ui/consts/offset_from_ub.rs21
-rw-r--r--src/test/ui/consts/offset_from_ub.stderr14
20 files changed, 307 insertions, 28 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index d76dfca7960..f7a83373e87 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -713,14 +713,21 @@ fn codegen_regular_intrinsic_call<'tcx>(
             ret.write_cvalue(fx, val);
         };
 
-        ptr_offset_from, (v ptr, v base) {
+        ptr_offset_from | ptr_offset_from_unsigned, (v ptr, v base) {
             let ty = substs.type_at(0);
             let isize_layout = fx.layout_of(fx.tcx.types.isize);
 
             let pointee_size: u64 = fx.layout_of(ty).size.bytes();
-            let diff = fx.bcx.ins().isub(ptr, base);
+            let diff_bytes = fx.bcx.ins().isub(ptr, base);
             // FIXME this can be an exact division.
-            let val = CValue::by_val(fx.bcx.ins().sdiv_imm(diff, pointee_size as i64), isize_layout);
+            let diff = if intrinsic == sym::ptr_offset_from_unsigned {
+                // Because diff_bytes ULE isize::MAX, this would be fine as signed,
+                // but unsigned is slightly easier to codegen, so might as well.
+                fx.bcx.ins().udiv_imm(diff_bytes, pointee_size as i64)
+            } else {
+                fx.bcx.ins().sdiv_imm(diff_bytes, pointee_size as i64)
+            };
+            let val = CValue::by_val(diff, isize_layout);
             ret.write_cvalue(fx, val);
         };
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index f15c469ae57..6d6d3ae01f4 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -555,21 +555,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
             }
 
-            sym::ptr_offset_from => {
+            sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
                 let ty = substs.type_at(0);
                 let pointee_size = bx.layout_of(ty).size;
 
-                // This is the same sequence that Clang emits for pointer subtraction.
-                // It can be neither `nsw` nor `nuw` because the input is treated as
-                // unsigned but then the output is treated as signed, so neither works.
                 let a = args[0].immediate();
                 let b = args[1].immediate();
                 let a = bx.ptrtoint(a, bx.type_isize());
                 let b = bx.ptrtoint(b, bx.type_isize());
-                let d = bx.sub(a, b);
                 let pointee_size = bx.const_usize(pointee_size.bytes());
-                // this is where the signed magic happens (notice the `s` in `exactsdiv`)
-                bx.exactsdiv(d, pointee_size)
+                if name == sym::ptr_offset_from {
+                    // This is the same sequence that Clang emits for pointer subtraction.
+                    // It can be neither `nsw` nor `nuw` because the input is treated as
+                    // unsigned but then the output is treated as signed, so neither works.
+                    let d = bx.sub(a, b);
+                    // this is where the signed magic happens (notice the `s` in `exactsdiv`)
+                    bx.exactsdiv(d, pointee_size)
+                } else {
+                    // The `_unsigned` version knows the relative ordering of the pointers,
+                    // so can use `sub nuw` and `udiv exact` instead of dealing in signed.
+                    let d = bx.unchecked_usub(a, b);
+                    bx.exactudiv(d, pointee_size)
+                }
             }
 
             _ => {
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 3cc237faf69..59ea40dc2f9 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -308,7 +308,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self);
                 self.write_pointer(offset_ptr, dest)?;
             }
-            sym::ptr_offset_from => {
+            sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
                 let a = self.read_pointer(&args[0])?;
                 let b = self.read_pointer(&args[1])?;
 
@@ -330,8 +330,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         // Both are pointers. They must be into the same allocation.
                         if a_alloc_id != b_alloc_id {
                             throw_ub_format!(
-                                "ptr_offset_from cannot compute offset of pointers into different \
-                                allocations.",
+                                "{} cannot compute offset of pointers into different allocations.",
+                                intrinsic_name,
                             );
                         }
                         // And they must both be valid for zero-sized accesses ("in-bounds or one past the end").
@@ -348,16 +348,39 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             CheckInAllocMsg::OffsetFromTest,
                         )?;
 
+                        if intrinsic_name == sym::ptr_offset_from_unsigned && a_offset < b_offset {
+                            throw_ub_format!(
+                                "{} cannot compute a negative offset, but {} < {}",
+                                intrinsic_name,
+                                a_offset.bytes(),
+                                b_offset.bytes(),
+                            );
+                        }
+
                         // Compute offset.
                         let usize_layout = self.layout_of(self.tcx.types.usize)?;
                         let isize_layout = self.layout_of(self.tcx.types.isize)?;
-                        let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout);
-                        let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout);
-                        let (val, _overflowed, _ty) =
+                        let ret_layout = if intrinsic_name == sym::ptr_offset_from {
+                            isize_layout
+                        } else {
+                            usize_layout
+                        };
+
+                        // The subtraction is always done in `isize` to enforce
+                        // the "no more than `isize::MAX` apart" requirement.
+                        let a_offset = ImmTy::from_uint(a_offset.bytes(), isize_layout);
+                        let b_offset = ImmTy::from_uint(b_offset.bytes(), isize_layout);
+                        let (val, overflowed, _ty) =
                             self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?;
+                        if overflowed {
+                            throw_ub_format!("Pointers were too far apart for {}", intrinsic_name);
+                        }
+
                         let pointee_layout = self.layout_of(substs.type_at(0))?;
-                        let val = ImmTy::from_scalar(val, isize_layout);
-                        let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
+                        // This re-interprets an isize at ret_layout, but we already checked
+                        // that if ret_layout is usize, then the result must be non-negative.
+                        let val = ImmTy::from_scalar(val, ret_layout);
+                        let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout);
                         self.exact_div(&val, &size, dest)?;
                     }
                 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index d6885bebc32..2cc6eb03585 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1079,6 +1079,7 @@ symbols! {
         ptr_null,
         ptr_null_mut,
         ptr_offset_from,
+        ptr_offset_from_unsigned,
         pub_macro_rules,
         pub_restricted,
         pure,
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs
index 0dd8ee88ca2..b67185e5211 100644
--- a/compiler/rustc_typeck/src/check/intrinsic.rs
+++ b/compiler/rustc_typeck/src/check/intrinsic.rs
@@ -305,6 +305,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             sym::ptr_offset_from => {
                 (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize)
             }
+            sym::ptr_offset_from_unsigned => {
+                (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.usize)
+            }
             sym::unchecked_div | sym::unchecked_rem | sym::exact_div => {
                 (1, vec![param(0), param(0)], param(0))
             }
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index ecebc7ed9ac..fd21b367118 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -127,6 +127,7 @@
 #![feature(pattern)]
 #![feature(ptr_internals)]
 #![feature(ptr_metadata)]
+#![feature(ptr_sub_ptr)]
 #![feature(receiver_trait)]
 #![feature(set_ptr_value)]
 #![feature(slice_group_by)]
diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs
index 5bdf36a63a2..199b3c9d029 100644
--- a/library/alloc/src/slice.rs
+++ b/library/alloc/src/slice.rs
@@ -1056,7 +1056,7 @@ where
         fn drop(&mut self) {
             // `T` is not a zero-sized type, and these are pointers into a slice's elements.
             unsafe {
-                let len = self.end.offset_from(self.start) as usize;
+                let len = self.end.sub_ptr(self.start);
                 ptr::copy_nonoverlapping(self.start, self.dest, len);
             }
         }
diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs
index 1bff19d05c1..5cdee0bd4da 100644
--- a/library/alloc/src/vec/drain.rs
+++ b/library/alloc/src/vec/drain.rs
@@ -163,7 +163,7 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
             // it from the original vec but also avoid creating a &mut to the front since that could
             // invalidate raw pointers to it which some unsafe code might rely on.
             let vec_ptr = vec.as_mut().as_mut_ptr();
-            let drop_offset = drop_ptr.offset_from(vec_ptr) as usize;
+            let drop_offset = drop_ptr.sub_ptr(vec_ptr);
             let to_drop = ptr::slice_from_raw_parts_mut(vec_ptr.add(drop_offset), drop_len);
             ptr::drop_in_place(to_drop);
         }
diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs
index 282af8cc33f..55dcb84ad16 100644
--- a/library/alloc/src/vec/in_place_collect.rs
+++ b/library/alloc/src/vec/in_place_collect.rs
@@ -250,7 +250,7 @@ where
         let sink =
             self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).unwrap();
         // iteration succeeded, don't drop head
-        unsafe { ManuallyDrop::new(sink).dst.offset_from(dst_buf) as usize }
+        unsafe { ManuallyDrop::new(sink).dst.sub_ptr(dst_buf) }
     }
 }
 
diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs
index 354d25c2389..1b1ef9130fa 100644
--- a/library/alloc/src/vec/in_place_drop.rs
+++ b/library/alloc/src/vec/in_place_drop.rs
@@ -10,7 +10,7 @@ pub(super) struct InPlaceDrop<T> {
 
 impl<T> InPlaceDrop<T> {
     fn len(&self) -> usize {
-        unsafe { self.dst.offset_from(self.inner) as usize }
+        unsafe { self.dst.sub_ptr(self.inner) }
     }
 }
 
diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs
index a7df6f59b59..9b84a1d9b4b 100644
--- a/library/alloc/src/vec/into_iter.rs
+++ b/library/alloc/src/vec/into_iter.rs
@@ -169,7 +169,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
         let exact = if mem::size_of::<T>() == 0 {
             self.end.addr().wrapping_sub(self.ptr.addr())
         } else {
-            unsafe { self.end.offset_from(self.ptr) as usize }
+            unsafe { self.end.sub_ptr(self.ptr) }
         };
         (exact, Some(exact))
     }
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index d1647c38859..88e4262922d 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -1903,6 +1903,11 @@ extern "rust-intrinsic" {
     #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
     pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
 
+    /// See documentation of `<*const T>::sub_ptr` for details.
+    #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
+    #[cfg(not(bootstrap))]
+    pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
+
     /// See documentation of `<*const T>::guaranteed_eq` for details.
     ///
     /// Note that, unlike most intrinsics, this is safe to call;
@@ -2385,3 +2390,11 @@ where
 {
     called_in_const.call_once(arg)
 }
+
+/// Bootstrap polyfill
+#[cfg(bootstrap)]
+pub const unsafe fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize {
+    // SAFETY: we have stricter preconditions than `ptr_offset_from`, so can
+    // call it, and its output has to be positive, so we can just cast.
+    unsafe { ptr_offset_from(ptr, base) as _ }
+}
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 1612aa582ad..d1936b6b566 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -126,6 +126,7 @@
 #![feature(const_option)]
 #![feature(const_option_ext)]
 #![feature(const_pin)]
+#![feature(const_ptr_sub_ptr)]
 #![feature(const_replace)]
 #![feature(const_ptr_as_ref)]
 #![feature(const_ptr_is_null)]
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 45964c3a444..028adc796e5 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -611,6 +611,83 @@ impl<T: ?Sized> *const T {
         unsafe { intrinsics::ptr_offset_from(self, origin) }
     }
 
+    /// Calculates the distance between two pointers, *where it's known that
+    /// `self` is equal to or greater than `origin`*. The returned value is in
+    /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+    ///
+    /// This computes the same value that [`offset_from`](#method.offset_from)
+    /// would compute, but with the added precondition that that the offset is
+    /// guaranteed to be non-negative.  This method is equivalent to
+    /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+    /// but it provides slightly more information to the optimizer, which can
+    /// sometimes allow it to optimize slightly better with some backends.
+    ///
+    /// This method can be though of as recovering the `count` that was passed
+    /// to [`add`](#method.add) (or, with the parameters in the other order,
+    /// to [`sub`](#method.sub)).  The following are all equivalent, assuming
+    /// that their safety preconditions are met:
+    /// ```rust
+    /// # #![feature(ptr_sub_ptr)]
+    /// # unsafe fn blah(ptr: *const i32, origin: *const i32, count: usize) -> bool {
+    /// ptr.sub_ptr(origin) == count
+    /// # &&
+    /// origin.add(count) == ptr
+    /// # &&
+    /// ptr.sub(count) == origin
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// - The distance between the pointers must be non-negative (`self >= origin`)
+    ///
+    /// - *All* the safety conditions of [`offset_from`](#method.offset_from)
+    ///   apply to this method as well; see it for the full details.
+    ///
+    /// Importantly, despite the return type of this method being able to represent
+    /// a larger offset, it's still *not permitted* to pass pointers which differ
+    /// by more than `isize::MAX` *bytes*.  As such, the result of this method will
+    /// always be less than or equal to `isize::MAX as usize`.
+    ///
+    /// # Panics
+    ///
+    /// This function panics if `T` is a Zero-Sized Type ("ZST").
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(ptr_sub_ptr)]
+    ///
+    /// let a = [0; 5];
+    /// let ptr1: *const i32 = &a[1];
+    /// let ptr2: *const i32 = &a[3];
+    /// unsafe {
+    ///     assert_eq!(ptr2.sub_ptr(ptr1), 2);
+    ///     assert_eq!(ptr1.add(2), ptr2);
+    ///     assert_eq!(ptr2.sub(2), ptr1);
+    ///     assert_eq!(ptr2.sub_ptr(ptr2), 0);
+    /// }
+    ///
+    /// // This would be incorrect, as the pointers are not correctly ordered:
+    /// // ptr1.offset_from(ptr2)
+    /// ```
+    #[unstable(feature = "ptr_sub_ptr", issue = "95892")]
+    #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")]
+    #[inline]
+    pub const unsafe fn sub_ptr(self, origin: *const T) -> usize
+    where
+        T: Sized,
+    {
+        // SAFETY: The comparison has no side-effects, and the intrinsic
+        // does this check internally in the CTFE implementation.
+        unsafe { assert_unsafe_precondition!(self >= origin) };
+
+        let pointee_size = mem::size_of::<T>();
+        assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
+        // SAFETY: the caller must uphold the safety contract for `ptr_offset_from_unsigned`.
+        unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
+    }
+
     /// Returns whether two pointers are guaranteed to be equal.
     ///
     /// At runtime this function behaves like `self == other`.
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index ed80cdc9bf9..1a32dd62dfd 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -787,6 +787,78 @@ impl<T: ?Sized> *mut T {
         unsafe { (self as *const T).offset_from(origin) }
     }
 
+    /// Calculates the distance between two pointers, *where it's known that
+    /// `self` is equal to or greater than `origin`*. The returned value is in
+    /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+    ///
+    /// This computes the same value that [`offset_from`](#method.offset_from)
+    /// would compute, but with the added precondition that that the offset is
+    /// guaranteed to be non-negative.  This method is equivalent to
+    /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+    /// but it provides slightly more information to the optimizer, which can
+    /// sometimes allow it to optimize slightly better with some backends.
+    ///
+    /// This method can be though of as recovering the `count` that was passed
+    /// to [`add`](#method.add) (or, with the parameters in the other order,
+    /// to [`sub`](#method.sub)).  The following are all equivalent, assuming
+    /// that their safety preconditions are met:
+    /// ```rust
+    /// # #![feature(ptr_sub_ptr)]
+    /// # unsafe fn blah(ptr: *mut i32, origin: *mut i32, count: usize) -> bool {
+    /// ptr.sub_ptr(origin) == count
+    /// # &&
+    /// origin.add(count) == ptr
+    /// # &&
+    /// ptr.sub(count) == origin
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// - The distance between the pointers must be non-negative (`self >= origin`)
+    ///
+    /// - *All* the safety conditions of [`offset_from`](#method.offset_from)
+    ///   apply to this method as well; see it for the full details.
+    ///
+    /// Importantly, despite the return type of this method being able to represent
+    /// a larger offset, it's still *not permitted* to pass pointers which differ
+    /// by more than `isize::MAX` *bytes*.  As such, the result of this method will
+    /// always be less than or equal to `isize::MAX as usize`.
+    ///
+    /// # Panics
+    ///
+    /// This function panics if `T` is a Zero-Sized Type ("ZST").
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(ptr_sub_ptr)]
+    ///
+    /// let mut a = [0; 5];
+    /// let p: *mut i32 = a.as_mut_ptr();
+    /// unsafe {
+    ///     let ptr1: *mut i32 = p.add(1);
+    ///     let ptr2: *mut i32 = p.add(3);
+    ///
+    ///     assert_eq!(ptr2.sub_ptr(ptr1), 2);
+    ///     assert_eq!(ptr1.add(2), ptr2);
+    ///     assert_eq!(ptr2.sub(2), ptr1);
+    ///     assert_eq!(ptr2.sub_ptr(ptr2), 0);
+    /// }
+    ///
+    /// // This would be incorrect, as the pointers are not correctly ordered:
+    /// // ptr1.offset_from(ptr2)
+    #[unstable(feature = "ptr_sub_ptr", issue = "95892")]
+    #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")]
+    #[inline]
+    pub const unsafe fn sub_ptr(self, origin: *const T) -> usize
+    where
+        T: Sized,
+    {
+        // SAFETY: the caller must uphold the safety contract for `sub_ptr`.
+        unsafe { (self as *const T).sub_ptr(origin) }
+    }
+
     /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
     ///
     /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs
index 1b88573035d..6bc60b04b5c 100644
--- a/library/core/src/slice/raw.rs
+++ b/library/core/src/slice/raw.rs
@@ -215,7 +215,7 @@ pub const fn from_mut<T>(s: &mut T) -> &mut [T] {
 #[unstable(feature = "slice_from_ptr_range", issue = "89792")]
 pub unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] {
     // SAFETY: the caller must uphold the safety contract for `from_ptr_range`.
-    unsafe { from_raw_parts(range.start, range.end.offset_from(range.start) as usize) }
+    unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
 }
 
 /// Performs the same functionality as [`from_ptr_range`], except that a
@@ -265,5 +265,5 @@ pub unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] {
 #[unstable(feature = "slice_from_ptr_range", issue = "89792")]
 pub unsafe fn from_mut_ptr_range<'a, T>(range: Range<*mut T>) -> &'a mut [T] {
     // SAFETY: the caller must uphold the safety contract for `from_mut_ptr_range`.
-    unsafe { from_raw_parts_mut(range.start, range.end.offset_from(range.start) as usize) }
+    unsafe { from_raw_parts_mut(range.start, range.end.sub_ptr(range.start)) }
 }
diff --git a/src/test/codegen/intrinsics/offset_from.rs b/src/test/codegen/intrinsics/offset_from.rs
new file mode 100644
index 00000000000..d0de4c8355d
--- /dev/null
+++ b/src/test/codegen/intrinsics/offset_from.rs
@@ -0,0 +1,36 @@
+// compile-flags: -C opt-level=1
+// only-64bit (because we're using [ui]size)
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+//! Basic optimizations are enabled because otherwise `x86_64-gnu-nopt` had an alloca.
+//! Uses a type with non-power-of-two size to avoid normalizations to shifts.
+
+use std::intrinsics::*;
+
+type RGB = [u8; 3];
+
+// CHECK-LABEL: @offset_from_odd_size
+#[no_mangle]
+pub unsafe fn offset_from_odd_size(a: *const RGB, b: *const RGB) -> isize {
+    // CHECK: start
+    // CHECK-NEXT: ptrtoint
+    // CHECK-NEXT: ptrtoint
+    // CHECK-NEXT: sub i64
+    // CHECK-NEXT: sdiv exact i64 %{{[0-9]+}}, 3
+    // CHECK-NEXT: ret i64
+    ptr_offset_from(a, b)
+}
+
+// CHECK-LABEL: @offset_from_unsigned_odd_size
+#[no_mangle]
+pub unsafe fn offset_from_unsigned_odd_size(a: *const RGB, b: *const RGB) -> usize {
+    // CHECK: start
+    // CHECK-NEXT: ptrtoint
+    // CHECK-NEXT: ptrtoint
+    // CHECK-NEXT: sub nuw i64
+    // CHECK-NEXT: udiv exact i64 %{{[0-9]+}}, 3
+    // CHECK-NEXT: ret i64
+    ptr_offset_from_unsigned(a, b)
+}
diff --git a/src/test/ui/consts/offset_from.rs b/src/test/ui/consts/offset_from.rs
index 4c9b1c1571d..b53718316f3 100644
--- a/src/test/ui/consts/offset_from.rs
+++ b/src/test/ui/consts/offset_from.rs
@@ -1,6 +1,8 @@
 // run-pass
 
 #![feature(const_ptr_offset_from)]
+#![feature(const_ptr_sub_ptr)]
+#![feature(ptr_sub_ptr)]
 
 struct Struct {
     field: (),
@@ -43,9 +45,16 @@ pub const OFFSET_EQUAL_INTS: isize = {
     unsafe { ptr.offset_from(ptr) }
 };
 
+pub const OFFSET_UNSIGNED: usize = {
+    let a = ['a', 'b', 'c'];
+    let ptr = a.as_ptr();
+    unsafe { ptr.add(2).sub_ptr(ptr) }
+};
+
 fn main() {
     assert_eq!(OFFSET, 0);
     assert_eq!(OFFSET_2, 1);
     assert_eq!(OVERFLOW, -1);
     assert_eq!(OFFSET_EQUAL_INTS, 0);
+    assert_eq!(OFFSET_UNSIGNED, 2);
 }
diff --git a/src/test/ui/consts/offset_from_ub.rs b/src/test/ui/consts/offset_from_ub.rs
index 939c1e31f9a..f604f57e39d 100644
--- a/src/test/ui/consts/offset_from_ub.rs
+++ b/src/test/ui/consts/offset_from_ub.rs
@@ -1,7 +1,7 @@
 #![feature(const_ptr_offset_from)]
 #![feature(core_intrinsics)]
 
-use std::intrinsics::ptr_offset_from;
+use std::intrinsics::{ptr_offset_from, ptr_offset_from_unsigned};
 
 #[repr(C)]
 struct Struct {
@@ -15,7 +15,7 @@ pub const DIFFERENT_ALLOC: usize = {
     let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
     let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
     let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed
-    //~| cannot compute offset of pointers into different allocations.
+    //~| ptr_offset_from cannot compute offset of pointers into different allocations.
     offset as usize
 };
 
@@ -70,4 +70,21 @@ const OUT_OF_BOUNDS_SAME: isize = {
     //~| pointer at offset 10 is out-of-bounds
 };
 
+pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
+    let uninit = std::mem::MaybeUninit::<Struct>::uninit();
+    let base_ptr: *const Struct = &uninit as *const _ as *const Struct;
+    let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
+    let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
+    let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed
+    //~| ptr_offset_from_unsigned cannot compute offset of pointers into different allocations.
+    offset as usize
+};
+
+const WRONG_ORDER_UNSIGNED: usize = {
+    let a = ['a', 'b', 'c'];
+    let p = a.as_ptr();
+    unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } //~ERROR evaluation of constant value failed
+    //~| cannot compute a negative offset, but 0 < 8
+};
+
 fn main() {}
diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr
index 293a2b47d30..4c98fd72cac 100644
--- a/src/test/ui/consts/offset_from_ub.stderr
+++ b/src/test/ui/consts/offset_from_ub.stderr
@@ -54,6 +54,18 @@ error[E0080]: evaluation of constant value failed
 LL |     unsafe { ptr_offset_from(end_ptr, end_ptr) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc26 has size 4, so pointer at offset 10 is out-of-bounds
 
-error: aborting due to 8 previous errors
+error[E0080]: evaluation of constant value failed
+  --> $DIR/offset_from_ub.rs:78:27
+   |
+LL |     let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) };
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute offset of pointers into different allocations.
+
+error[E0080]: evaluation of constant value failed
+  --> $DIR/offset_from_ub.rs:86:14
+   |
+LL |     unsafe { ptr_offset_from_unsigned(p, p.add(2) ) }
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute a negative offset, but 0 < 8
+
+error: aborting due to 10 previous errors
 
 For more information about this error, try `rustc --explain E0080`.