From 3533204e999cf70378131c63e1abbfe812109792 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 26 Dec 2020 15:59:02 +0100 Subject: [PATCH 1/2] clarify wrapping ptr arithmetic docs --- library/core/src/ptr/const_ptr.rs | 87 ++++++++++++++++++------------- library/core/src/ptr/mut_ptr.rs | 87 ++++++++++++++++++------------- 2 files changed, 102 insertions(+), 72 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 8fd9ff768c4f4..6638033566f29 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -232,23 +232,26 @@ impl *const T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// 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. + /// 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, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::())` - /// is *not* the same as `y`, and dereferencing it is undefined behavior - /// unless `x` and `y` point into the same allocated object. + /// In other words, `let z = x.wrapping_add((y as usize).wrapping_sub(x as usize) / + /// size_of::())` does *not* make `z` the same as `y`: `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. /// - /// 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. + /// 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. + /// + /// `x.wrapping_offset(o).wrapping_offset(-o)` is always the same as `x` (if `-o` does not + /// overflow). In other words, leaving the allocated object and then re-entering it later is + /// permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -571,19 +574,25 @@ impl *const T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// 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).wrapping_sub(x as usize) / + /// size_of::())` does *not* make `z` the same as `y`: `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. /// - /// 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. + /// 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. /// - /// 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. + /// `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. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -628,19 +637,25 @@ impl *const T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// 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).wrapping_sub(x as usize) / + /// size_of::())` does *not* make `z` the same as `y`: `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. /// - /// 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. + /// 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. /// - /// 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. + /// `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. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5f94c2393aef3..a0edb036c251d 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -238,23 +238,26 @@ impl *mut T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// 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. + /// 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, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::())` - /// is *not* the same as `y`, and dereferencing it is undefined behavior - /// unless `x` and `y` point into the same allocated object. + /// In other words, `let z = x.wrapping_add((y as usize).wrapping_sub(x as usize) / + /// size_of::())` does *not* make `z` the same as `y`: `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. /// - /// 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. + /// 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. + /// + /// `x.wrapping_offset(o).wrapping_offset(-o)` is always the same as `x` (if `-o` does not + /// overflow). In other words, leaving the allocated object and then re-entering it later is + /// permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -678,19 +681,25 @@ impl *mut T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// 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).wrapping_sub(x as usize) / + /// size_of::())` does *not* make `z` the same as `y`: `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. /// - /// 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. + /// 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. /// - /// 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. + /// `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. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -735,19 +744,25 @@ impl *mut T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// 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).wrapping_sub(x as usize) / + /// size_of::())` does *not* make `z` the same as `y`: `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. /// - /// 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. + /// 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. /// - /// 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. + /// `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. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. From 8543388beb52785897b87e3f0a5ffd3c8560f956 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 26 Dec 2020 18:07:52 +0100 Subject: [PATCH 2/2] address review feedback --- library/core/src/ptr/const_ptr.rs | 35 ++++++++++++++++++------------- library/core/src/ptr/mut_ptr.rs | 35 ++++++++++++++++++------------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 6638033566f29..38519f759ae05 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -238,10 +238,10 @@ impl *const T { /// 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).wrapping_sub(x as usize) / - /// size_of::())` does *not* make `z` the same as `y`: `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. + /// 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. /// /// Compared to [`offset`], this method basically delays the requirement of staying within the /// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object @@ -249,9 +249,10 @@ impl *const T { /// 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. /// - /// `x.wrapping_offset(o).wrapping_offset(-o)` is always the same as `x` (if `-o` does not - /// overflow). In other words, leaving the allocated object and then re-entering it later is - /// permitted. + /// 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. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -580,10 +581,10 @@ impl *const T { /// 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).wrapping_sub(x as usize) / - /// size_of::())` does *not* make `z` the same as `y`: `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. + /// 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. /// /// Compared to [`add`], this method basically delays the requirement of staying within the /// same allocated object: [`add`] is immediate Undefined Behavior when crossing object @@ -591,6 +592,8 @@ impl *const T { /// 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. /// + /// 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. /// @@ -643,10 +646,10 @@ impl *const T { /// 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).wrapping_sub(x as usize) / - /// size_of::())` does *not* make `z` the same as `y`: `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. + /// 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. /// /// Compared to [`sub`], this method basically delays the requirement of staying within the /// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object @@ -654,6 +657,8 @@ impl *const T { /// 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. /// + /// 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. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index a0edb036c251d..92f4e431de499 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -244,10 +244,10 @@ impl *mut T { /// 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).wrapping_sub(x as usize) / - /// size_of::())` does *not* make `z` the same as `y`: `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. + /// 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. /// /// Compared to [`offset`], this method basically delays the requirement of staying within the /// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object @@ -255,9 +255,10 @@ impl *mut T { /// 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. /// - /// `x.wrapping_offset(o).wrapping_offset(-o)` is always the same as `x` (if `-o` does not - /// overflow). In other words, leaving the allocated object and then re-entering it later is - /// permitted. + /// 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. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -687,10 +688,10 @@ impl *mut T { /// 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).wrapping_sub(x as usize) / - /// size_of::())` does *not* make `z` the same as `y`: `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. + /// 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. /// /// Compared to [`add`], this method basically delays the requirement of staying within the /// same allocated object: [`add`] is immediate Undefined Behavior when crossing object @@ -698,6 +699,8 @@ impl *mut T { /// 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. /// + /// 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. /// @@ -750,10 +753,10 @@ impl *mut T { /// 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).wrapping_sub(x as usize) / - /// size_of::())` does *not* make `z` the same as `y`: `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. + /// 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. /// /// Compared to [`sub`], this method basically delays the requirement of staying within the /// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object @@ -761,6 +764,8 @@ impl *mut T { /// 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. /// + /// 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. ///