Skip to content

Commit 569ab6a

Browse files
committed
CloneToUninit: use a private specialization trait
and move implementation details into a submodule
1 parent 26874cc commit 569ab6a

File tree

2 files changed

+134
-117
lines changed

2 files changed

+134
-117
lines changed

Diff for: 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
///
@@ -248,7 +247,7 @@ pub unsafe trait CloneToUninit {
248247
/// * `dst` must be properly aligned.
249248
/// * `dst` must have the same [pointer metadata] (slice length or `dyn` vtable) as `self`.
250249
///
251-
/// [valid]: ptr#safety
250+
/// [valid]: crate::ptr#safety
252251
/// [pointer metadata]: crate::ptr::metadata()
253252
///
254253
/// # Panics
@@ -272,83 +271,20 @@ pub unsafe trait CloneToUninit {
272271

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

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

@@ -374,53 +310,6 @@ unsafe impl CloneToUninit for crate::ffi::CStr {
374310
}
375311
}
376312

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

Diff for: 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+
/// [the dev guide](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)