diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index c302f35e5ed6..f584a9a6d4d6 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -11,10 +11,8 @@ use core::any::Any; use core::cell::CloneFromCell; #[cfg(not(no_global_oom_handling))] -use core::clone::CloneToUninit; -#[cfg(not(no_global_oom_handling))] use core::clone::TrivialClone; -use core::clone::UseCloned; +use core::clone::{CloneToUninit, UseCloned}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; @@ -1442,6 +1440,104 @@ impl Arc, A> { } } +impl Arc { + /// Constructs a new `Arc` with a clone of `value`. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// use std::sync::Arc; + /// + /// let hello: Arc = Arc::clone_from_ref("hello"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + pub fn clone_from_ref(value: &T) -> Arc { + Arc::clone_from_ref_in(value, Global) + } + + /// Constructs a new `Arc` with a clone of `value`, returning an error if allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// + /// let hello: Arc = Arc::try_clone_from_ref("hello")?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + pub fn try_clone_from_ref(value: &T) -> Result, AllocError> { + Arc::try_clone_from_ref_in(value, Global) + } +} + +impl Arc { + /// Constructs a new `Arc` with a clone of `value` in the provided allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let hello: Arc = Arc::clone_from_ref_in("hello", System); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + pub fn clone_from_ref_in(value: &T, alloc: A) -> Arc { + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueArcUninit = UniqueArcUninit::new(value, alloc); + + // Initialize with clone of value. + let initialized_clone = unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + value.clone_to_uninit(in_progress.data_ptr().cast()); + // Cast type of pointer, now that it is initialized. + in_progress.into_arc() + }; + + initialized_clone + } + + /// Constructs a new `Arc` with a clone of `value` in the provided allocator, returning an error if allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let hello: Arc = Arc::try_clone_from_ref_in("hello", System)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + pub fn try_clone_from_ref_in(value: &T, alloc: A) -> Result, AllocError> { + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueArcUninit = UniqueArcUninit::try_new(value, alloc)?; + + // Initialize with clone of value. + let initialized_clone = unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + value.clone_to_uninit(in_progress.data_ptr().cast()); + // Cast type of pointer, now that it is initialized. + in_progress.into_arc() + }; + + Ok(initialized_clone) + } +} + impl Arc<[mem::MaybeUninit], A> { /// Converts to `Arc<[T]>`. /// @@ -4137,16 +4233,15 @@ fn data_offset_align(align: usize) -> usize { /// but will deallocate it (without dropping the value) when dropped. /// /// This is a helper for [`Arc::make_mut()`] to ensure correct cleanup on panic. -#[cfg(not(no_global_oom_handling))] struct UniqueArcUninit { ptr: NonNull>, layout_for_value: Layout, alloc: Option, } -#[cfg(not(no_global_oom_handling))] impl UniqueArcUninit { /// Allocates an ArcInner with layout suitable to contain `for_value` or a clone of it. + #[cfg(not(no_global_oom_handling))] fn new(for_value: &T, alloc: A) -> UniqueArcUninit { let layout = Layout::for_value(for_value); let ptr = unsafe { @@ -4159,6 +4254,20 @@ impl UniqueArcUninit { Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) } } + /// Allocates an ArcInner with layout suitable to contain `for_value` or a clone of it, + /// returning an error if allocation fails. + fn try_new(for_value: &T, alloc: A) -> Result, AllocError> { + let layout = Layout::for_value(for_value); + let ptr = unsafe { + Arc::try_allocate_for_layout( + layout, + |layout_for_arcinner| alloc.allocate(layout_for_arcinner), + |mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const ArcInner), + )? + }; + Ok(Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) }) + } + /// Returns the pointer to be written into to initialize the [`Arc`]. fn data_ptr(&mut self) -> *mut T { let offset = data_offset_align(self.layout_for_value.align());