diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index b9b086db010..9748feddddf 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -986,11 +986,14 @@ extern "rust-intrinsic" { /// size_of::()` bytes must *not* overlap with the region of memory /// beginning at `dst` with the same size. /// - /// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of - /// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values + /// Like [`read`], `copy_nonoverlapping` creates a bitwise copy of `T`, regardless of + /// whether `T` is [`Copy`]. If `T` is not [`Copy`], using *both* the values /// in the region beginning at `*src` and the region beginning at `*dst` can /// [violate memory safety][read-ownership]. /// + /// These restrictions apply even if the effectively copied size (`count * + /// size_of::()`) is `0`. + /// /// [`Copy`]: ../marker/trait.Copy.html /// [`read`]: ../ptr/fn.read.html /// [read-ownership]: ../ptr/fn.read.html#ownership-of-the-returned-value @@ -1071,6 +1074,9 @@ extern "rust-intrinsic" { /// in the region beginning at `*src` and the region beginning at `*dst` can /// [violate memory safety][read-ownership]. /// + /// These restrictions apply even if the effectively copied size (`count * + /// size_of::()`) is `0`. + /// /// [`Copy`]: ../marker/trait.Copy.html /// [`read`]: ../ptr/fn.read.html /// [read-ownership]: ../ptr/fn.read.html#ownership-of-the-returned-value @@ -1115,6 +1121,9 @@ extern "rust-intrinsic" { /// value of `T`. Creating an invalid value of `T` can result in undefined /// behavior. /// + /// These restrictions apply even if the effectively written size (`count * + /// size_of::()`) is `0`. + /// /// [valid]: ../ptr/index.html#safety /// /// # Examples @@ -1164,7 +1173,7 @@ extern "rust-intrinsic" { /// `min_align_of::()` /// /// The volatile parameter is set to `true`, so it will not be optimized out - /// unless size is equal to zero.. + /// unless size is equal to zero. pub fn volatile_copy_memory(dst: *mut T, src: *const T, count: usize); /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a /// size of `count` * `size_of::()` and an alignment of diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 1d8c3696b45..b82afecd769 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -16,35 +16,23 @@ //! //! # Safety //! -//! Many functions in this module take raw pointers as arguments and dereference -//! them. For this to be safe, these pointers must be valid. However, because -//! rust does not yet have a formal memory model, determining whether an -//! arbitrary pointer is valid for a given operation can be tricky. +//! Many functions in this module take raw pointers as arguments and read from +//! or write to them. For this to be safe, these pointers must be *valid*. +//! Whether a pointer is valid depends on the operation it is used for +//! (read or write), and the extent of the memory that is accessed (i.e., +//! how many bytes are read/written). Most functions use `*mut T` and `*const T` +//! to access only a single value, in which case the documentation omits the size +//! and implicitly assumes it to be `size_of::()` bytes. //! -//! There are two types of operations on memory, reads and writes. A single -//! pointer can be valid for any combination of these operations. For example, a -//! pointer is not valid for writes if a `&mut` exists which [refers to the same -//! memory][aliasing]. The set of operations for which a pointer argument must -//! be valid is explicitly documented for each function. This is not strictly -//! necessary for `*const` arguments, as they can only be used for reads and -//! never for writes. -//! -//! Some functions (e.g. [`copy`]) take a single pointer but -//! operate on many values. In this case, the function will state the size of -//! the operation for which the pointer must be valid. For example, -//! `copy::(&src, &mut dst, 3)` requires `dst` to be valid for writes of -//! `size_of::() * 3` bytes. When the documentation requires that a pointer -//! be valid for an operation but omits the size of that operation, the size is -//! implied to be `size_of::()` bytes. -//! -//! While we can't yet define whether an arbitrary pointer is a valid one, there +//! While we can't yet define whether an arbitrary pointer is valid, there //! are a few rules regarding validity: //! -//! * The result of casting a reference to a pointer is valid for as long as the -//! underlying object is live. -//! * A [null] pointer is *never* valid. +//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst]. //! * All pointers (except for the null pointer) are valid for all operations of //! [size zero][zst]. +//! * 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 +//! access the same memory. //! //! These axioms, along with careful use of [`offset`] for pointer arithmentic, //! are enough to correctly implement many useful things in unsafe code. Still, @@ -60,6 +48,10 @@ //! this requirement in their documentation. Notable exceptions to this are //! [`read_unaligned`] and [`write_unaligned`]. //! +//! When a function requires proper alignment, it does so even if the access +//! has size 0, i.e., even if memory is not actually touched. Consider using +//! [`NonNull::dangling`] in such cases. +//! //! [aliasing]: ../../nomicon/aliasing.html //! [book]: ../../book/second-edition/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer //! [ub]: ../../reference/behavior-considered-undefined.html @@ -69,6 +61,7 @@ //! [`offset`]: ../../std/primitive.pointer.html#method.offset //! [`read_unaligned`]: ./fn.read_unaligned.html //! [`write_unaligned`]: ./fn.write_unaligned.html +//! [`NonNull::dangling`]: ./struct.NonNull.html#method.dangling #![stable(feature = "rust1", since = "1.0.0")] @@ -122,6 +115,8 @@ pub use intrinsics::write_bytes; /// again. [`write`] can be used to overwrite data without causing it to be /// dropped. /// +/// These restrictions apply even if `T` has size `0`. +/// /// [valid]: ../ptr/index.html#safety /// [`Copy`]: ../marker/trait.Copy.html /// [`write`]: ../ptr/fn.write.html @@ -211,6 +206,8 @@ pub const fn null_mut() -> *mut T { 0 as *mut T } /// /// * Both `x` and `y` must be properly aligned. /// +/// These restrictions apply even if `T` has size `0`. +/// /// [valid]: ../ptr/index.html#safety /// /// # Examples @@ -278,6 +275,8 @@ pub unsafe fn swap(x: *mut T, y: *mut T) { /// size_of::()` bytes must *not* overlap with the region of memory /// beginning at `y` with the same size. /// +/// These restrictions apply even if `T` has size `0`. +/// /// [valid]: ../ptr/index.html#safety /// /// # Examples @@ -389,6 +388,8 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { /// /// * `dest` must be properly aligned. /// +/// These restrictions apply even if `T` has size `0`. +/// /// [valid]: ../ptr/index.html#safety /// /// # Examples @@ -426,6 +427,8 @@ pub unsafe fn replace(dest: *mut T, mut src: T) -> T { /// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the /// case. /// +/// These restrictions apply even if `T` has size `0`. +/// /// ## Ownership of the Returned Value /// /// `read` creates a bitwise copy of `T`, regardless of whether `T` is [`Copy`]. @@ -540,6 +543,8 @@ pub unsafe fn read(src: *const T) -> T { /// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned /// value and the value at `*src` can [violate memory safety][read-ownership]. /// +/// These restrictions apply even if `T` has size `0`. +/// /// [`Copy`]: ../marker/trait.Copy.html /// [`read`]: ./fn.read.html /// [`write_unaligned`]: ./fn.write_unaligned.html @@ -616,6 +621,8 @@ pub unsafe fn read_unaligned(src: *const T) -> T { /// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the /// case. /// +/// These restrictions apply even if `T` has size `0`. +/// /// [valid]: ../ptr/index.html#safety /// [`write_unaligned`]: ./fn.write_unaligned.html /// @@ -687,6 +694,8 @@ pub unsafe fn write(dst: *mut T, src: T) { /// /// * `dst` must be [valid] for writes. /// +/// These restrictions apply even if `T` has size `0`. +/// /// [valid]: ../ptr/index.html#safety /// /// # Examples @@ -770,6 +779,8 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { /// However, storing non-[`Copy`] types in volatile memory is almost certainly /// incorrect. /// +/// These restrictions apply even if `T` has size `0`. +/// /// [valid]: ../ptr/index.html#safety /// [`Copy`]: ../marker/trait.Copy.html /// [`read`]: ./fn.read.html @@ -839,6 +850,8 @@ pub unsafe fn read_volatile(src: *const T) -> T { /// /// * `dst` must be properly aligned. /// +/// These restrictions apply even if `T` has size `0`. +/// /// [valid]: ../ptr/index.html#safety /// /// Just like in C, whether an operation is volatile has no bearing whatsoever