diff options
| author | Jeremy Smart <jeremy3141592@gmail.com> | 2025-08-20 18:20:53 -0400 |
|---|---|---|
| committer | Jeremy Smart <jeremy3141592@gmail.com> | 2025-10-01 18:47:09 -0400 |
| commit | 08b4641f50f6f8fa3aa222fa606a0c1a2482b72d (patch) | |
| tree | a4c2800bd5553fa8adcfdcabd7fde33ff2c2efd2 | |
| parent | e9385f9eea0221ef295a188d49d16f8f5189abf1 (diff) | |
| download | rust-08b4641f50f6f8fa3aa222fa606a0c1a2482b72d.tar.gz rust-08b4641f50f6f8fa3aa222fa606a0c1a2482b72d.zip | |
add CloneFromCell and Cell::get_cloned
| -rw-r--r-- | library/alloc/src/lib.rs | 1 | ||||
| -rw-r--r-- | library/alloc/src/rc.rs | 10 | ||||
| -rw-r--r-- | library/alloc/src/sync.rs | 9 | ||||
| -rw-r--r-- | library/core/src/cell.rs | 92 | ||||
| -rw-r--r-- | library/core/src/tuple.rs | 10 |
5 files changed, 119 insertions, 3 deletions
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index cba1ce40f75..e7cfd01e654 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -103,6 +103,7 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(cast_maybe_uninit)] +#![feature(cell_get_cloned)] #![feature(char_internals)] #![feature(char_max_len)] #![feature(clone_to_uninit)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index aed3357afbf..783b6147e95 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -242,7 +242,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::any::Any; -use core::cell::Cell; +use core::cell::{Cell, CloneFromCell}; #[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; use core::clone::UseCloned; @@ -338,6 +338,10 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Rc<U, A>> for #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Rc<U>> for Rc<T> {} +// SAFETY: `Rc::clone` doesn't access any `Cell`s which could contain the `Rc` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: ?Sized> CloneFromCell for Rc<T> {} + impl<T: ?Sized> Rc<T> { #[inline] unsafe fn from_inner(ptr: NonNull<RcInner<T>>) -> Self { @@ -3011,6 +3015,10 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Weak<U, A>> f #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Weak<U>> for Weak<T> {} +// SAFETY: `Weak::clone` doesn't access any `Cell`s which could contain the `Weak` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: ?Sized> CloneFromCell for Weak<T> {} + impl<T> Weak<T> { /// Constructs a new `Weak<T>`, without allocating any memory. /// Calling [`upgrade`] on the return value always gives [`None`]. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a466b74944c..eddc9047f21 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -9,6 +9,7 @@ //! `#[cfg(target_has_atomic = "ptr")]`. use core::any::Any; +use core::cell::CloneFromCell; #[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; use core::clone::UseCloned; @@ -281,6 +282,10 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Arc<U, A>> fo #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Arc<U>> for Arc<T> {} +// SAFETY: `Arc::clone` doesn't access any `Cell`s which could contain the `Arc` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: ?Sized> CloneFromCell for Arc<T> {} + impl<T: ?Sized> Arc<T> { unsafe fn from_inner(ptr: NonNull<ArcInner<T>>) -> Self { unsafe { Self::from_inner_in(ptr, Global) } @@ -356,6 +361,10 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Weak<U, A>> f #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Weak<U>> for Weak<T> {} +// SAFETY: `Weak::clone` doesn't access any `Cell`s which could contain the `Weak` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: ?Sized> CloneFromCell for Weak<T> {} + #[stable(feature = "arc_weak", since = "1.4.0")] impl<T: ?Sized, A: Allocator> fmt::Debug for Weak<T, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 7d4a66640b1..a991bbf46b1 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -253,11 +253,12 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; use crate::marker::{PhantomData, Unsize}; -use crate::mem; -use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; +use crate::mem::{self, ManuallyDrop}; +use crate::ops::{self, CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; use crate::panic::const_panic; use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; +use crate::range; mod lazy; mod once; @@ -713,6 +714,93 @@ impl<T, const N: usize> Cell<[T; N]> { } } +/// Types for which cloning `Cell<Self>` is sound. +/// +/// # Safety +/// +/// Implementing this trait for a type is sound if and only if the following code is sound for T = +/// that type. +/// +/// ``` +/// #![feature(cell_get_cloned)] +/// # use std::cell::{CloneFromCell, Cell}; +/// fn clone_from_cell<T: CloneFromCell>(cell: &Cell<T>) -> T { +/// unsafe { T::clone(&*cell.as_ptr()) } +/// } +/// ``` +/// +/// Importantly, you can't just implement `CloneFromCell` for any arbitrary `Copy` type, e.g. the +/// following is unsound: +/// +/// ```rust +/// #![feature(cell_get_cloned)] +/// # use std::cell::Cell; +/// +/// #[derive(Copy, Debug)] +/// pub struct Bad<'a>(Option<&'a Cell<Bad<'a>>>, u8); +/// +/// impl Clone for Bad<'_> { +/// fn clone(&self) -> Self { +/// let a: &u8 = &self.1; +/// // when self.0 points to self, we write to self.1 while we have a live `&u8` pointing to +/// // it -- this is UB +/// self.0.unwrap().set(Self(None, 1)); +/// dbg!((a, self)); +/// Self(None, 0) +/// } +/// } +/// +/// // this is not sound +/// // unsafe impl CloneFromCell for Bad<'_> {} +/// ``` +#[unstable(feature = "cell_get_cloned", issue = "145329")] +// Allow potential overlapping implementations in user code +#[marker] +pub unsafe trait CloneFromCell: Clone {} + +// `CloneFromCell` can be implemented for types that don't have indirection and which don't access +// `Cell`s in their `Clone` implementation. A commonly-used subset is covered here. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: CloneFromCell, const N: usize> CloneFromCell for [T; N] {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: CloneFromCell> CloneFromCell for Option<T> {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: CloneFromCell, E: CloneFromCell> CloneFromCell for Result<T, E> {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: ?Sized> CloneFromCell for PhantomData<T> {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: CloneFromCell> CloneFromCell for ManuallyDrop<T> {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: CloneFromCell> CloneFromCell for ops::Range<T> {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl<T: CloneFromCell> CloneFromCell for range::Range<T> {} + +#[unstable(feature = "cell_get_cloned", issue = "145329")] +impl<T: CloneFromCell> Cell<T> { + /// Get a clone of the `Cell` that contains a copy of the original value. + /// + /// This allows a cheaply `Clone`-able type like an `Rc` to be stored in a `Cell`, exposing the + /// cheaper `clone()` method. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_get_cloned)] + /// + /// use core::cell::Cell; + /// use std::rc::Rc; + /// + /// let rc = Rc::new(1usize); + /// let c1 = Cell::new(rc); + /// let c2 = c1.get_cloned(); + /// assert_eq!(*c2.into_inner(), 1); + /// ``` + pub fn get_cloned(&self) -> Self { + // SAFETY: T is CloneFromCell, which guarantees that this is sound. + Cell::new(T::clone(unsafe { &*self.as_ptr() })) + } +} + /// A mutable memory location with dynamically checked borrow rules /// /// See the [module-level documentation](self) for more. diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index c57a8d81ade..58f81372aff 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,5 +1,6 @@ // See core/src/primitive_docs.rs for documentation. +use crate::cell::CloneFromCell; use crate::cmp::Ordering::{self, *}; use crate::marker::{ConstParamTy_, StructuralPartialEq}; use crate::ops::ControlFlow::{self, Break, Continue}; @@ -155,6 +156,15 @@ macro_rules! tuple_impls { } } } + + maybe_tuple_doc! { + $($T)+ @ + // SAFETY: tuples introduce no additional indirection, so they can be copied whenever T + // can. + #[unstable(feature = "cell_get_cloned", issue = "145329")] + unsafe impl<$($T: CloneFromCell),+> CloneFromCell for ($($T,)+) + {} + } } } |
