terminology: allocated object → allocation
This commit is contained in:
parent
852f15c0f1
commit
f388c987cf
11 changed files with 146 additions and 143 deletions
|
|
@ -207,7 +207,7 @@ impl CStr {
|
|||
/// * `ptr` must be [valid] for reads of bytes up to and including the nul terminator.
|
||||
/// This means in particular:
|
||||
///
|
||||
/// * The entire memory range of this `CStr` must be contained within a single allocated object!
|
||||
/// * The entire memory range of this `CStr` must be contained within a single allocation!
|
||||
/// * `ptr` must be non-null even for a zero-length cstr.
|
||||
///
|
||||
/// * The memory referenced by the returned `CStr` must not be mutated for
|
||||
|
|
|
|||
|
|
@ -1722,7 +1722,7 @@ pub const fn needs_drop<T: ?Sized>() -> bool;
|
|||
/// # Safety
|
||||
///
|
||||
/// If the computed offset is non-zero, then both the starting and resulting pointer must be
|
||||
/// either in bounds or at the end of an allocated object. If either pointer is out
|
||||
/// either in bounds or at the end of an allocation. If either pointer is out
|
||||
/// of bounds or arithmetic overflow occurs then this operation is undefined behavior.
|
||||
///
|
||||
/// The stabilized version of this intrinsic is [`pointer::offset`].
|
||||
|
|
|
|||
|
|
@ -1623,7 +1623,7 @@ mod prim_usize {}
|
|||
/// * if `size_of_val(t) > 0`, then `t` is dereferenceable for `size_of_val(t)` many bytes
|
||||
///
|
||||
/// If `t` points at address `a`, being "dereferenceable" for N bytes means that the memory range
|
||||
/// `[a, a + N)` is all contained within a single [allocated object].
|
||||
/// `[a, a + N)` is all contained within a single [allocation].
|
||||
///
|
||||
/// For instance, this means that unsafe code in a safe function may assume these invariants are
|
||||
/// ensured of arguments passed by the caller, and it may assume that these invariants are ensured
|
||||
|
|
@ -1639,7 +1639,7 @@ mod prim_usize {}
|
|||
/// may be unsound or become unsound in future versions of Rust depending on how this question is
|
||||
/// decided.
|
||||
///
|
||||
/// [allocated object]: ptr#allocated-object
|
||||
/// [allocation]: ptr#allocation
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
mod prim_ref {}
|
||||
|
||||
|
|
|
|||
|
|
@ -482,17 +482,17 @@ impl<T: ?Sized> *const T {
|
|||
///
|
||||
/// This operation itself is always safe, but using the resulting pointer is not.
|
||||
///
|
||||
/// The resulting pointer "remembers" the [allocated object] that `self` points to
|
||||
/// The resulting pointer "remembers" the [allocation] that `self` points to
|
||||
/// (this is called "[Provenance](ptr/index.html#provenance)").
|
||||
/// The pointer must not be used to read or write other allocated objects.
|
||||
/// The pointer must not be used to read or write other allocations.
|
||||
///
|
||||
/// 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.
|
||||
/// `x` and `y` point into the same allocation.
|
||||
///
|
||||
/// Compared to [`offset`], this method basically delays the requirement of staying within the
|
||||
/// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object
|
||||
/// same allocation: [`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.
|
||||
|
|
@ -500,10 +500,10 @@ impl<T: ?Sized> *const T {
|
|||
/// 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.
|
||||
/// words, leaving the allocation and then re-entering it later is permitted.
|
||||
///
|
||||
/// [`offset`]: #method.offset
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -616,7 +616,7 @@ impl<T: ?Sized> *const T {
|
|||
/// * `self` and `origin` must either
|
||||
///
|
||||
/// * point to the same address, or
|
||||
/// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between
|
||||
/// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocation], and the memory range between
|
||||
/// the two pointers must be in bounds of that object. (See below for an example.)
|
||||
///
|
||||
/// * The distance between the pointers, in bytes, must be an exact multiple
|
||||
|
|
@ -624,10 +624,10 @@ impl<T: ?Sized> *const T {
|
|||
///
|
||||
/// As a consequence, the absolute distance between the pointers, in bytes, computed on
|
||||
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
|
||||
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
|
||||
/// implied by the in-bounds requirement, and the fact that no allocation can be larger
|
||||
/// than `isize::MAX` bytes.
|
||||
///
|
||||
/// The requirement for pointers to be derived from the same allocated object is primarily
|
||||
/// The requirement for pointers to be derived from the same allocation is primarily
|
||||
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
||||
/// objects is not known at compile-time. However, the requirement also exists at
|
||||
/// runtime and may be exploited by optimizations. If you wish to compute the difference between
|
||||
|
|
@ -636,7 +636,7 @@ impl<T: ?Sized> *const T {
|
|||
// FIXME: recommend `addr()` instead of `as usize` once that is stable.
|
||||
///
|
||||
/// [`add`]: #method.add
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
|
@ -969,12 +969,12 @@ impl<T: ?Sized> *const T {
|
|||
/// "wrapping around"), must fit in an `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// [allocation], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocation. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
///
|
||||
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||
/// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement.
|
||||
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||
/// safe.
|
||||
///
|
||||
|
|
@ -983,7 +983,7 @@ impl<T: ?Sized> *const T {
|
|||
/// enables more aggressive compiler optimizations.
|
||||
///
|
||||
/// [`wrapping_sub`]: #method.wrapping_sub
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -1073,16 +1073,16 @@ impl<T: ?Sized> *const T {
|
|||
///
|
||||
/// This operation itself is always safe, but using the resulting pointer is not.
|
||||
///
|
||||
/// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not
|
||||
/// be used to read or write other allocated objects.
|
||||
/// The resulting pointer "remembers" the [allocation] that `self` points to; it must not
|
||||
/// be used to read or write other allocations.
|
||||
///
|
||||
/// 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.
|
||||
/// `x` and `y` point into the same allocation.
|
||||
///
|
||||
/// Compared to [`add`], this method basically delays the requirement of staying within the
|
||||
/// same allocated object: [`add`] is immediate Undefined Behavior when crossing object
|
||||
/// same allocation: [`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.
|
||||
|
|
@ -1090,10 +1090,10 @@ impl<T: ?Sized> *const T {
|
|||
/// 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.
|
||||
/// allocation and then re-entering it later is permitted.
|
||||
///
|
||||
/// [`add`]: #method.add
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -1152,16 +1152,16 @@ impl<T: ?Sized> *const T {
|
|||
///
|
||||
/// This operation itself is always safe, but using the resulting pointer is not.
|
||||
///
|
||||
/// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not
|
||||
/// be used to read or write other allocated objects.
|
||||
/// The resulting pointer "remembers" the [allocation] that `self` points to; it must not
|
||||
/// be used to read or write other allocations.
|
||||
///
|
||||
/// 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.
|
||||
/// `x` and `y` point into the same allocation.
|
||||
///
|
||||
/// Compared to [`sub`], this method basically delays the requirement of staying within the
|
||||
/// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object
|
||||
/// same allocation: [`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.
|
||||
|
|
@ -1169,10 +1169,10 @@ impl<T: ?Sized> *const T {
|
|||
/// 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.
|
||||
/// allocation and then re-entering it later is permitted.
|
||||
///
|
||||
/// [`sub`]: #method.sub
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -1564,8 +1564,8 @@ impl<T> *const [T] {
|
|||
/// * The pointer must be [valid] for reads for `ptr.len() * size_of::<T>()` many bytes,
|
||||
/// and it must be properly aligned. This means in particular:
|
||||
///
|
||||
/// * The entire memory range of this slice must be contained within a single [allocated object]!
|
||||
/// Slices can never span across multiple allocated objects.
|
||||
/// * The entire memory range of this slice must be contained within a single [allocation]!
|
||||
/// Slices can never span across multiple allocations.
|
||||
///
|
||||
/// * The pointer must be aligned even for zero-length slices. One
|
||||
/// reason for this is that enum layout optimizations may rely on references
|
||||
|
|
@ -1586,7 +1586,7 @@ impl<T> *const [T] {
|
|||
/// See also [`slice::from_raw_parts`][].
|
||||
///
|
||||
/// [valid]: crate::ptr#safety
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Panics during const evaluation
|
||||
///
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ If any of the following conditions are violated, the result is Undefined Behavio
|
|||
"wrapping around"), must fit in an `isize`.
|
||||
|
||||
* If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
[allocated object], and the entire memory range between `self` and the result must be in
|
||||
bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
[allocation], and the entire memory range between `self` and the result must be in
|
||||
bounds of that allocation. In particular, this range must not "wrap around" the edge
|
||||
of the address space.
|
||||
|
||||
Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||
stays in bounds of the allocation, it is guaranteed to satisfy the first requirement.
|
||||
This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||
safe.
|
||||
|
||||
|
|
@ -29,4 +29,4 @@ difficult to satisfy. The only advantage of this method is that it
|
|||
enables more aggressive compiler optimizations.
|
||||
|
||||
[`wrapping_add`]: #method.wrapping_add
|
||||
[allocated object]: crate::ptr#allocated-object
|
||||
[allocation]: crate::ptr#allocation
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ If any of the following conditions are violated, the result is Undefined Behavio
|
|||
"wrapping around"), must fit in an `isize`.
|
||||
|
||||
* If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
[allocated object], and the entire memory range between `self` and the result must be in
|
||||
bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
[allocation], and the entire memory range between `self` and the result must be in
|
||||
bounds of that allocation. In particular, this range must not "wrap around" the edge
|
||||
of the address space. Note that "range" here refers to a half-open range as usual in Rust,
|
||||
i.e., `self..result` for non-negative offsets and `result..self` for negative offsets.
|
||||
|
||||
Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||
stays in bounds of the allocation, it is guaranteed to satisfy the first requirement.
|
||||
This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||
safe.
|
||||
|
||||
|
|
@ -26,4 +26,4 @@ difficult to satisfy. The only advantage of this method is that it
|
|||
enables more aggressive compiler optimizations.
|
||||
|
||||
[`wrapping_offset`]: #method.wrapping_offset
|
||||
[allocated object]: crate::ptr#allocated-object
|
||||
[allocation]: crate::ptr#allocation
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
//! pointer. The following points are only concerned with non-zero-sized accesses.
|
||||
//! * A [null] pointer is *never* valid.
|
||||
//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be
|
||||
//! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocated
|
||||
//! object] it is derived from; a pointer is dereferenceable if the memory range of the given size
|
||||
//! starting at the pointer is entirely contained within the bounds of that allocated object. Note
|
||||
//! that in Rust, every (stack-allocated) variable is considered a separate allocated object.
|
||||
//! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocation]
|
||||
//! it is derived from; a pointer is dereferenceable if the memory range of the given size
|
||||
//! starting at the pointer is entirely contained within the bounds of that allocation. Note
|
||||
//! that in Rust, every (stack-allocated) variable is considered a separate allocation.
|
||||
//! * All accesses performed by functions in this module are *non-atomic* in the sense
|
||||
//! of [atomic operations] used to synchronize between threads. This means it is
|
||||
//! undefined behavior to perform two concurrent accesses to the same location from different
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
//! includes [`read_volatile`] and [`write_volatile`]: Volatile accesses cannot
|
||||
//! be used for inter-thread synchronization.
|
||||
//! * The result of casting a reference to a pointer is valid for as long as the
|
||||
//! underlying object is live and no reference (just raw pointers) is used to
|
||||
//! underlying allocation is live and no reference (just raw pointers) is used to
|
||||
//! access the same memory. That is, reference and pointer accesses cannot be
|
||||
//! interleaved.
|
||||
//!
|
||||
|
|
@ -95,24 +95,26 @@
|
|||
//!
|
||||
//! [valid value]: ../../reference/behavior-considered-undefined.html#invalid-values
|
||||
//!
|
||||
//! ## Allocated object
|
||||
//! ## Allocation
|
||||
//!
|
||||
//! An *allocated object* is a subset of program memory which is addressable
|
||||
//! <a id="allocated-object"></a> <!-- keep old URLs working -->
|
||||
//!
|
||||
//! An *allocation* is a subset of program memory which is addressable
|
||||
//! from Rust, and within which pointer arithmetic is possible. Examples of
|
||||
//! allocated objects include heap allocations, stack-allocated variables,
|
||||
//! allocations include heap allocations, stack-allocated variables,
|
||||
//! statics, and consts. The safety preconditions of some Rust operations -
|
||||
//! such as `offset` and field projections (`expr.field`) - are defined in
|
||||
//! terms of the allocated objects on which they operate.
|
||||
//! terms of the allocations on which they operate.
|
||||
//!
|
||||
//! An allocated object has a base address, a size, and a set of memory
|
||||
//! addresses. It is possible for an allocated object to have zero size, but
|
||||
//! such an allocated object will still have a base address. The base address
|
||||
//! of an allocated object is not necessarily unique. While it is currently the
|
||||
//! case that an allocated object always has a set of memory addresses which is
|
||||
//! An allocation has a base address, a size, and a set of memory
|
||||
//! addresses. It is possible for an allocation to have zero size, but
|
||||
//! such an allocation will still have a base address. The base address
|
||||
//! of an allocation is not necessarily unique. While it is currently the
|
||||
//! case that an allocation always has a set of memory addresses which is
|
||||
//! fully contiguous (i.e., has no "holes"), there is no guarantee that this
|
||||
//! will not change in the future.
|
||||
//!
|
||||
//! For any allocated object with `base` address, `size`, and a set of
|
||||
//! For any allocation with `base` address, `size`, and a set of
|
||||
//! `addresses`, the following are guaranteed:
|
||||
//! - For all addresses `a` in `addresses`, `a` is in the range `base .. (base +
|
||||
//! size)` (note that this requires `a < base + size`, not `a <= base + size`)
|
||||
|
|
@ -122,11 +124,11 @@
|
|||
//! - `size <= isize::MAX`
|
||||
//!
|
||||
//! As a consequence of these guarantees, given any address `a` within the set
|
||||
//! of addresses of an allocated object:
|
||||
//! of addresses of an allocation:
|
||||
//! - It is guaranteed that `a - base` does not overflow `isize`
|
||||
//! - It is guaranteed that `a - base` is non-negative
|
||||
//! - It is guaranteed that, given `o = a - base` (i.e., the offset of `a` within
|
||||
//! the allocated object), `base + o` will not wrap around the address space (in
|
||||
//! the allocation), `base + o` will not wrap around the address space (in
|
||||
//! other words, will not overflow `usize`)
|
||||
//!
|
||||
//! [`null()`]: null
|
||||
|
|
@ -138,8 +140,8 @@
|
|||
//! and the freed memory gets reallocated before your read/write (in fact this is the
|
||||
//! worst-case scenario, UAFs would be much less concerning if this didn't happen!).
|
||||
//! As another example, consider that [`wrapping_offset`] is documented to "remember"
|
||||
//! the allocated object that the original pointer points to, even if it is offset far
|
||||
//! outside the memory range occupied by that allocated object.
|
||||
//! the allocation that the original pointer points to, even if it is offset far
|
||||
//! outside the memory range occupied by that allocation.
|
||||
//! To rationalize claims like this, pointers need to somehow be *more* than just their addresses:
|
||||
//! they must have **provenance**.
|
||||
//!
|
||||
|
|
@ -159,12 +161,12 @@
|
|||
//! writes. Note that this can interact with the other components, e.g. a pointer might permit
|
||||
//! mutation only for a subset of addresses, or only for a subset of its maximal timespan.
|
||||
//!
|
||||
//! When an [allocated object] is created, it has a unique Original Pointer. For alloc
|
||||
//! When an [allocation] is created, it has a unique Original Pointer. For alloc
|
||||
//! APIs this is literally the pointer the call returns, and for local variables and statics,
|
||||
//! this is the name of the variable/static. (This is mildly overloading the term "pointer"
|
||||
//! for the sake of brevity/exposition.)
|
||||
//!
|
||||
//! The Original Pointer for an allocated object has provenance that constrains the *spatial*
|
||||
//! The Original Pointer for an allocation has provenance that constrains the *spatial*
|
||||
//! permissions of this pointer to the memory range of the allocation, and the *temporal*
|
||||
//! permissions to the lifetime of the allocation. Provenance is implicitly inherited by all
|
||||
//! pointers transitively derived from the Original Pointer through operations like [`offset`],
|
||||
|
|
@ -192,10 +194,10 @@
|
|||
//! provenance since they access an empty range of memory.
|
||||
//!
|
||||
//! * It is undefined behavior to [`offset`] a pointer across a memory range that is not contained
|
||||
//! in the allocated object it is derived from, or to [`offset_from`] two pointers not derived
|
||||
//! from the same allocated object. Provenance is used to say what exactly "derived from" even
|
||||
//! in the allocation it is derived from, or to [`offset_from`] two pointers not derived
|
||||
//! from the same allocation. Provenance is used to say what exactly "derived from" even
|
||||
//! means: the lineage of a pointer is traced back to the Original Pointer it descends from, and
|
||||
//! that identifies the relevant allocated object. In particular, it's always UB to offset a
|
||||
//! that identifies the relevant allocation. In particular, it's always UB to offset a
|
||||
//! pointer derived from something that is now deallocated, except if the offset is 0.
|
||||
//!
|
||||
//! But it *is* still sound to:
|
||||
|
|
@ -216,7 +218,7 @@
|
|||
//! * Compare arbitrary pointers by address. Pointer comparison ignores provenance and addresses
|
||||
//! *are* just integers, so there is always a coherent answer, even if the pointers are dangling
|
||||
//! or from different provenances. Note that if you get "lucky" and notice that a pointer at the
|
||||
//! end of one allocated object is the "same" address as the start of another allocated object,
|
||||
//! end of one allocation is the "same" address as the start of another allocation,
|
||||
//! anything you do with that fact is *probably* going to be gibberish. The scope of that
|
||||
//! gibberish is kept under control by the fact that the two pointers *still* aren't allowed to
|
||||
//! access the other's allocation (bytes), because they still have different provenance.
|
||||
|
|
@ -369,7 +371,7 @@
|
|||
//! integer-to-pointer casts.
|
||||
//!
|
||||
//! [aliasing]: ../../nomicon/aliasing.html
|
||||
//! [allocated object]: #allocated-object
|
||||
//! [allocation]: #allocation
|
||||
//! [provenance]: #provenance
|
||||
//! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
|
||||
//! [ub]: ../../reference/behavior-considered-undefined.html
|
||||
|
|
@ -1289,7 +1291,7 @@ pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
|
|||
// SAFETY: the caller must guarantee that `x` and `y` are
|
||||
// valid for writes and properly aligned. `tmp` cannot be
|
||||
// overlapping either `x` or `y` because `tmp` was just allocated
|
||||
// on the stack as a separate allocated object.
|
||||
// on the stack as a separate allocation.
|
||||
unsafe {
|
||||
copy_nonoverlapping(x, tmp.as_mut_ptr(), 1);
|
||||
copy(y, x, 1); // `x` and `y` may overlap
|
||||
|
|
@ -1409,7 +1411,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
|
|||
// Going though a slice here helps codegen know the size fits in `isize`
|
||||
let slice = slice_from_raw_parts_mut(x, count);
|
||||
// SAFETY: This is all readable from the pointer, meaning it's one
|
||||
// allocated object, and thus cannot be more than isize::MAX bytes.
|
||||
// allocation, and thus cannot be more than isize::MAX bytes.
|
||||
let bytes = unsafe { mem::size_of_val_raw::<[T]>(slice) };
|
||||
if let Some(bytes) = NonZero::new(bytes) {
|
||||
// SAFETY: These are the same ranges, just expressed in a different
|
||||
|
|
@ -1563,7 +1565,7 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T {
|
|||
// SAFETY: the caller must guarantee that `dst` is valid to be
|
||||
// cast to a mutable reference (valid for writes, aligned, initialized),
|
||||
// and cannot overlap `src` since `dst` must point to a distinct
|
||||
// allocated object.
|
||||
// allocation.
|
||||
unsafe {
|
||||
ub_checks::assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
|
|
@ -1810,7 +1812,7 @@ pub const unsafe fn read_unaligned<T>(src: *const T) -> T {
|
|||
let mut tmp = MaybeUninit::<T>::uninit();
|
||||
// SAFETY: the caller must guarantee that `src` is valid for reads.
|
||||
// `src` cannot overlap `tmp` because `tmp` was just allocated on
|
||||
// the stack as a separate allocated object.
|
||||
// the stack as a separate allocation.
|
||||
//
|
||||
// Also, since we just wrote a valid value into `tmp`, it is guaranteed
|
||||
// to be properly initialized.
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@ impl<T: ?Sized> *mut T {
|
|||
|
||||
// SAFETY: the caller must uphold the safety contract for `offset`.
|
||||
// The obtained pointer is valid for writes since the caller must
|
||||
// guarantee that it points to the same allocated object as `self`.
|
||||
// guarantee that it points to the same allocation as `self`.
|
||||
unsafe { intrinsics::offset(self, count) }
|
||||
}
|
||||
|
||||
|
|
@ -481,17 +481,17 @@ impl<T: ?Sized> *mut T {
|
|||
///
|
||||
/// This operation itself is always safe, but using the resulting pointer is not.
|
||||
///
|
||||
/// The resulting pointer "remembers" the [allocated object] that `self` points to
|
||||
/// The resulting pointer "remembers" the [allocation] that `self` points to
|
||||
/// (this is called "[Provenance](ptr/index.html#provenance)").
|
||||
/// The pointer must not be used to read or write other allocated objects.
|
||||
/// The pointer must not be used to read or write other allocations.
|
||||
///
|
||||
/// 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.
|
||||
/// `x` and `y` point into the same allocation.
|
||||
///
|
||||
/// Compared to [`offset`], this method basically delays the requirement of staying within the
|
||||
/// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object
|
||||
/// same allocation: [`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.
|
||||
|
|
@ -499,10 +499,10 @@ impl<T: ?Sized> *mut T {
|
|||
/// 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.
|
||||
/// words, leaving the allocation and then re-entering it later is permitted.
|
||||
///
|
||||
/// [`offset`]: #method.offset
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -791,7 +791,7 @@ impl<T: ?Sized> *mut T {
|
|||
/// * `self` and `origin` must either
|
||||
///
|
||||
/// * point to the same address, or
|
||||
/// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between
|
||||
/// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocation], and the memory range between
|
||||
/// the two pointers must be in bounds of that object. (See below for an example.)
|
||||
///
|
||||
/// * The distance between the pointers, in bytes, must be an exact multiple
|
||||
|
|
@ -799,10 +799,10 @@ impl<T: ?Sized> *mut T {
|
|||
///
|
||||
/// As a consequence, the absolute distance between the pointers, in bytes, computed on
|
||||
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
|
||||
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
|
||||
/// implied by the in-bounds requirement, and the fact that no allocation can be larger
|
||||
/// than `isize::MAX` bytes.
|
||||
///
|
||||
/// The requirement for pointers to be derived from the same allocated object is primarily
|
||||
/// The requirement for pointers to be derived from the same allocation is primarily
|
||||
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
||||
/// objects is not known at compile-time. However, the requirement also exists at
|
||||
/// runtime and may be exploited by optimizations. If you wish to compute the difference between
|
||||
|
|
@ -811,7 +811,7 @@ impl<T: ?Sized> *mut T {
|
|||
// FIXME: recommend `addr()` instead of `as usize` once that is stable.
|
||||
///
|
||||
/// [`add`]: #method.add
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
|
@ -1061,12 +1061,12 @@ impl<T: ?Sized> *mut T {
|
|||
/// "wrapping around"), must fit in an `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// [allocation], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocation. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
///
|
||||
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||
/// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement.
|
||||
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||
/// safe.
|
||||
///
|
||||
|
|
@ -1075,7 +1075,7 @@ impl<T: ?Sized> *mut T {
|
|||
/// enables more aggressive compiler optimizations.
|
||||
///
|
||||
/// [`wrapping_sub`]: #method.wrapping_sub
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -1165,16 +1165,16 @@ impl<T: ?Sized> *mut T {
|
|||
///
|
||||
/// This operation itself is always safe, but using the resulting pointer is not.
|
||||
///
|
||||
/// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not
|
||||
/// be used to read or write other allocated objects.
|
||||
/// The resulting pointer "remembers" the [allocation] that `self` points to; it must not
|
||||
/// be used to read or write other allocations.
|
||||
///
|
||||
/// 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.
|
||||
/// `x` and `y` point into the same allocation.
|
||||
///
|
||||
/// Compared to [`add`], this method basically delays the requirement of staying within the
|
||||
/// same allocated object: [`add`] is immediate Undefined Behavior when crossing object
|
||||
/// same allocation: [`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.
|
||||
|
|
@ -1182,10 +1182,10 @@ impl<T: ?Sized> *mut T {
|
|||
/// 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.
|
||||
/// allocation and then re-entering it later is permitted.
|
||||
///
|
||||
/// [`add`]: #method.add
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -1241,16 +1241,16 @@ impl<T: ?Sized> *mut T {
|
|||
///
|
||||
/// This operation itself is always safe, but using the resulting pointer is not.
|
||||
///
|
||||
/// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not
|
||||
/// be used to read or write other allocated objects.
|
||||
/// The resulting pointer "remembers" the [allocation] that `self` points to; it must not
|
||||
/// be used to read or write other allocations.
|
||||
///
|
||||
/// 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.
|
||||
/// `x` and `y` point into the same allocation.
|
||||
///
|
||||
/// Compared to [`sub`], this method basically delays the requirement of staying within the
|
||||
/// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object
|
||||
/// same allocation: [`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.
|
||||
|
|
@ -1258,10 +1258,10 @@ impl<T: ?Sized> *mut T {
|
|||
/// 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.
|
||||
/// allocation and then re-entering it later is permitted.
|
||||
///
|
||||
/// [`sub`]: #method.sub
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -1770,7 +1770,7 @@ impl<T> *mut [T] {
|
|||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `mid` must be [in-bounds] of the underlying [allocated object].
|
||||
/// `mid` must be [in-bounds] of the underlying [allocation].
|
||||
/// Which means `self` must be dereferenceable and span a single allocation
|
||||
/// that is at least `mid * size_of::<T>()` bytes long. Not upholding these
|
||||
/// requirements is *[undefined behavior]* even if the resulting pointers are not used.
|
||||
|
|
@ -1781,7 +1781,7 @@ impl<T> *mut [T] {
|
|||
///
|
||||
/// [`split_at_mut_unchecked`]: #method.split_at_mut_unchecked
|
||||
/// [in-bounds]: #method.add
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -1816,13 +1816,14 @@ impl<T> *mut [T] {
|
|||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `mid` must be [in-bounds] of the underlying [allocated object].
|
||||
/// `mid` must be [in-bounds] of the underlying [allocation].
|
||||
/// Which means `self` must be dereferenceable and span a single allocation
|
||||
/// that is at least `mid * size_of::<T>()` bytes long. Not upholding these
|
||||
/// requirements is *[undefined behavior]* even if the resulting pointers are not used.
|
||||
///
|
||||
/// [in-bounds]: #method.add
|
||||
/// [out-of-bounds index]: #method.add
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -1922,8 +1923,8 @@ impl<T> *mut [T] {
|
|||
/// * The pointer must be [valid] for reads for `ptr.len() * size_of::<T>()` many bytes,
|
||||
/// and it must be properly aligned. This means in particular:
|
||||
///
|
||||
/// * The entire memory range of this slice must be contained within a single [allocated object]!
|
||||
/// Slices can never span across multiple allocated objects.
|
||||
/// * The entire memory range of this slice must be contained within a single [allocation]!
|
||||
/// Slices can never span across multiple allocations.
|
||||
///
|
||||
/// * The pointer must be aligned even for zero-length slices. One
|
||||
/// reason for this is that enum layout optimizations may rely on references
|
||||
|
|
@ -1944,7 +1945,7 @@ impl<T> *mut [T] {
|
|||
/// See also [`slice::from_raw_parts`][].
|
||||
///
|
||||
/// [valid]: crate::ptr#safety
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Panics during const evaluation
|
||||
///
|
||||
|
|
@ -1980,8 +1981,8 @@ impl<T> *mut [T] {
|
|||
/// * The pointer must be [valid] for reads and writes for `ptr.len() * size_of::<T>()`
|
||||
/// many bytes, and it must be properly aligned. This means in particular:
|
||||
///
|
||||
/// * The entire memory range of this slice must be contained within a single [allocated object]!
|
||||
/// Slices can never span across multiple allocated objects.
|
||||
/// * The entire memory range of this slice must be contained within a single [allocation]!
|
||||
/// Slices can never span across multiple allocations.
|
||||
///
|
||||
/// * The pointer must be aligned even for zero-length slices. One
|
||||
/// reason for this is that enum layout optimizations may rely on references
|
||||
|
|
@ -2002,7 +2003,7 @@ impl<T> *mut [T] {
|
|||
/// See also [`slice::from_raw_parts_mut`][].
|
||||
///
|
||||
/// [valid]: crate::ptr#safety
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Panics during const evaluation
|
||||
///
|
||||
|
|
|
|||
|
|
@ -530,16 +530,16 @@ impl<T: ?Sized> NonNull<T> {
|
|||
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// [allocation], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocation. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
///
|
||||
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||
/// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement.
|
||||
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||
/// safe.
|
||||
///
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -606,16 +606,16 @@ impl<T: ?Sized> NonNull<T> {
|
|||
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// [allocation], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocation. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
///
|
||||
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||
/// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement.
|
||||
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||
/// safe.
|
||||
///
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -683,16 +683,16 @@ impl<T: ?Sized> NonNull<T> {
|
|||
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// [allocation], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocation. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
///
|
||||
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||
/// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||
/// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement.
|
||||
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||
/// safe.
|
||||
///
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -775,7 +775,7 @@ impl<T: ?Sized> NonNull<T> {
|
|||
/// * `self` and `origin` must either
|
||||
///
|
||||
/// * point to the same address, or
|
||||
/// * both be *derived from* a pointer to the same [allocated object], and the memory range between
|
||||
/// * both be *derived from* a pointer to the same [allocation], and the memory range between
|
||||
/// the two pointers must be in bounds of that object. (See below for an example.)
|
||||
///
|
||||
/// * The distance between the pointers, in bytes, must be an exact multiple
|
||||
|
|
@ -783,10 +783,10 @@ impl<T: ?Sized> NonNull<T> {
|
|||
///
|
||||
/// As a consequence, the absolute distance between the pointers, in bytes, computed on
|
||||
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
|
||||
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
|
||||
/// implied by the in-bounds requirement, and the fact that no allocation can be larger
|
||||
/// than `isize::MAX` bytes.
|
||||
///
|
||||
/// The requirement for pointers to be derived from the same allocated object is primarily
|
||||
/// The requirement for pointers to be derived from the same allocation is primarily
|
||||
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
||||
/// objects is not known at compile-time. However, the requirement also exists at
|
||||
/// runtime and may be exploited by optimizations. If you wish to compute the difference between
|
||||
|
|
@ -795,7 +795,7 @@ impl<T: ?Sized> NonNull<T> {
|
|||
// FIXME: recommend `addr()` instead of `as usize` once that is stable.
|
||||
///
|
||||
/// [`add`]: #method.add
|
||||
/// [allocated object]: crate::ptr#allocated-object
|
||||
/// [allocation]: crate::ptr#allocation
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
|
@ -1475,8 +1475,8 @@ impl<T> NonNull<[T]> {
|
|||
/// * The pointer must be [valid] for reads for `ptr.len() * size_of::<T>()` many bytes,
|
||||
/// and it must be properly aligned. This means in particular:
|
||||
///
|
||||
/// * The entire memory range of this slice must be contained within a single allocated object!
|
||||
/// Slices can never span across multiple allocated objects.
|
||||
/// * The entire memory range of this slice must be contained within a single allocation!
|
||||
/// Slices can never span across multiple allocations.
|
||||
///
|
||||
/// * The pointer must be aligned even for zero-length slices. One
|
||||
/// reason for this is that enum layout optimizations may rely on references
|
||||
|
|
@ -1520,8 +1520,8 @@ impl<T> NonNull<[T]> {
|
|||
/// * The pointer must be [valid] for reads and writes for `ptr.len() * size_of::<T>()`
|
||||
/// many bytes, and it must be properly aligned. This means in particular:
|
||||
///
|
||||
/// * The entire memory range of this slice must be contained within a single allocated object!
|
||||
/// Slices can never span across multiple allocated objects.
|
||||
/// * The entire memory range of this slice must be contained within a single allocation!
|
||||
/// Slices can never span across multiple allocations.
|
||||
///
|
||||
/// * The pointer must be aligned even for zero-length slices. One
|
||||
/// reason for this is that enum layout optimizations may rely on references
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ use crate::{array, ptr, ub_checks};
|
|||
/// * `data` must be non-null, [valid] for reads for `len * size_of::<T>()` many bytes,
|
||||
/// and it must be properly aligned. This means in particular:
|
||||
///
|
||||
/// * The entire memory range of this slice must be contained within a single allocated object!
|
||||
/// Slices can never span across multiple allocated objects. See [below](#incorrect-usage)
|
||||
/// * The entire memory range of this slice must be contained within a single allocation!
|
||||
/// Slices can never span across multiple allocations. See [below](#incorrect-usage)
|
||||
/// for an example incorrectly not taking this into account.
|
||||
/// * `data` must be non-null and aligned even for zero-length slices or slices of ZSTs. One
|
||||
/// reason for this is that enum layout optimizations may rely on references
|
||||
|
|
@ -65,14 +65,14 @@ use crate::{array, ptr, ub_checks};
|
|||
/// assert_eq!(fst_end, snd_start, "Slices must be contiguous!");
|
||||
/// unsafe {
|
||||
/// // The assertion above ensures `fst` and `snd` are contiguous, but they might
|
||||
/// // still be contained within _different allocated objects_, in which case
|
||||
/// // still be contained within _different allocations_, in which case
|
||||
/// // creating this slice is undefined behavior.
|
||||
/// slice::from_raw_parts(fst.as_ptr(), fst.len() + snd.len())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// // `a` and `b` are different allocated objects...
|
||||
/// // `a` and `b` are different allocations...
|
||||
/// let a = 42;
|
||||
/// let b = 27;
|
||||
/// // ... which may nevertheless be laid out contiguously in memory: | a | b |
|
||||
|
|
@ -150,8 +150,8 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
|
|||
/// * `data` must be non-null, [valid] for both reads and writes for `len * size_of::<T>()` many bytes,
|
||||
/// and it must be properly aligned. This means in particular:
|
||||
///
|
||||
/// * The entire memory range of this slice must be contained within a single allocated object!
|
||||
/// Slices can never span across multiple allocated objects.
|
||||
/// * The entire memory range of this slice must be contained within a single allocation!
|
||||
/// Slices can never span across multiple allocations.
|
||||
/// * `data` must be non-null and aligned even for zero-length slices or slices of ZSTs. One
|
||||
/// reason for this is that enum layout optimizations may rely on references
|
||||
/// (including slices of any length) being aligned and non-null to distinguish
|
||||
|
|
@ -228,8 +228,8 @@ pub const fn from_mut<T>(s: &mut T) -> &mut [T] {
|
|||
/// the last element, such that the offset from the end to the start pointer is
|
||||
/// the length of the slice.
|
||||
///
|
||||
/// * The entire memory range of this slice must be contained within a single allocated object!
|
||||
/// Slices can never span across multiple allocated objects.
|
||||
/// * The entire memory range of this slice must be contained within a single allocation!
|
||||
/// Slices can never span across multiple allocations.
|
||||
///
|
||||
/// * The range must contain `N` consecutive properly initialized values of type `T`.
|
||||
///
|
||||
|
|
@ -298,8 +298,8 @@ pub const unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] {
|
|||
/// the last element, such that the offset from the end to the start pointer is
|
||||
/// the length of the slice.
|
||||
///
|
||||
/// * The entire memory range of this slice must be contained within a single allocated object!
|
||||
/// Slices can never span across multiple allocated objects.
|
||||
/// * The entire memory range of this slice must be contained within a single allocation!
|
||||
/// Slices can never span across multiple allocations.
|
||||
///
|
||||
/// * The range must contain `N` consecutive properly initialized values of type `T`.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub static S5: &[MaybeUninit<u8>] = unsafe { from_raw_parts((&D1) as *const _ as
|
|||
// is valid as [bool; 4], so this is not UB (it's basically a transmute)
|
||||
pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) };
|
||||
|
||||
// Structs are considered single allocated objects,
|
||||
// Structs are considered single allocations,
|
||||
// as long as you don't reinterpret padding as initialized
|
||||
// data everything is ok.
|
||||
pub static S7: &[u16] = unsafe {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue