From 302e33fde2785874c6b85aa9760e8e6be2e7dad4 Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Mon, 12 Sep 2022 11:12:28 +0200 Subject: [PATCH 1/4] add description of the memory layout for `UnsafeCell` --- library/core/src/cell.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index fb4454c94cb..cce4e5a5946 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1811,6 +1811,8 @@ impl fmt::Display for RefMut<'_, T> { /// /// [`.get_mut()`]: `UnsafeCell::get_mut` /// +/// `UnsafeCell` has the same in-memory representation as its inner type `T`. +/// /// # Examples /// /// Here is an example showcasing how to soundly mutate the contents of an `UnsafeCell<_>` despite From 13bc0996ddb64919feefb256ad9468087333121d Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Wed, 14 Sep 2022 10:10:18 +0200 Subject: [PATCH 2/4] expand documentation on type conversion w.r.t. `UnsafeCell` --- library/core/src/cell.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index cce4e5a5946..b730e13dcb8 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1811,7 +1811,36 @@ impl fmt::Display for RefMut<'_, T> { /// /// [`.get_mut()`]: `UnsafeCell::get_mut` /// -/// `UnsafeCell` has the same in-memory representation as its inner type `T`. +/// `UnsafeCell` has the same in-memory representation as its inner type `T`. A consequence +/// of this guarantee is that it is possible to convert between `T` and `UnsafeCell`. +/// However, it is only valid to obtain a `*mut T` pointer or `&mut T` reference to the +/// contents of an `UnsafeCell` through [`.get()`], [`.raw_get()`] or [`.get_mut()`], e.g.: +/// +/// ```rust +/// use std::cell::UnsafeCell; +/// +/// let mut x: UnsafeCell = UnsafeCell::new(5); +/// let p1: &UnsafeCell = &x; +/// // using `.get()` is okay: +/// unsafe { +/// // SAFETY: there exist no other references to the contents of `x` +/// let p2: &mut u32 = &mut *p1.get(); +/// }; +/// // using `.raw_get()` is also okay: +/// unsafe { +/// // SAFETY: there exist no other references to the contents of `x` in this scope +/// let p2: &mut u32 = &mut *UnsafeCell::raw_get(p1 as *const _); +/// }; +/// // using `.get_mut()` is always safe: +/// let p2: &mut u32 = x.get_mut(); +/// // but the following is not allowed! +/// // let p2: &mut u32 = unsafe { +/// // let t: *mut u32 = &x as *const _ as *mut u32; +/// // &mut *t +/// // }; +/// ``` +/// +/// [`.raw_get()`]: `UnsafeCell::raw_get` /// /// # Examples /// From 9c37c801ad737006c494f6b3bd97ff59764573ae Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Sun, 9 Oct 2022 22:32:23 +0200 Subject: [PATCH 3/4] expand documentation on type conversion w.r.t. `UnsafeCell` --- library/core/src/cell.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index b730e13dcb8..be1017e5842 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1811,35 +1811,43 @@ impl fmt::Display for RefMut<'_, T> { /// /// [`.get_mut()`]: `UnsafeCell::get_mut` /// -/// `UnsafeCell` has the same in-memory representation as its inner type `T`. A consequence -/// of this guarantee is that it is possible to convert between `T` and `UnsafeCell`. -/// However, it is only valid to obtain a `*mut T` pointer or `&mut T` reference to the -/// contents of an `UnsafeCell` through [`.get()`], [`.raw_get()`] or [`.get_mut()`], e.g.: +/// `UnsafeCell` has the same in-memory representation as its inner type `T` if and only if +/// the type `T` does not contain a [niche] (e.g. the type `Option>` is typically +/// 8 bytes large on 64-bit platforms, but the type `Option>>` takes +/// up 16 bytes of space). A consequence of this guarantee is that it is possible to convert +/// between `T` and `UnsafeCell` when `T` has no niches. However, it is only valid to obtain +/// a `*mut T` pointer to the contents of a _shared_ `UnsafeCell` through [`.get()`] or +/// [`.raw_get()`]. A `&mut T` reference can be obtained by either dereferencing this pointer +/// or by calling [`.get_mut()`] on an _exclusive_ `UnsafeCell`, e.g.: /// /// ```rust /// use std::cell::UnsafeCell; /// /// let mut x: UnsafeCell = UnsafeCell::new(5); -/// let p1: &UnsafeCell = &x; +/// let shared: &UnsafeCell = &x; /// // using `.get()` is okay: /// unsafe { /// // SAFETY: there exist no other references to the contents of `x` -/// let p2: &mut u32 = &mut *p1.get(); +/// let exclusive: &mut u32 = &mut *shared.get(); /// }; /// // using `.raw_get()` is also okay: /// unsafe { /// // SAFETY: there exist no other references to the contents of `x` in this scope -/// let p2: &mut u32 = &mut *UnsafeCell::raw_get(p1 as *const _); +/// let exclusive: &mut u32 = &mut *UnsafeCell::raw_get(shared as *const _); /// }; /// // using `.get_mut()` is always safe: -/// let p2: &mut u32 = x.get_mut(); -/// // but the following is not allowed! -/// // let p2: &mut u32 = unsafe { -/// // let t: *mut u32 = &x as *const _ as *mut u32; -/// // &mut *t -/// // }; +/// let exclusive: &mut u32 = x.get_mut(); +/// +/// // when we have exclusive access, we can convert it to a shared `&UnsafeCell`: +/// unsafe { +/// // SAFETY: `u32` has no niche, therefore it has the same layout as `UnsafeCell` +/// let shared: &UnsafeCell = &*(exclusive as *mut _ as *const UnsafeCell); +/// // SAFETY: there exist no other *active* references to the contents of `x` in this scope +/// let exclusive: &mut u32 = &mut *shared.get(); +/// } /// ``` /// +/// [niche]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#niche /// [`.raw_get()`]: `UnsafeCell::raw_get` /// /// # Examples From ddd119b2fed57eb6b19c44c18108de95c564a48d Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Wed, 12 Oct 2022 23:34:13 +0200 Subject: [PATCH 4/4] expand documentation on type conversion w.r.t. `UnsafeCell` --- library/core/src/cell.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index be1017e5842..4dee386f887 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1811,14 +1811,19 @@ impl fmt::Display for RefMut<'_, T> { /// /// [`.get_mut()`]: `UnsafeCell::get_mut` /// -/// `UnsafeCell` has the same in-memory representation as its inner type `T` if and only if -/// the type `T` does not contain a [niche] (e.g. the type `Option>` is typically -/// 8 bytes large on 64-bit platforms, but the type `Option>>` takes -/// up 16 bytes of space). A consequence of this guarantee is that it is possible to convert -/// between `T` and `UnsafeCell` when `T` has no niches. However, it is only valid to obtain -/// a `*mut T` pointer to the contents of a _shared_ `UnsafeCell` through [`.get()`] or -/// [`.raw_get()`]. A `&mut T` reference can be obtained by either dereferencing this pointer -/// or by calling [`.get_mut()`] on an _exclusive_ `UnsafeCell`, e.g.: +/// `UnsafeCell` has the same in-memory representation as its inner type `T`. A consequence +/// of this guarantee is that it is possible to convert between `T` and `UnsafeCell`. +/// Special care has to be taken when converting a nested `T` inside of an `Outer` type +/// to an `Outer>` type: this is not sound when the `Outer` type enables [niche] +/// optimizations. For example, the type `Option>` is typically 8 bytes large on +/// 64-bit platforms, but the type `Option>>` takes up 16 bytes of space. +/// Therefore this is not a valid conversion, despite `NonNull` and `UnsafeCell>>` +/// having the same memory layout. This is because `UnsafeCell` disables niche optimizations in +/// order to avoid its interior mutability property from spreading from `T` into the `Outer` type, +/// thus this can cause distortions in the type size in these cases. Furthermore, it is only valid +/// to obtain a `*mut T` pointer to the contents of a _shared_ `UnsafeCell` through [`.get()`] +/// or [`.raw_get()`]. A `&mut T` reference can be obtained by either dereferencing this pointer or +/// by calling [`.get_mut()`] on an _exclusive_ `UnsafeCell`, e.g.: /// /// ```rust /// use std::cell::UnsafeCell;