Skip to content

Commit bd7aa57

Browse files
committed
Auto merge of rust-lang#126877 - GrigorenkoPV:clone_to_uninit, r=dtolnay
CloneToUninit impls As per rust-lang#126799. Also implements it for `Wtf8` and both versions of `os_str::Slice`. Maybe it is worth to slap `#[inline]` on some of those impls. r? `@dtolnay`
2 parents d3c08f8 + 569ab6a commit bd7aa57

File tree

11 files changed

+290
-105
lines changed

11 files changed

+290
-105
lines changed

core/src/clone.rs

+22-105
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,124 +271,42 @@ pub unsafe trait CloneToUninit {
272271

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

299281
#[unstable(feature = "clone_to_uninit", issue = "126799")]
300282
unsafe impl<T: Clone> CloneToUninit for [T] {
283+
#[inline]
301284
#[cfg_attr(debug_assertions, track_caller)]
302-
default unsafe fn clone_to_uninit(&self, dst: *mut Self) {
303-
let len = self.len();
304-
// This is the most likely mistake to make, so check it as a debug assertion.
305-
debug_assert_eq!(
306-
len,
307-
dst.len(),
308-
"clone_to_uninit() source and destination must have equal lengths",
309-
);
310-
311-
// SAFETY: The produced `&mut` is valid because:
312-
// * The caller is obligated to provide a pointer which is valid for writes.
313-
// * All bytes pointed to are in MaybeUninit, so we don't care about the memory's
314-
// initialization status.
315-
let uninit_ref = unsafe { &mut *(dst as *mut [MaybeUninit<T>]) };
316-
317-
// Copy the elements
318-
let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref);
319-
for element_ref in self.iter() {
320-
// If the clone() panics, `initializing` will take care of the cleanup.
321-
initializing.push(element_ref.clone());
322-
}
323-
// If we reach here, then the entire slice is initialized, and we've satisfied our
324-
// responsibilities to the caller. Disarm the cleanup guard by forgetting it.
325-
mem::forget(initializing);
285+
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
286+
// SAFETY: we're calling a specialization with the same contract
287+
unsafe { <T as self::uninit::CopySpec>::clone_slice(self, dst) }
326288
}
327289
}
328290

329291
#[unstable(feature = "clone_to_uninit", issue = "126799")]
330-
unsafe impl<T: Copy> CloneToUninit for [T] {
292+
unsafe impl CloneToUninit for str {
293+
#[inline]
331294
#[cfg_attr(debug_assertions, track_caller)]
332295
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
333-
let len = self.len();
334-
// This is the most likely mistake to make, so check it as a debug assertion.
335-
debug_assert_eq!(
336-
len,
337-
dst.len(),
338-
"clone_to_uninit() source and destination must have equal lengths",
339-
);
340-
341-
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
342-
// ptr::copy_nonoverlapping().
343-
unsafe {
344-
ptr::copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), len);
345-
}
296+
// SAFETY: str is just a [u8] with UTF-8 invariant
297+
unsafe { self.as_bytes().clone_to_uninit(dst as *mut [u8]) }
346298
}
347299
}
348300

349-
/// Ownership of a collection of values stored in a non-owned `[MaybeUninit<T>]`, some of which
350-
/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation.
351-
/// Its responsibility is to provide cleanup on unwind by dropping the values that *are*
352-
/// initialized, unless disarmed by forgetting.
353-
///
354-
/// This is a helper for `impl<T: Clone> CloneToUninit for [T]`.
355-
struct InitializingSlice<'a, T> {
356-
data: &'a mut [MaybeUninit<T>],
357-
/// Number of elements of `*self.data` that are initialized.
358-
initialized_len: usize,
359-
}
360-
361-
impl<'a, T> InitializingSlice<'a, T> {
362-
#[inline]
363-
fn from_fully_uninit(data: &'a mut [MaybeUninit<T>]) -> Self {
364-
Self { data, initialized_len: 0 }
365-
}
366-
367-
/// Push a value onto the end of the initialized part of the slice.
368-
///
369-
/// # Panics
370-
///
371-
/// Panics if the slice is already fully initialized.
372-
#[inline]
373-
fn push(&mut self, value: T) {
374-
MaybeUninit::write(&mut self.data[self.initialized_len], value);
375-
self.initialized_len += 1;
376-
}
377-
}
378-
379-
impl<'a, T> Drop for InitializingSlice<'a, T> {
380-
#[cold] // will only be invoked on unwind
381-
fn drop(&mut self) {
382-
let initialized_slice = ptr::slice_from_raw_parts_mut(
383-
MaybeUninit::slice_as_mut_ptr(self.data),
384-
self.initialized_len,
385-
);
386-
// SAFETY:
387-
// * the pointer is valid because it was made from a mutable reference
388-
// * `initialized_len` counts the initialized elements as an invariant of this type,
389-
// so each of the pointed-to elements is initialized and may be dropped.
390-
unsafe {
391-
ptr::drop_in_place::<[T]>(initialized_slice);
392-
}
301+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
302+
unsafe impl CloneToUninit for crate::ffi::CStr {
303+
#[cfg_attr(debug_assertions, track_caller)]
304+
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
305+
// SAFETY: For now, CStr is just a #[repr(trasnsparent)] [c_char] with some invariants.
306+
// And we can cast [c_char] to [u8] on all supported platforms (see: to_bytes_with_nul).
307+
// The pointer metadata properly preserves the length (NUL included).
308+
// See: `cstr_metadata_is_length_with_nul` in tests.
309+
unsafe { self.to_bytes_with_nul().clone_to_uninit(dst as *mut [u8]) }
393310
}
394311
}
395312

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+
}

core/tests/clone.rs

+40
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use core::clone::CloneToUninit;
2+
use core::ffi::CStr;
23
use core::mem::MaybeUninit;
4+
use core::ptr;
35

46
#[test]
57
#[allow(suspicious_double_ref_op)]
@@ -81,3 +83,41 @@ fn test_clone_to_uninit_slice_drops_on_panic() {
8183
drop(a);
8284
assert_eq!(COUNTER.load(Relaxed), 0);
8385
}
86+
87+
#[test]
88+
fn test_clone_to_uninit_str() {
89+
let a = "hello";
90+
91+
let mut storage: MaybeUninit<[u8; 5]> = MaybeUninit::uninit();
92+
unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut str) };
93+
assert_eq!(a.as_bytes(), unsafe { storage.assume_init() }.as_slice());
94+
95+
let mut b: Box<str> = "world".into();
96+
assert_eq!(a.len(), b.len());
97+
assert_ne!(a, &*b);
98+
unsafe { a.clone_to_uninit(ptr::from_mut::<str>(&mut b)) };
99+
assert_eq!(a, &*b);
100+
}
101+
102+
#[test]
103+
fn test_clone_to_uninit_cstr() {
104+
let a = c"hello";
105+
106+
let mut storage: MaybeUninit<[u8; 6]> = MaybeUninit::uninit();
107+
unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut CStr) };
108+
assert_eq!(a.to_bytes_with_nul(), unsafe { storage.assume_init() }.as_slice());
109+
110+
let mut b: Box<CStr> = c"world".into();
111+
assert_eq!(a.count_bytes(), b.count_bytes());
112+
assert_ne!(a, &*b);
113+
unsafe { a.clone_to_uninit(ptr::from_mut::<CStr>(&mut b)) };
114+
assert_eq!(a, &*b);
115+
}
116+
117+
#[test]
118+
fn cstr_metadata_is_length_with_nul() {
119+
let s: &CStr = c"abcdef";
120+
let p: *const CStr = ptr::from_ref(s);
121+
let bytes: *const [u8] = p as *const [u8];
122+
assert_eq!(s.to_bytes_with_nul().len(), bytes.len());
123+
}

std/src/ffi/os_str.rs

+13
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
#[cfg(test)]
44
mod tests;
55

6+
use core::clone::CloneToUninit;
7+
68
use crate::borrow::{Borrow, Cow};
79
use crate::collections::TryReserveError;
810
use crate::hash::{Hash, Hasher};
911
use crate::ops::{self, Range};
12+
use crate::ptr::addr_of_mut;
1013
use crate::rc::Rc;
1114
use crate::str::FromStr;
1215
use crate::sync::Arc;
@@ -1261,6 +1264,16 @@ impl Clone for Box<OsStr> {
12611264
}
12621265
}
12631266

1267+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
1268+
unsafe impl CloneToUninit for OsStr {
1269+
#[inline]
1270+
#[cfg_attr(debug_assertions, track_caller)]
1271+
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
1272+
// SAFETY: we're just a wrapper around a platform-specific Slice
1273+
unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) }
1274+
}
1275+
}
1276+
12641277
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
12651278
impl From<OsString> for Arc<OsStr> {
12661279
/// Converts an [`OsString`] into an <code>[Arc]<[OsStr]></code> by moving the [`OsString`]

std/src/ffi/os_str/tests.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use super::*;
2+
use crate::mem::MaybeUninit;
3+
use crate::ptr;
24

35
#[test]
46
fn test_os_string_with_capacity() {
@@ -286,3 +288,18 @@ fn slice_surrogate_edge() {
286288
assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀");
287289
assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate);
288290
}
291+
292+
#[test]
293+
fn clone_to_uninit() {
294+
let a = OsStr::new("hello.txt");
295+
296+
let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<OsStr>(a)];
297+
unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()) as *mut OsStr) };
298+
assert_eq!(a.as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) });
299+
300+
let mut b: Box<OsStr> = OsStr::new("world.exe").into();
301+
assert_eq!(size_of_val::<OsStr>(a), size_of_val::<OsStr>(&b));
302+
assert_ne!(a, &*b);
303+
unsafe { a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b)) };
304+
assert_eq!(a, &*b);
305+
}

0 commit comments

Comments
 (0)