Skip to content

Commit cbc0a73

Browse files
authored
Rollup merge of #101717 - Pointerbender:unsafecell-memory-layout, r=Amanieu
Add documentation about the memory layout of `UnsafeCell<T>` The documentation for `UnsafeCell<T>` currently does not make any promises about its memory layout. This PR adds this documentation, namely that the memory layout of `UnsafeCell<T>` is the same as the memory layout of its inner `T`. # Use case Without this layout promise, the following cast would not be legally possible: ```rust fn example<T>(ptr: *mut T) -> *const UnsafeCell<T> { ptr as *const UnsafeCell<T> } ``` A use case where this can come up involves FFI. If Rust receives a pointer over a FFI boundary which provides shared read-write access (with some form of custom synchronization), and this pointer is managed by some Rust struct with lifetime `'a`, then it would greatly simplify its (internal) API and safety contract if a `&'a UnsafeCell<T>` can be created from a raw FFI pointer `*mut T`. A lot of safety checks can be done when receiving the pointer for the first time through FFI (non-nullness, alignment, initialize uninit bytes, etc.) and these properties can then be encoded into the `&UnsafeCell<T>` type. Without this documentation guarantee, this is not legal today outside of the standard library. # Caveats Casting in the opposite direction is still not valid, even with this documentation change: ```rust fn example2<T>(ptr: &UnsafeCell<T>) -> &mut T { let t = ptr as *const UnsafeCell<T> as *mut T; unsafe { &mut *t } } ``` This is because the only legal way to obtain a mutable pointer to the contents of the shared reference is through [`UnsafeCell::get`](https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html#method.get) and [`UnsafeCell::raw_get`](https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html#method.raw_get). Although there might be a desire to also make this legal at some point in the future, that part is outside the scope of this PR. Also see this relevant [Zulip thread](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-lang.2Fwg-unsafe-code-guidelines/topic/transmuting.20.26.20-.3E.20.26mut). # Alternatives Instead of adding a new documentation promise, it's also possible to add a new method to `UnsafeCell<T>` with signature `pub fn from_ptr_bikeshed(ptr: *mut T) -> *const UnsafeCell<T>` which indirectly only allows one-way casting to `*const UnsafeCell<T>`.
2 parents ddc7fd9 + ddd119b commit cbc0a73

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

library/core/src/cell.rs

+44
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,50 @@ impl<T: ?Sized + fmt::Display> fmt::Display for RefMut<'_, T> {
18161816
///
18171817
/// [`.get_mut()`]: `UnsafeCell::get_mut`
18181818
///
1819+
/// `UnsafeCell<T>` has the same in-memory representation as its inner type `T`. A consequence
1820+
/// of this guarantee is that it is possible to convert between `T` and `UnsafeCell<T>`.
1821+
/// Special care has to be taken when converting a nested `T` inside of an `Outer<T>` type
1822+
/// to an `Outer<UnsafeCell<T>>` type: this is not sound when the `Outer<T>` type enables [niche]
1823+
/// optimizations. For example, the type `Option<NonNull<u8>>` is typically 8 bytes large on
1824+
/// 64-bit platforms, but the type `Option<UnsafeCell<NonNull<u8>>>` takes up 16 bytes of space.
1825+
/// Therefore this is not a valid conversion, despite `NonNull<u8>` and `UnsafeCell<NonNull<u8>>>`
1826+
/// having the same memory layout. This is because `UnsafeCell` disables niche optimizations in
1827+
/// order to avoid its interior mutability property from spreading from `T` into the `Outer` type,
1828+
/// thus this can cause distortions in the type size in these cases. Furthermore, it is only valid
1829+
/// to obtain a `*mut T` pointer to the contents of a _shared_ `UnsafeCell<T>` through [`.get()`]
1830+
/// or [`.raw_get()`]. A `&mut T` reference can be obtained by either dereferencing this pointer or
1831+
/// by calling [`.get_mut()`] on an _exclusive_ `UnsafeCell<T>`, e.g.:
1832+
///
1833+
/// ```rust
1834+
/// use std::cell::UnsafeCell;
1835+
///
1836+
/// let mut x: UnsafeCell<u32> = UnsafeCell::new(5);
1837+
/// let shared: &UnsafeCell<u32> = &x;
1838+
/// // using `.get()` is okay:
1839+
/// unsafe {
1840+
/// // SAFETY: there exist no other references to the contents of `x`
1841+
/// let exclusive: &mut u32 = &mut *shared.get();
1842+
/// };
1843+
/// // using `.raw_get()` is also okay:
1844+
/// unsafe {
1845+
/// // SAFETY: there exist no other references to the contents of `x` in this scope
1846+
/// let exclusive: &mut u32 = &mut *UnsafeCell::raw_get(shared as *const _);
1847+
/// };
1848+
/// // using `.get_mut()` is always safe:
1849+
/// let exclusive: &mut u32 = x.get_mut();
1850+
///
1851+
/// // when we have exclusive access, we can convert it to a shared `&UnsafeCell`:
1852+
/// unsafe {
1853+
/// // SAFETY: `u32` has no niche, therefore it has the same layout as `UnsafeCell<u32>`
1854+
/// let shared: &UnsafeCell<u32> = &*(exclusive as *mut _ as *const UnsafeCell<u32>);
1855+
/// // SAFETY: there exist no other *active* references to the contents of `x` in this scope
1856+
/// let exclusive: &mut u32 = &mut *shared.get();
1857+
/// }
1858+
/// ```
1859+
///
1860+
/// [niche]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#niche
1861+
/// [`.raw_get()`]: `UnsafeCell::raw_get`
1862+
///
18191863
/// # Examples
18201864
///
18211865
/// Here is an example showcasing how to soundly mutate the contents of an `UnsafeCell<_>` despite

0 commit comments

Comments
 (0)