Skip to content

Commit 9d606d9

Browse files
committed
Auto merge of #74238 - RalfJung:offset_from, r=oli-obk
stabilize ptr_offset_from This stabilizes ptr::offset_from, and closes #41079. It also removes the deprecated `wrapping_offset_from`. This function was deprecated 19 days ago and was never stable; given an FCP of 10 days and some waiting time until FCP starts, that leaves at least a month between deprecation and removal which I think is fine for a nightly-only API. Regarding the open questions in #41079: * Should offset_from abort instead of panic on ZSTs? -- As far as I know, there is no precedent for such aborts. We could, however, declare this UB. Given that the size is always known statically and the check thus rather cheap, UB seems excessive. * Should there be more methods like this with different restrictions (to allow nuw/nsw, perhaps) or that return usize (like how isize-taking offset is more conveniently done with usize-taking add these days)? -- No reason to block stabilization on that, we can always add such methods later. Also nominating the lang team because this exposes an intrinsic. The stabilized method is best described [by its doc-comment](https://github.com/RalfJung/rust/blob/56d4b2d69abb93e4f0ca79471deca7aaaaeca214/src/libcore/ptr/const_ptr.rs#L227). The documentation forgot to mention the requirement that both pointers must "have the same provenance", aka "be derived from pointers to the same allocation", which I am adding in this PR. This is a precondition that [Miri already implements](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=a3b9d0a07a01321f5202cd99e9613480) and that, should LLVM ever obtain a `psub` operation to subtract pointers, will likely be required for that operation (following the semantics in [this paper](https://people.mpi-sws.org/~jung/twinsem/twinsem.pdf)).
2 parents b88434e + 4129e07 commit 9d606d9

File tree

10 files changed

+57
-141
lines changed

10 files changed

+57
-141
lines changed

library/alloc/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@
113113
#![feature(or_patterns)]
114114
#![feature(pattern)]
115115
#![feature(ptr_internals)]
116-
#![feature(ptr_offset_from)]
117116
#![feature(raw_ref_op)]
118117
#![feature(rustc_attrs)]
119118
#![feature(receiver_trait)]

library/core/src/ptr/const_ptr.rs

+23-63
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,8 @@ impl<T: ?Sized> *const T {
240240
/// different allocated object. Note that in Rust,
241241
/// every (stack-allocated) variable is considered a separate allocated object.
242242
///
243-
/// In other words, `x.wrapping_offset(y.wrapping_offset_from(x))` is
244-
/// *not* the same as `y`, and dereferencing it is undefined behavior
243+
/// In other words, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::<T>())`
244+
/// is *not* the same as `y`, and dereferencing it is undefined behavior
245245
/// unless `x` and `y` point into the same allocated object.
246246
///
247247
/// Compared to [`offset`], this method basically delays the requirement of staying
@@ -292,7 +292,6 @@ impl<T: ?Sized> *const T {
292292
/// This function is the inverse of [`offset`].
293293
///
294294
/// [`offset`]: #method.offset
295-
/// [`wrapping_offset_from`]: #method.wrapping_offset_from
296295
///
297296
/// # Safety
298297
///
@@ -303,6 +302,9 @@ impl<T: ?Sized> *const T {
303302
/// byte past the end of the same allocated object. Note that in Rust,
304303
/// every (stack-allocated) variable is considered a separate allocated object.
305304
///
305+
/// * Both pointers must be *derived from* a pointer to the same object.
306+
/// (See below for an example.)
307+
///
306308
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
307309
///
308310
/// * The distance between the pointers, in bytes, must be an exact multiple
@@ -323,10 +325,6 @@ impl<T: ?Sized> *const T {
323325
/// Extension. As such, memory acquired directly from allocators or memory
324326
/// mapped files *may* be too large to handle with this function.
325327
///
326-
/// Consider using [`wrapping_offset_from`] instead if these constraints are
327-
/// difficult to satisfy. The only advantage of this method is that it
328-
/// enables more aggressive compiler optimizations.
329-
///
330328
/// # Panics
331329
///
332330
/// This function panics if `T` is a Zero-Sized Type ("ZST").
@@ -336,8 +334,6 @@ impl<T: ?Sized> *const T {
336334
/// Basic usage:
337335
///
338336
/// ```
339-
/// #![feature(ptr_offset_from)]
340-
///
341337
/// let a = [0; 5];
342338
/// let ptr1: *const i32 = &a[1];
343339
/// let ptr2: *const i32 = &a[3];
@@ -348,7 +344,24 @@ impl<T: ?Sized> *const T {
348344
/// assert_eq!(ptr2.offset(-2), ptr1);
349345
/// }
350346
/// ```
351-
#[unstable(feature = "ptr_offset_from", issue = "41079")]
347+
///
348+
/// *Incorrect* usage:
349+
///
350+
/// ```rust,no_run
351+
/// let ptr1 = Box::into_raw(Box::new(0u8)) as *const u8;
352+
/// let ptr2 = Box::into_raw(Box::new(1u8)) as *const u8;
353+
/// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize);
354+
/// // Make ptr2_other an "alias" of ptr2, but derived from ptr1.
355+
/// let ptr2_other = (ptr1 as *const u8).wrapping_offset(diff);
356+
/// assert_eq!(ptr2 as usize, ptr2_other as usize);
357+
/// // Since ptr2_other and ptr2 are derived from pointers to different objects,
358+
/// // computing their offset is undefined behavior, even though
359+
/// // they point to the same address!
360+
/// unsafe {
361+
/// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior
362+
/// }
363+
/// ```
364+
#[stable(feature = "ptr_offset_from", since = "1.47.0")]
352365
#[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")]
353366
#[inline]
354367
pub const unsafe fn offset_from(self, origin: *const T) -> isize
@@ -423,59 +436,6 @@ impl<T: ?Sized> *const T {
423436
intrinsics::ptr_guaranteed_ne(self, other)
424437
}
425438

426-
/// Calculates the distance between two pointers. The returned value is in
427-
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
428-
///
429-
/// If the address different between the two pointers is not a multiple of
430-
/// `mem::size_of::<T>()` then the result of the division is rounded towards
431-
/// zero.
432-
///
433-
/// Though this method is safe for any two pointers, note that its result
434-
/// will be mostly useless if the two pointers aren't into the same allocated
435-
/// object, for example if they point to two different local variables.
436-
///
437-
/// # Panics
438-
///
439-
/// This function panics if `T` is a zero-sized type.
440-
///
441-
/// # Examples
442-
///
443-
/// Basic usage:
444-
///
445-
/// ```
446-
/// #![feature(ptr_wrapping_offset_from)]
447-
///
448-
/// let a = [0; 5];
449-
/// let ptr1: *const i32 = &a[1];
450-
/// let ptr2: *const i32 = &a[3];
451-
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2);
452-
/// assert_eq!(ptr1.wrapping_offset_from(ptr2), -2);
453-
/// assert_eq!(ptr1.wrapping_offset(2), ptr2);
454-
/// assert_eq!(ptr2.wrapping_offset(-2), ptr1);
455-
///
456-
/// let ptr1: *const i32 = 3 as _;
457-
/// let ptr2: *const i32 = 13 as _;
458-
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2);
459-
/// ```
460-
#[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")]
461-
#[rustc_deprecated(
462-
since = "1.46.0",
463-
reason = "Pointer distances across allocation \
464-
boundaries are not typically meaningful. \
465-
Use integer subtraction if you really need this."
466-
)]
467-
#[inline]
468-
pub fn wrapping_offset_from(self, origin: *const T) -> isize
469-
where
470-
T: Sized,
471-
{
472-
let pointee_size = mem::size_of::<T>();
473-
assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
474-
475-
let d = isize::wrapping_sub(self as _, origin as _);
476-
d.wrapping_div(pointee_size as _)
477-
}
478-
479439
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
480440
///
481441
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer

library/core/src/ptr/mut_ptr.rs

+23-60
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ impl<T: ?Sized> *mut T {
246246
/// different allocated object. Note that in Rust,
247247
/// every (stack-allocated) variable is considered a separate allocated object.
248248
///
249-
/// In other words, `x.wrapping_offset(y.wrapping_offset_from(x))` is
250-
/// *not* the same as `y`, and dereferencing it is undefined behavior
249+
/// In other words, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::<T>())`
250+
/// is *not* the same as `y`, and dereferencing it is undefined behavior
251251
/// unless `x` and `y` point into the same allocated object.
252252
///
253253
/// Compared to [`offset`], this method basically delays the requirement of staying
@@ -463,7 +463,6 @@ impl<T: ?Sized> *mut T {
463463
/// This function is the inverse of [`offset`].
464464
///
465465
/// [`offset`]: #method.offset-1
466-
/// [`wrapping_offset_from`]: #method.wrapping_offset_from-1
467466
///
468467
/// # Safety
469468
///
@@ -474,6 +473,9 @@ impl<T: ?Sized> *mut T {
474473
/// byte past the end of the same allocated object. Note that in Rust,
475474
/// every (stack-allocated) variable is considered a separate allocated object.
476475
///
476+
/// * Both pointers must be *derived from* a pointer to the same object.
477+
/// (See below for an example.)
478+
///
477479
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
478480
///
479481
/// * The distance between the pointers, in bytes, must be an exact multiple
@@ -494,10 +496,6 @@ impl<T: ?Sized> *mut T {
494496
/// Extension. As such, memory acquired directly from allocators or memory
495497
/// mapped files *may* be too large to handle with this function.
496498
///
497-
/// Consider using [`wrapping_offset_from`] instead if these constraints are
498-
/// difficult to satisfy. The only advantage of this method is that it
499-
/// enables more aggressive compiler optimizations.
500-
///
501499
/// # Panics
502500
///
503501
/// This function panics if `T` is a Zero-Sized Type ("ZST").
@@ -507,8 +505,6 @@ impl<T: ?Sized> *mut T {
507505
/// Basic usage:
508506
///
509507
/// ```
510-
/// #![feature(ptr_offset_from)]
511-
///
512508
/// let mut a = [0; 5];
513509
/// let ptr1: *mut i32 = &mut a[1];
514510
/// let ptr2: *mut i32 = &mut a[3];
@@ -519,7 +515,24 @@ impl<T: ?Sized> *mut T {
519515
/// assert_eq!(ptr2.offset(-2), ptr1);
520516
/// }
521517
/// ```
522-
#[unstable(feature = "ptr_offset_from", issue = "41079")]
518+
///
519+
/// *Incorrect* usage:
520+
///
521+
/// ```rust,no_run
522+
/// let ptr1 = Box::into_raw(Box::new(0u8));
523+
/// let ptr2 = Box::into_raw(Box::new(1u8));
524+
/// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize);
525+
/// // Make ptr2_other an "alias" of ptr2, but derived from ptr1.
526+
/// let ptr2_other = (ptr1 as *mut u8).wrapping_offset(diff);
527+
/// assert_eq!(ptr2 as usize, ptr2_other as usize);
528+
/// // Since ptr2_other and ptr2 are derived from pointers to different objects,
529+
/// // computing their offset is undefined behavior, even though
530+
/// // they point to the same address!
531+
/// unsafe {
532+
/// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior
533+
/// }
534+
/// ```
535+
#[stable(feature = "ptr_offset_from", since = "1.47.0")]
523536
#[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")]
524537
#[inline]
525538
pub const unsafe fn offset_from(self, origin: *const T) -> isize
@@ -530,56 +543,6 @@ impl<T: ?Sized> *mut T {
530543
unsafe { (self as *const T).offset_from(origin) }
531544
}
532545

533-
/// Calculates the distance between two pointers. The returned value is in
534-
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
535-
///
536-
/// If the address different between the two pointers is not a multiple of
537-
/// `mem::size_of::<T>()` then the result of the division is rounded towards
538-
/// zero.
539-
///
540-
/// Though this method is safe for any two pointers, note that its result
541-
/// will be mostly useless if the two pointers aren't into the same allocated
542-
/// object, for example if they point to two different local variables.
543-
///
544-
/// # Panics
545-
///
546-
/// This function panics if `T` is a zero-sized type.
547-
///
548-
/// # Examples
549-
///
550-
/// Basic usage:
551-
///
552-
/// ```
553-
/// #![feature(ptr_wrapping_offset_from)]
554-
///
555-
/// let mut a = [0; 5];
556-
/// let ptr1: *mut i32 = &mut a[1];
557-
/// let ptr2: *mut i32 = &mut a[3];
558-
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2);
559-
/// assert_eq!(ptr1.wrapping_offset_from(ptr2), -2);
560-
/// assert_eq!(ptr1.wrapping_offset(2), ptr2);
561-
/// assert_eq!(ptr2.wrapping_offset(-2), ptr1);
562-
///
563-
/// let ptr1: *mut i32 = 3 as _;
564-
/// let ptr2: *mut i32 = 13 as _;
565-
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2);
566-
/// ```
567-
#[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")]
568-
#[rustc_deprecated(
569-
since = "1.46.0",
570-
reason = "Pointer distances across allocation \
571-
boundaries are not typically meaningful. \
572-
Use integer subtraction if you really need this."
573-
)]
574-
#[inline]
575-
pub fn wrapping_offset_from(self, origin: *const T) -> isize
576-
where
577-
T: Sized,
578-
{
579-
#[allow(deprecated_in_future, deprecated)]
580-
(self as *const T).wrapping_offset_from(origin)
581-
}
582-
583546
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
584547
///
585548
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer

library/std/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@
208208
#![cfg_attr(test, feature(print_internals, set_stdio, update_panic_count))]
209209
#![cfg_attr(
210210
all(target_vendor = "fortanix", target_env = "sgx"),
211-
feature(slice_index_methods, coerce_unsized, sgx_platform, ptr_wrapping_offset_from)
211+
feature(slice_index_methods, coerce_unsized, sgx_platform)
212212
)]
213213
#![cfg_attr(all(test, target_vendor = "fortanix", target_env = "sgx"), feature(fixed_size_array))]
214214
// std is implemented with unstable features, many of which are internal

src/librustdoc/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#![feature(nll)]
1010
#![feature(or_patterns)]
1111
#![feature(test)]
12-
#![feature(ptr_offset_from)]
1312
#![feature(crate_visibility_modifier)]
1413
#![feature(never_type)]
1514
#![feature(once_cell)]

src/test/ui/consts/offset.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// run-pass
22
#![feature(const_ptr_offset)]
33
#![feature(const_ptr_offset_from)]
4-
#![feature(ptr_offset_from)]
54
use std::ptr;
65

76
#[repr(C)]

src/test/ui/consts/offset_from.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#![feature(const_raw_ptr_deref)]
44
#![feature(const_ptr_offset_from)]
5-
#![feature(ptr_offset_from)]
65

76
struct Struct {
87
field: (),

src/test/ui/consts/offset_from_ub.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#![feature(const_raw_ptr_deref)]
22
#![feature(const_ptr_offset_from)]
3-
#![feature(ptr_offset_from)]
43

54
#[repr(C)]
65
struct Struct {

src/test/ui/consts/offset_from_ub.stderr

+10-10
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
66
| |
77
| ptr_offset_from cannot compute offset of pointers into different allocations.
88
| inside `std::ptr::const_ptr::<impl *const Struct>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
9-
| inside `DIFFERENT_ALLOC` at $DIR/offset_from_ub.rs:17:27
9+
| inside `DIFFERENT_ALLOC` at $DIR/offset_from_ub.rs:16:27
1010
|
11-
::: $DIR/offset_from_ub.rs:11:1
11+
::: $DIR/offset_from_ub.rs:10:1
1212
|
1313
LL | / pub const DIFFERENT_ALLOC: usize = {
1414
LL | |
@@ -29,9 +29,9 @@ LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
2929
| |
3030
| unable to turn bytes into a pointer
3131
| inside `std::ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
32-
| inside `NOT_PTR` at $DIR/offset_from_ub.rs:23:14
32+
| inside `NOT_PTR` at $DIR/offset_from_ub.rs:22:14
3333
|
34-
::: $DIR/offset_from_ub.rs:21:1
34+
::: $DIR/offset_from_ub.rs:20:1
3535
|
3636
LL | / pub const NOT_PTR: usize = {
3737
LL | |
@@ -47,9 +47,9 @@ LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
4747
| |
4848
| exact_div: 1_isize cannot be divided by 2_isize without remainder
4949
| inside `std::ptr::const_ptr::<impl *const u16>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
50-
| inside `NOT_MULTIPLE_OF_SIZE` at $DIR/offset_from_ub.rs:31:14
50+
| inside `NOT_MULTIPLE_OF_SIZE` at $DIR/offset_from_ub.rs:30:14
5151
|
52-
::: $DIR/offset_from_ub.rs:26:1
52+
::: $DIR/offset_from_ub.rs:25:1
5353
|
5454
LL | / pub const NOT_MULTIPLE_OF_SIZE: isize = {
5555
LL | |
@@ -68,9 +68,9 @@ LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
6868
| |
6969
| inbounds test failed: 0x0 is not a valid pointer
7070
| inside `std::ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
71-
| inside `OFFSET_FROM_NULL` at $DIR/offset_from_ub.rs:37:14
71+
| inside `OFFSET_FROM_NULL` at $DIR/offset_from_ub.rs:36:14
7272
|
73-
::: $DIR/offset_from_ub.rs:34:1
73+
::: $DIR/offset_from_ub.rs:33:1
7474
|
7575
LL | / pub const OFFSET_FROM_NULL: isize = {
7676
LL | |
@@ -87,9 +87,9 @@ LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
8787
| |
8888
| unable to turn bytes into a pointer
8989
| inside `std::ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
90-
| inside `DIFFERENT_INT` at $DIR/offset_from_ub.rs:44:14
90+
| inside `DIFFERENT_INT` at $DIR/offset_from_ub.rs:43:14
9191
|
92-
::: $DIR/offset_from_ub.rs:40:1
92+
::: $DIR/offset_from_ub.rs:39:1
9393
|
9494
LL | / pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC
9595
LL | |

src/test/ui/offset_from.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// run-pass
22

3-
#![feature(ptr_offset_from)]
4-
53
fn main() {
64
let mut a = [0; 5];
75
let ptr1: *mut i32 = &mut a[1];

0 commit comments

Comments
 (0)