about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-02-28 19:12:36 +0100
committerRalf Jung <post@ralfj.de>2024-03-02 10:12:02 +0100
commitec5e2dc241718b14dcd784eb75f17fd891d5f9b8 (patch)
tree747da3eafc2870ce4afb7627ca0dfc8b7a3e5199
parentef324565d071c6d7e2477a195648549e33d6a465 (diff)
downloadrust-ec5e2dc241718b14dcd784eb75f17fd891d5f9b8.tar.gz
rust-ec5e2dc241718b14dcd784eb75f17fd891d5f9b8.zip
attempt to further clarify addr_of docs
-rw-r--r--library/core/src/ptr/mod.rs78
1 files changed, 63 insertions, 15 deletions
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index fc5b08c9801..018efd4b9b3 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -2071,11 +2071,16 @@ impl<F: FnPtr> fmt::Debug for F {
 /// as all other references. This macro can create a raw pointer *without* creating
 /// a reference first.
 ///
-/// The `expr` in `addr_of!(expr)` is evaluated as a place expression, but never loads
-/// from the place or requires the place to be dereferenceable. This means that
-/// `addr_of!(*ptr)` is defined behavior even if `ptr` is null, dangling, or misaligned.
-/// Note however that `addr_of!((*ptr).field)` still requires the projection to
-/// `field` to be in-bounds, using the same rules as [`offset`].
+/// See [`addr_of_mut`] for how to create a pointer to uninitialized data.
+/// Doing that with `addr_of` would not make much sense since one could only
+/// read the data, and that would be Undefined Behavior.
+///
+/// # Safety
+///
+/// The `expr` in `addr_of!(expr)` is evaluated as a place expression, but never loads from the
+/// place or requires the place to be dereferenceable. This means that `addr_of!((*ptr).field)`
+/// still requires the projection to `field` to be in-bounds, using the same rules as [`offset`].
+/// However, `addr_of!(*ptr)` is defined behavior even if `ptr` is null, dangling, or misaligned.
 ///
 /// Note that `Deref`/`Index` coercions (and their mutable counterparts) are applied inside
 /// `addr_of!` like everywhere else, in which case a reference is created to call `Deref::deref` or
@@ -2086,6 +2091,8 @@ impl<F: FnPtr> fmt::Debug for F {
 ///
 /// # Example
 ///
+/// **Correct usage: Creating a pointer to unaligned data**
+///
 /// ```
 /// use std::ptr;
 ///
@@ -2101,9 +2108,27 @@ impl<F: FnPtr> fmt::Debug for F {
 /// assert_eq!(unsafe { raw_f2.read_unaligned() }, 2);
 /// ```
 ///
-/// See [`addr_of_mut`] for how to create a pointer to uninitialized data.
-/// Doing that with `addr_of` would not make much sense since one could only
-/// read the data, and that would be Undefined Behavior.
+/// **Incorrect usage: Out-of-bounds fields projection**
+///
+/// ```rust,no_run
+/// use std::ptr;
+///
+/// #[repr(C)]
+/// struct MyStruct {
+///     field1: i32,
+///     field2: i32,
+/// }
+///
+/// let ptr: *const MyStruct = ptr::null();
+/// let fieldptr = unsafe { ptr::addr_of!((*ptr).field2) }; // Undefined Behavior ⚠️
+/// ```
+///
+/// The field projection `.field2` would offset the pointer by 4 bytes,
+/// but the pointer is not in-bounds of an allocation for 4 bytes,
+/// so this offset is Undefined Behavior.
+/// See the [`offset`] docs for a full list of requirements for inbounds pointer arithmetic; the
+/// same requirements apply to field projections, even inside `addr_of!`. (In particular, it makes
+/// no difference whether the pointer is null or dangling.)
 #[stable(feature = "raw_ref_macros", since = "1.51.0")]
 #[rustc_macro_transparency = "semitransparent"]
 #[allow_internal_unstable(raw_ref_op)]
@@ -2120,11 +2145,12 @@ pub macro addr_of($place:expr) {
 /// as all other references. This macro can create a raw pointer *without* creating
 /// a reference first.
 ///
-/// The `expr` in `addr_of_mut!(expr)` is evaluated as a place expression, but never loads
-/// from the place or requires the place to be dereferenceable. This means that
-/// `addr_of_mut!(*ptr)` is defined behavior even if `ptr` is null, dangling, or misaligned.
-/// Note however that `addr_of_mut!((*ptr).field)` still requires the projection to
-/// `field` to be in-bounds, using the same rules as [`offset`].
+/// # Safety
+///
+/// The `expr` in `addr_of_mut!(expr)` is evaluated as a place expression, but never loads from the
+/// place or requires the place to be dereferenceable. This means that `addr_of_mut!((*ptr).field)`
+/// still requires the projection to `field` to be in-bounds, using the same rules as [`offset`].
+/// However, `addr_of_mut!(*ptr)` is defined behavior even if `ptr` is null, dangling, or misaligned.
 ///
 /// Note that `Deref`/`Index` coercions (and their mutable counterparts) are applied inside
 /// `addr_of_mut!` like everywhere else, in which case a reference is created to call `Deref::deref`
@@ -2135,7 +2161,7 @@ pub macro addr_of($place:expr) {
 ///
 /// # Examples
 ///
-/// **Creating a pointer to unaligned data:**
+/// **Correct usage: Creating a pointer to unaligned data**
 ///
 /// ```
 /// use std::ptr;
@@ -2153,7 +2179,7 @@ pub macro addr_of($place:expr) {
 /// assert_eq!({packed.f2}, 42); // `{...}` forces copying the field instead of creating a reference.
 /// ```
 ///
-/// **Creating a pointer to uninitialized data:**
+/// **Correct usage: Creating a pointer to uninitialized data**
 ///
 /// ```rust
 /// use std::{ptr, mem::MaybeUninit};
@@ -2169,6 +2195,28 @@ pub macro addr_of($place:expr) {
 /// unsafe { f1_ptr.write(true); }
 /// let init = unsafe { uninit.assume_init() };
 /// ```
+///
+/// **Incorrect usage: Out-of-bounds fields projection**
+///
+/// ```rust,no_run
+/// use std::ptr;
+///
+/// #[repr(C)]
+/// struct MyStruct {
+///     field1: i32,
+///     field2: i32,
+/// }
+///
+/// let ptr: *mut MyStruct = ptr::null_mut();
+/// let fieldptr = unsafe { ptr::addr_of_mut!((*ptr).field2) }; // Undefined Behavior ⚠️
+/// ```
+///
+/// The field projection `.field2` would offset the pointer by 4 bytes,
+/// but the pointer is not in-bounds of an allocation for 4 bytes,
+/// so this offset is Undefined Behavior.
+/// See the [`offset`] docs for a full list of requirements for inbounds pointer arithmetic; the
+/// same requirements apply to field projections, even inside `addr_of_mut!`. (In particular, it
+/// makes no difference whether the pointer is null or dangling.)
 #[stable(feature = "raw_ref_macros", since = "1.51.0")]
 #[rustc_macro_transparency = "semitransparent"]
 #[allow_internal_unstable(raw_ref_op)]