Change:
whether pointer arithmetic is allowed to - temporarily - leave the bounds of an object, then go back into bounds and afterwards dereference the pointer. The question is if code like this: is sound or unsound. The current documentation says: So it specifies that the resulting pointer “remains attached to the same allocated object that points to”, which suggests that after going back into bounds, dereferencing should be safe again. It also says “this method basically delays the requirement of staying within the same allocated object; ” which sounds like, staying within the same allocated object is still a requirement, which could be interpreted as that whenever any intermediate value did not stay within bounds, that intermediate value has some delayed violation of requirements attached to it that triggers UB once the final (back in bounds) pointer is dereferenced. The following comparison, “ / / is immediate Undefined Behavior when crossing object boundaries; / / produces a pointer but still leads to Undefined Behavior if that pointer is dereferenced” can easily be interpreted as confirming this interpretation, since object boundaries are crossed (twice, first going out of the object, then going back in) and the pointer is dereference afterwards (right after the second crossing of object boundaries). After I was addressing this , gave the answer modify labels: T-doc, T-libs, T-lang, C-enhancement Feel free to remove a label if this doesn’t seem relevant for that team.
Ah I see... yeah that is a misunderstanding. The delayed check means that when the (derived) ptr is dereferenced, that ptr needs to be in-bounds, but the intermediate ptrs do not have to be.

Discussion:
/// /// # Safety /// <del> /// The resulting pointer does not need to be in bounds, but it is /// potentially hazardous to dereference (which requires `unsafe`). </del> <ins> /// This operation itself is always safe, but using the resulting pointer is not. </ins> /// <del> /// In particular, the resulting pointer remains attached to the same allocated /// object that `self` points to. It may *not* be used to access a /// different allocated object. Note that in Rust, /// every (stack-allocated) variable is considered a separate allocated object. </del> <ins> /// The resulting pointer remains attached to the same allocated object that `self` points to. /// It may *not* be used to access a different allocated object. Note that in Rust, every /// (stack-allocated) variable is considered a separate allocated object. </ins> /// <del> /// In other words, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::<T>())` /// is *not* the same as `y`, and dereferencing it is undefined behavior /// unless `x` and `y` point into the same allocated object. </del> <ins> /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless /// `x` and `y` point into the same allocated object. </ins> /// <del> /// Compared to [`offset`], this method basically delays the requirement of staying /// within the same allocated object: [`offset`] is immediate Undefined Behavior when /// crossing object boundaries; `wrapping_offset` produces a pointer but still leads /// to Undefined Behavior if that pointer is dereferenced. [`offset`] can be optimized /// better and is thus preferable in performance-sensitive code. </del> <ins> /// Compared to [`offset`], this method basically delays the requirement of staying within the /// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object /// boundaries; `wrapping_offset` produces a pointer but still leads to Undefined Behavior if a /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`offset`] /// can be optimized better and is thus preferable in performance-sensitive code. /// /// The delayed check only considers the value of the pointer that was dereferenced, not the /// intermediate values used during the computation of the final result. For example, /// `x.wrapping_offset(o).wrapping_offset(o.wrapping_neg())` is always the same as `x`. In other /// words, leaving the allocated object and then re-entering it later is permitted. </ins> /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there.

/// /// # Safety /// <del> /// The resulting pointer does not need to be in bounds, but it is /// potentially hazardous to dereference (which requires `unsafe`). </del> <ins> /// This operation itself is always safe, but using the resulting pointer is not. /// /// The resulting pointer remains attached to the same allocated object that `self` points to. /// It may *not* be used to access a different allocated object. Note that in Rust, every /// (stack-allocated) variable is considered a separate allocated object. /// /// In other words, `let z = x.wrapping_add((y as usize) - (x as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless /// `x` and `y` point into the same allocated object. </ins> /// <del> /// In particular, the resulting pointer remains attached to the same allocated /// object that `self` points to. It may *not* be used to access a /// different allocated object. Note that in Rust, /// every (stack-allocated) variable is considered a separate allocated object. </del> <ins> /// Compared to [`add`], this method basically delays the requirement of staying within the /// same allocated object: [`add`] is immediate Undefined Behavior when crossing object /// boundaries; `wrapping_add` produces a pointer but still leads to Undefined Behavior if a /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`add`] /// can be optimized better and is thus preferable in performance-sensitive code. </ins> /// <del> /// Compared to [`add`], this method basically delays the requirement of staying /// within the same allocated object: [`add`] is immediate Undefined Behavior when /// crossing object boundaries; `wrapping_add` produces a pointer but still leads /// to Undefined Behavior if that pointer is dereferenced. [`add`] can be optimized /// better and is thus preferable in performance-sensitive code. </del> <ins> /// The delayed check only considers the value of the pointer that was dereferenced, not the /// intermediate values used during the computation of the final result. For example, /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the /// allocated object and then re-entering it later is permitted. </ins> /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there.

/// /// # Safety /// <del> /// The resulting pointer does not need to be in bounds, but it is /// potentially hazardous to dereference (which requires `unsafe`). </del> <ins> /// This operation itself is always safe, but using the resulting pointer is not. /// /// The resulting pointer remains attached to the same allocated object that `self` points to. /// It may *not* be used to access a different allocated object. Note that in Rust, every /// (stack-allocated) variable is considered a separate allocated object. /// /// In other words, `let z = x.wrapping_sub((x as usize) - (y as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless /// `x` and `y` point into the same allocated object. </ins> /// <del> /// In particular, the resulting pointer remains attached to the same allocated /// object that `self` points to. It may *not* be used to access a /// different allocated object. Note that in Rust, /// every (stack-allocated) variable is considered a separate allocated object. </del> <ins> /// Compared to [`sub`], this method basically delays the requirement of staying within the /// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object /// boundaries; `wrapping_sub` produces a pointer but still leads to Undefined Behavior if a /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`sub`] /// can be optimized better and is thus preferable in performance-sensitive code. </ins> /// <del> /// Compared to [`sub`], this method basically delays the requirement of staying /// within the same allocated object: [`sub`] is immediate Undefined Behavior when /// crossing object boundaries; `wrapping_sub` produces a pointer but still leads /// to Undefined Behavior if that pointer is dereferenced. [`sub`] can be optimized /// better and is thus preferable in performance-sensitive code. </del> <ins> /// The delayed check only considers the value of the pointer that was dereferenced, not the /// intermediate values used during the computation of the final result. For example, /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the /// allocated object and then re-entering it later is permitted. </ins> /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there.