Skip to content

Commit c07c7d6

Browse files
committed
CloneToUninit: use a private specialization trait
and move implementation details into a submodule
1 parent c4139df commit c07c7d6

File tree

2 files changed

+134
-117
lines changed

2 files changed

+134
-117
lines changed

library/core/src/clone.rs

+6-117
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@
3636
3737
#![stable(feature = "rust1", since = "1.0.0")]
3838

39-
use crate::mem::{self, MaybeUninit};
40-
use crate::ptr;
39+
mod uninit;
4140

4241
/// A common trait for the ability to explicitly duplicate an object.
4342
///
@@ -245,7 +244,7 @@ pub unsafe trait CloneToUninit {
245244
/// * `dst` must be properly aligned.
246245
/// * `dst` must have the same [pointer metadata] (slice length or `dyn` vtable) as `self`.
247246
///
248-
/// [valid]: ptr#safety
247+
/// [valid]: crate::ptr#safety
249248
/// [pointer metadata]: crate::ptr::metadata()
250249
///
251250
/// # Panics
@@ -269,83 +268,20 @@ pub unsafe trait CloneToUninit {
269268

270269
#[unstable(feature = "clone_to_uninit", issue = "126799")]
271270
unsafe impl<T: Clone> CloneToUninit for T {
272-
#[inline]
273-
default unsafe fn clone_to_uninit(&self, dst: *mut Self) {
274-
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
275-
// ptr::write().
276-
unsafe {
277-
// We hope the optimizer will figure out to create the cloned value in-place,
278-
// skipping ever storing it on the stack and the copy to the destination.
279-
ptr::write(dst, self.clone());
280-
}
281-
}
282-
}
283-
284-
// Specialized implementation for types that are [`Copy`], not just [`Clone`],
285-
// and can therefore be copied bitwise.
286-
#[doc(hidden)]
287-
#[unstable(feature = "clone_to_uninit", issue = "126799")]
288-
unsafe impl<T: Copy> CloneToUninit for T {
289271
#[inline]
290272
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
291-
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
292-
// ptr::copy_nonoverlapping().
293-
unsafe {
294-
ptr::copy_nonoverlapping(self, dst, 1);
295-
}
273+
// SAFETY: we're calling a specialization with the same contract
274+
unsafe { <T as self::uninit::CopySpec>::clone_one(self, dst) }
296275
}
297276
}
298277

299278
#[unstable(feature = "clone_to_uninit", issue = "126799")]
300279
unsafe impl<T: Clone> CloneToUninit for [T] {
301-
#[inline]
302-
#[cfg_attr(debug_assertions, track_caller)]
303-
default unsafe fn clone_to_uninit(&self, dst: *mut Self) {
304-
let len = self.len();
305-
// This is the most likely mistake to make, so check it as a debug assertion.
306-
debug_assert_eq!(
307-
len,
308-
dst.len(),
309-
"clone_to_uninit() source and destination must have equal lengths",
310-
);
311-
312-
// SAFETY: The produced `&mut` is valid because:
313-
// * The caller is obligated to provide a pointer which is valid for writes.
314-
// * All bytes pointed to are in MaybeUninit, so we don't care about the memory's
315-
// initialization status.
316-
let uninit_ref = unsafe { &mut *(dst as *mut [MaybeUninit<T>]) };
317-
318-
// Copy the elements
319-
let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref);
320-
for element_ref in self.iter() {
321-
// If the clone() panics, `initializing` will take care of the cleanup.
322-
initializing.push(element_ref.clone());
323-
}
324-
// If we reach here, then the entire slice is initialized, and we've satisfied our
325-
// responsibilities to the caller. Disarm the cleanup guard by forgetting it.
326-
mem::forget(initializing);
327-
}
328-
}
329-
330-
#[doc(hidden)]
331-
#[unstable(feature = "clone_to_uninit", issue = "126799")]
332-
unsafe impl<T: Copy> CloneToUninit for [T] {
333280
#[inline]
334281
#[cfg_attr(debug_assertions, track_caller)]
335282
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
336-
let len = self.len();
337-
// This is the most likely mistake to make, so check it as a debug assertion.
338-
debug_assert_eq!(
339-
len,
340-
dst.len(),
341-
"clone_to_uninit() source and destination must have equal lengths",
342-
);
343-
344-
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
345-
// ptr::copy_nonoverlapping().
346-
unsafe {
347-
ptr::copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), len);
348-
}
283+
// SAFETY: we're calling a specialization with the same contract
284+
unsafe { <T as self::uninit::CopySpec>::clone_slice(self, dst) }
349285
}
350286
}
351287

@@ -371,53 +307,6 @@ unsafe impl CloneToUninit for crate::ffi::CStr {
371307
}
372308
}
373309

374-
/// Ownership of a collection of values stored in a non-owned `[MaybeUninit<T>]`, some of which
375-
/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation.
376-
/// Its responsibility is to provide cleanup on unwind by dropping the values that *are*
377-
/// initialized, unless disarmed by forgetting.
378-
///
379-
/// This is a helper for `impl<T: Clone> CloneToUninit for [T]`.
380-
struct InitializingSlice<'a, T> {
381-
data: &'a mut [MaybeUninit<T>],
382-
/// Number of elements of `*self.data` that are initialized.
383-
initialized_len: usize,
384-
}
385-
386-
impl<'a, T> InitializingSlice<'a, T> {
387-
#[inline]
388-
fn from_fully_uninit(data: &'a mut [MaybeUninit<T>]) -> Self {
389-
Self { data, initialized_len: 0 }
390-
}
391-
392-
/// Push a value onto the end of the initialized part of the slice.
393-
///
394-
/// # Panics
395-
///
396-
/// Panics if the slice is already fully initialized.
397-
#[inline]
398-
fn push(&mut self, value: T) {
399-
MaybeUninit::write(&mut self.data[self.initialized_len], value);
400-
self.initialized_len += 1;
401-
}
402-
}
403-
404-
impl<'a, T> Drop for InitializingSlice<'a, T> {
405-
#[cold] // will only be invoked on unwind
406-
fn drop(&mut self) {
407-
let initialized_slice = ptr::slice_from_raw_parts_mut(
408-
MaybeUninit::slice_as_mut_ptr(self.data),
409-
self.initialized_len,
410-
);
411-
// SAFETY:
412-
// * the pointer is valid because it was made from a mutable reference
413-
// * `initialized_len` counts the initialized elements as an invariant of this type,
414-
// so each of the pointed-to elements is initialized and may be dropped.
415-
unsafe {
416-
ptr::drop_in_place::<[T]>(initialized_slice);
417-
}
418-
}
419-
}
420-
421310
/// Implementations of `Clone` for primitive types.
422311
///
423312
/// Implementations that cannot be described in Rust

library/core/src/clone/uninit.rs

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use crate::mem::{self, MaybeUninit};
2+
use crate::ptr;
3+
4+
/// Private specialization trait used by CloneToUninit, as per
5+
/// https://std-dev-guide.rust-lang.org/policy/specialization.html
6+
pub(super) unsafe trait CopySpec: Clone {
7+
unsafe fn clone_one(src: &Self, dst: *mut Self);
8+
unsafe fn clone_slice(src: &[Self], dst: *mut [Self]);
9+
}
10+
11+
unsafe impl<T: Clone> CopySpec for T {
12+
#[inline]
13+
default unsafe fn clone_one(src: &Self, dst: *mut Self) {
14+
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
15+
// ptr::write().
16+
unsafe {
17+
// We hope the optimizer will figure out to create the cloned value in-place,
18+
// skipping ever storing it on the stack and the copy to the destination.
19+
ptr::write(dst, src.clone());
20+
}
21+
}
22+
23+
#[inline]
24+
#[cfg_attr(debug_assertions, track_caller)]
25+
default unsafe fn clone_slice(src: &[Self], dst: *mut [Self]) {
26+
let len = src.len();
27+
// This is the most likely mistake to make, so check it as a debug assertion.
28+
debug_assert_eq!(
29+
len,
30+
dst.len(),
31+
"clone_to_uninit() source and destination must have equal lengths",
32+
);
33+
34+
// SAFETY: The produced `&mut` is valid because:
35+
// * The caller is obligated to provide a pointer which is valid for writes.
36+
// * All bytes pointed to are in MaybeUninit, so we don't care about the memory's
37+
// initialization status.
38+
let uninit_ref = unsafe { &mut *(dst as *mut [MaybeUninit<T>]) };
39+
40+
// Copy the elements
41+
let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref);
42+
for element_ref in src {
43+
// If the clone() panics, `initializing` will take care of the cleanup.
44+
initializing.push(element_ref.clone());
45+
}
46+
// If we reach here, then the entire slice is initialized, and we've satisfied our
47+
// responsibilities to the caller. Disarm the cleanup guard by forgetting it.
48+
mem::forget(initializing);
49+
}
50+
}
51+
52+
// Specialized implementation for types that are [`Copy`], not just [`Clone`],
53+
// and can therefore be copied bitwise.
54+
unsafe impl<T: Copy> CopySpec for T {
55+
#[inline]
56+
unsafe fn clone_one(src: &Self, dst: *mut Self) {
57+
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
58+
// ptr::copy_nonoverlapping().
59+
unsafe {
60+
ptr::copy_nonoverlapping(src, dst, 1);
61+
}
62+
}
63+
64+
#[inline]
65+
#[cfg_attr(debug_assertions, track_caller)]
66+
unsafe fn clone_slice(src: &[Self], dst: *mut [Self]) {
67+
let len = src.len();
68+
// This is the most likely mistake to make, so check it as a debug assertion.
69+
debug_assert_eq!(
70+
len,
71+
dst.len(),
72+
"clone_to_uninit() source and destination must have equal lengths",
73+
);
74+
75+
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
76+
// ptr::copy_nonoverlapping().
77+
unsafe {
78+
ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), len);
79+
}
80+
}
81+
}
82+
83+
/// Ownership of a collection of values stored in a non-owned `[MaybeUninit<T>]`, some of which
84+
/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation.
85+
/// Its responsibility is to provide cleanup on unwind by dropping the values that *are*
86+
/// initialized, unless disarmed by forgetting.
87+
///
88+
/// This is a helper for `impl<T: Clone> CloneToUninit for [T]`.
89+
struct InitializingSlice<'a, T> {
90+
data: &'a mut [MaybeUninit<T>],
91+
/// Number of elements of `*self.data` that are initialized.
92+
initialized_len: usize,
93+
}
94+
95+
impl<'a, T> InitializingSlice<'a, T> {
96+
#[inline]
97+
fn from_fully_uninit(data: &'a mut [MaybeUninit<T>]) -> Self {
98+
Self { data, initialized_len: 0 }
99+
}
100+
101+
/// Push a value onto the end of the initialized part of the slice.
102+
///
103+
/// # Panics
104+
///
105+
/// Panics if the slice is already fully initialized.
106+
#[inline]
107+
fn push(&mut self, value: T) {
108+
MaybeUninit::write(&mut self.data[self.initialized_len], value);
109+
self.initialized_len += 1;
110+
}
111+
}
112+
113+
impl<'a, T> Drop for InitializingSlice<'a, T> {
114+
#[cold] // will only be invoked on unwind
115+
fn drop(&mut self) {
116+
let initialized_slice = ptr::slice_from_raw_parts_mut(
117+
MaybeUninit::slice_as_mut_ptr(self.data),
118+
self.initialized_len,
119+
);
120+
// SAFETY:
121+
// * the pointer is valid because it was made from a mutable reference
122+
// * `initialized_len` counts the initialized elements as an invariant of this type,
123+
// so each of the pointed-to elements is initialized and may be dropped.
124+
unsafe {
125+
ptr::drop_in_place::<[T]>(initialized_slice);
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)