diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 24bdb3fb8b075..70ac6be9b3624 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -36,8 +36,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::mem::{self, MaybeUninit}; -use crate::ptr; +mod uninit; /// A common trait for the ability to explicitly duplicate an object. /// @@ -245,7 +244,7 @@ pub unsafe trait CloneToUninit { /// * `dst` must be properly aligned. /// * `dst` must have the same [pointer metadata] (slice length or `dyn` vtable) as `self`. /// - /// [valid]: ptr#safety + /// [valid]: crate::ptr#safety /// [pointer metadata]: crate::ptr::metadata() /// /// # Panics @@ -269,83 +268,20 @@ pub unsafe trait CloneToUninit { #[unstable(feature = "clone_to_uninit", issue = "126799")] unsafe impl CloneToUninit for T { - #[inline] - default unsafe fn clone_to_uninit(&self, dst: *mut Self) { - // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of - // ptr::write(). - unsafe { - // We hope the optimizer will figure out to create the cloned value in-place, - // skipping ever storing it on the stack and the copy to the destination. - ptr::write(dst, self.clone()); - } - } -} - -// Specialized implementation for types that are [`Copy`], not just [`Clone`], -// and can therefore be copied bitwise. -#[doc(hidden)] -#[unstable(feature = "clone_to_uninit", issue = "126799")] -unsafe impl CloneToUninit for T { #[inline] unsafe fn clone_to_uninit(&self, dst: *mut Self) { - // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of - // ptr::copy_nonoverlapping(). - unsafe { - ptr::copy_nonoverlapping(self, dst, 1); - } + // SAFETY: we're calling a specialization with the same contract + unsafe { ::clone_one(self, dst) } } } #[unstable(feature = "clone_to_uninit", issue = "126799")] unsafe impl CloneToUninit for [T] { - #[inline] - #[cfg_attr(debug_assertions, track_caller)] - default unsafe fn clone_to_uninit(&self, dst: *mut Self) { - let len = self.len(); - // This is the most likely mistake to make, so check it as a debug assertion. - debug_assert_eq!( - len, - dst.len(), - "clone_to_uninit() source and destination must have equal lengths", - ); - - // SAFETY: The produced `&mut` is valid because: - // * The caller is obligated to provide a pointer which is valid for writes. - // * All bytes pointed to are in MaybeUninit, so we don't care about the memory's - // initialization status. - let uninit_ref = unsafe { &mut *(dst as *mut [MaybeUninit]) }; - - // Copy the elements - let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref); - for element_ref in self.iter() { - // If the clone() panics, `initializing` will take care of the cleanup. - initializing.push(element_ref.clone()); - } - // If we reach here, then the entire slice is initialized, and we've satisfied our - // responsibilities to the caller. Disarm the cleanup guard by forgetting it. - mem::forget(initializing); - } -} - -#[doc(hidden)] -#[unstable(feature = "clone_to_uninit", issue = "126799")] -unsafe impl CloneToUninit for [T] { #[inline] #[cfg_attr(debug_assertions, track_caller)] unsafe fn clone_to_uninit(&self, dst: *mut Self) { - let len = self.len(); - // This is the most likely mistake to make, so check it as a debug assertion. - debug_assert_eq!( - len, - dst.len(), - "clone_to_uninit() source and destination must have equal lengths", - ); - - // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of - // ptr::copy_nonoverlapping(). - unsafe { - ptr::copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), len); - } + // SAFETY: we're calling a specialization with the same contract + unsafe { ::clone_slice(self, dst) } } } @@ -371,53 +307,6 @@ unsafe impl CloneToUninit for crate::ffi::CStr { } } -/// Ownership of a collection of values stored in a non-owned `[MaybeUninit]`, some of which -/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation. -/// Its responsibility is to provide cleanup on unwind by dropping the values that *are* -/// initialized, unless disarmed by forgetting. -/// -/// This is a helper for `impl CloneToUninit for [T]`. -struct InitializingSlice<'a, T> { - data: &'a mut [MaybeUninit], - /// Number of elements of `*self.data` that are initialized. - initialized_len: usize, -} - -impl<'a, T> InitializingSlice<'a, T> { - #[inline] - fn from_fully_uninit(data: &'a mut [MaybeUninit]) -> Self { - Self { data, initialized_len: 0 } - } - - /// Push a value onto the end of the initialized part of the slice. - /// - /// # Panics - /// - /// Panics if the slice is already fully initialized. - #[inline] - fn push(&mut self, value: T) { - MaybeUninit::write(&mut self.data[self.initialized_len], value); - self.initialized_len += 1; - } -} - -impl<'a, T> Drop for InitializingSlice<'a, T> { - #[cold] // will only be invoked on unwind - fn drop(&mut self) { - let initialized_slice = ptr::slice_from_raw_parts_mut( - MaybeUninit::slice_as_mut_ptr(self.data), - self.initialized_len, - ); - // SAFETY: - // * the pointer is valid because it was made from a mutable reference - // * `initialized_len` counts the initialized elements as an invariant of this type, - // so each of the pointed-to elements is initialized and may be dropped. - unsafe { - ptr::drop_in_place::<[T]>(initialized_slice); - } - } -} - /// Implementations of `Clone` for primitive types. /// /// Implementations that cannot be described in Rust diff --git a/library/core/src/clone/uninit.rs b/library/core/src/clone/uninit.rs new file mode 100644 index 0000000000000..b8ba7f2fb5de2 --- /dev/null +++ b/library/core/src/clone/uninit.rs @@ -0,0 +1,128 @@ +use crate::mem::{self, MaybeUninit}; +use crate::ptr; + +/// Private specialization trait used by CloneToUninit, as per +/// https://std-dev-guide.rust-lang.org/policy/specialization.html +pub(super) unsafe trait CopySpec: Clone { + unsafe fn clone_one(src: &Self, dst: *mut Self); + unsafe fn clone_slice(src: &[Self], dst: *mut [Self]); +} + +unsafe impl CopySpec for T { + #[inline] + default unsafe fn clone_one(src: &Self, dst: *mut Self) { + // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of + // ptr::write(). + unsafe { + // We hope the optimizer will figure out to create the cloned value in-place, + // skipping ever storing it on the stack and the copy to the destination. + ptr::write(dst, src.clone()); + } + } + + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + default unsafe fn clone_slice(src: &[Self], dst: *mut [Self]) { + let len = src.len(); + // This is the most likely mistake to make, so check it as a debug assertion. + debug_assert_eq!( + len, + dst.len(), + "clone_to_uninit() source and destination must have equal lengths", + ); + + // SAFETY: The produced `&mut` is valid because: + // * The caller is obligated to provide a pointer which is valid for writes. + // * All bytes pointed to are in MaybeUninit, so we don't care about the memory's + // initialization status. + let uninit_ref = unsafe { &mut *(dst as *mut [MaybeUninit]) }; + + // Copy the elements + let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref); + for element_ref in src { + // If the clone() panics, `initializing` will take care of the cleanup. + initializing.push(element_ref.clone()); + } + // If we reach here, then the entire slice is initialized, and we've satisfied our + // responsibilities to the caller. Disarm the cleanup guard by forgetting it. + mem::forget(initializing); + } +} + +// Specialized implementation for types that are [`Copy`], not just [`Clone`], +// and can therefore be copied bitwise. +unsafe impl CopySpec for T { + #[inline] + unsafe fn clone_one(src: &Self, dst: *mut Self) { + // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of + // ptr::copy_nonoverlapping(). + unsafe { + ptr::copy_nonoverlapping(src, dst, 1); + } + } + + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_slice(src: &[Self], dst: *mut [Self]) { + let len = src.len(); + // This is the most likely mistake to make, so check it as a debug assertion. + debug_assert_eq!( + len, + dst.len(), + "clone_to_uninit() source and destination must have equal lengths", + ); + + // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of + // ptr::copy_nonoverlapping(). + unsafe { + ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), len); + } + } +} + +/// Ownership of a collection of values stored in a non-owned `[MaybeUninit]`, some of which +/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation. +/// Its responsibility is to provide cleanup on unwind by dropping the values that *are* +/// initialized, unless disarmed by forgetting. +/// +/// This is a helper for `impl CloneToUninit for [T]`. +struct InitializingSlice<'a, T> { + data: &'a mut [MaybeUninit], + /// Number of elements of `*self.data` that are initialized. + initialized_len: usize, +} + +impl<'a, T> InitializingSlice<'a, T> { + #[inline] + fn from_fully_uninit(data: &'a mut [MaybeUninit]) -> Self { + Self { data, initialized_len: 0 } + } + + /// Push a value onto the end of the initialized part of the slice. + /// + /// # Panics + /// + /// Panics if the slice is already fully initialized. + #[inline] + fn push(&mut self, value: T) { + MaybeUninit::write(&mut self.data[self.initialized_len], value); + self.initialized_len += 1; + } +} + +impl<'a, T> Drop for InitializingSlice<'a, T> { + #[cold] // will only be invoked on unwind + fn drop(&mut self) { + let initialized_slice = ptr::slice_from_raw_parts_mut( + MaybeUninit::slice_as_mut_ptr(self.data), + self.initialized_len, + ); + // SAFETY: + // * the pointer is valid because it was made from a mutable reference + // * `initialized_len` counts the initialized elements as an invariant of this type, + // so each of the pointed-to elements is initialized and may be dropped. + unsafe { + ptr::drop_in_place::<[T]>(initialized_slice); + } + } +}