Skip to content

Commit ec201b8

Browse files
committed
Add core::clone::CloneToUninit.
This trait allows cloning DSTs, but is unsafe to implement and use because it writes to possibly-uninitialized memory which must be of the correct size, and must initialize that memory. It is only implemented for `T: Clone` and `[T] where T: Clone`, but additional implementations could be provided for specific `dyn Trait` or custom-DST types.
1 parent 4333551 commit ec201b8

File tree

3 files changed

+254
-0
lines changed

3 files changed

+254
-0
lines changed

library/core/src/clone.rs

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
3737
#![stable(feature = "rust1", since = "1.0.0")]
3838

39+
use crate::mem::{self, MaybeUninit};
40+
use crate::ptr;
41+
3942
/// A common trait for the ability to explicitly duplicate an object.
4043
///
4144
/// Differs from [`Copy`] in that [`Copy`] is implicit and an inexpensive bit-wise copy, while
@@ -204,6 +207,189 @@ pub struct AssertParamIsCopy<T: Copy + ?Sized> {
204207
_field: crate::marker::PhantomData<T>,
205208
}
206209

210+
/// A generalization of [`Clone`] to dynamically-sized types stored in arbitrary containers.
211+
///
212+
/// This trait is implemented for all types implementing [`Clone`], and also [slices](slice) of all
213+
/// such types. You may also implement this trait to enable cloning trait objects and custom DSTs
214+
/// (structures containing dynamically-sized fields).
215+
///
216+
/// # Safety
217+
///
218+
/// Implementations must ensure that when `.clone_to_uninit(dst)` returns normally rather than
219+
/// panicking, it always leaves `*dst` initialized as a valid value of type `Self`.
220+
///
221+
/// # See also
222+
///
223+
/// * [`Clone::clone_from`] is a safe function which may be used instead when `Self` is a [`Sized`]
224+
/// and the destination is already initialized; it may be able to reuse allocations owned by
225+
/// the destination.
226+
/// * [`ToOwned`], which allocates a new destination container.
227+
///
228+
/// [`ToOwned`]: ../../std/borrow/trait.ToOwned.html
229+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
230+
pub unsafe trait CloneToUninit {
231+
/// Performs copy-assignment from `self` to `dst`.
232+
///
233+
/// This is analogous to to `std::ptr::write(dst, self.clone())`,
234+
/// except that `self` may be a dynamically-sized type ([`!Sized`](Sized)).
235+
///
236+
/// Before this function is called, `dst` may point to uninitialized memory.
237+
/// After this function is called, `dst` will point to initialized memory; it will be
238+
/// sound to create a `&Self` reference from the pointer.
239+
///
240+
/// # Safety
241+
///
242+
/// Behavior is undefined if any of the following conditions are violated:
243+
///
244+
/// * `dst` must be [valid] for writes.
245+
/// * `dst` must be properly aligned.
246+
/// * `dst` must have the same [pointer metadata] (slice length or `dyn` vtable) as `self`.
247+
///
248+
/// [valid]: ptr#safety
249+
/// [pointer metadata]: crate::ptr::metadata()
250+
///
251+
/// # Panics
252+
///
253+
/// This function may panic. (For example, it might panic if memory allocation for a clone
254+
/// of a value owned by `self` fails.)
255+
/// If the call panics, then `*dst` should be treated as uninitialized memory; it must not be
256+
/// read or dropped, because even if it was previously valid, it may have been partially
257+
/// overwritten.
258+
///
259+
/// The caller may also need to take care to deallocate the allocation pointed to by `dst`,
260+
/// if applicable, to avoid a memory leak, and may need to take other precautions to ensure
261+
/// soundness in the presence of unwinding.
262+
///
263+
/// Implementors should avoid leaking values by, upon unwinding, dropping all component values
264+
/// that might have already been created. (For example, if a `[Foo]` of length 3 is being
265+
/// cloned, and the second of the three calls to `Foo::clone()` unwinds, then the first `Foo`
266+
/// cloned should be dropped.)
267+
unsafe fn clone_to_uninit(&self, dst: *mut Self);
268+
}
269+
270+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
271+
unsafe impl<T: Clone> CloneToUninit for T {
272+
default unsafe fn clone_to_uninit(&self, dst: *mut Self) {
273+
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
274+
// ptr::write().
275+
unsafe {
276+
// We hope the optimizer will figure out to create the cloned value in-place,
277+
// skipping ever storing it on the stack and the copy to the destination.
278+
ptr::write(dst, self.clone());
279+
}
280+
}
281+
}
282+
283+
// Specialized implementation for types that are [`Copy`], not just [`Clone`],
284+
// and can therefore be copied bitwise.
285+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
286+
unsafe impl<T: Copy> CloneToUninit for T {
287+
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
288+
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
289+
// ptr::copy_nonoverlapping().
290+
unsafe {
291+
ptr::copy_nonoverlapping(self, dst, 1);
292+
}
293+
}
294+
}
295+
296+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
297+
unsafe impl<T: Clone> CloneToUninit for [T] {
298+
#[cfg_attr(debug_assertions, track_caller)]
299+
default unsafe fn clone_to_uninit(&self, dst: *mut Self) {
300+
let len = self.len();
301+
// This is the most likely mistake to make, so check it as a debug assertion.
302+
debug_assert_eq!(
303+
len,
304+
dst.len(),
305+
"clone_to_uninit() source and destination must have equal lengths",
306+
);
307+
308+
// SAFETY: The produced `&mut` is valid because:
309+
// * The caller is obligated to provide a pointer which is valid for writes.
310+
// * All bytes pointed to are in MaybeUninit, so we don't care about the memory's
311+
// initialization status.
312+
let uninit_ref = unsafe { &mut *(dst as *mut [MaybeUninit<T>]) };
313+
314+
// Copy the elements
315+
let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref);
316+
for element_ref in self.iter() {
317+
// If the clone() panics, `initializing` will take care of the cleanup.
318+
initializing.push(element_ref.clone());
319+
}
320+
// If we reach here, then the entire slice is initialized, and we've satisfied our
321+
// responsibilities to the caller. Disarm the cleanup guard by forgetting it.
322+
mem::forget(initializing);
323+
}
324+
}
325+
326+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
327+
unsafe impl<T: Copy> CloneToUninit for [T] {
328+
#[cfg_attr(debug_assertions, track_caller)]
329+
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
330+
let len = self.len();
331+
// This is the most likely mistake to make, so check it as a debug assertion.
332+
debug_assert_eq!(
333+
len,
334+
dst.len(),
335+
"clone_to_uninit() source and destination must have equal lengths",
336+
);
337+
338+
// SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
339+
// ptr::copy_nonoverlapping().
340+
unsafe {
341+
ptr::copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), len);
342+
}
343+
}
344+
}
345+
346+
/// Ownership of a collection of values stored in a non-owned `[MaybeUninit<T>]`, some of which
347+
/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation.
348+
/// Its responsibility is to provide cleanup on unwind by dropping the values that *are*
349+
/// initialized, unless disarmed by forgetting.
350+
///
351+
/// This is a helper for `impl<T: Clone> CloneToUninit for [T]`.
352+
struct InitializingSlice<'a, T> {
353+
data: &'a mut [MaybeUninit<T>],
354+
/// Number of elements of `*self.data` that are initialized.
355+
initialized_len: usize,
356+
}
357+
358+
impl<'a, T> InitializingSlice<'a, T> {
359+
#[inline]
360+
fn from_fully_uninit(data: &'a mut [MaybeUninit<T>]) -> Self {
361+
Self { data, initialized_len: 0 }
362+
}
363+
364+
/// Push a value onto the end of the initialized part of the slice.
365+
///
366+
/// # Panics
367+
///
368+
/// Panics if the slice is already fully initialized.
369+
#[inline]
370+
fn push(&mut self, value: T) {
371+
MaybeUninit::write(&mut self.data[self.initialized_len], value);
372+
self.initialized_len += 1;
373+
}
374+
}
375+
376+
impl<'a, T> Drop for InitializingSlice<'a, T> {
377+
#[cold] // will only be invoked on unwind
378+
fn drop(&mut self) {
379+
let initialized_slice = ptr::slice_from_raw_parts_mut(
380+
MaybeUninit::slice_as_mut_ptr(self.data),
381+
self.initialized_len,
382+
);
383+
// SAFETY:
384+
// * the pointer is valid because it was made from a mutable reference
385+
// * `initialized_len` counts the initialized elements as an invariant of this type,
386+
// so each of the pointed-to elements is initialized and may be dropped.
387+
unsafe {
388+
ptr::drop_in_place::<[T]>(initialized_slice);
389+
}
390+
}
391+
}
392+
207393
/// Implementations of `Clone` for primitive types.
208394
///
209395
/// Implementations that cannot be described in Rust

library/core/tests/clone.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use core::clone::CloneToUninit;
2+
use core::mem::MaybeUninit;
3+
14
#[test]
25
#[allow(suspicious_double_ref_op)]
36
fn test_borrowed_clone() {
@@ -14,3 +17,66 @@ fn test_clone_from() {
1417
b.clone_from(&a);
1518
assert_eq!(*b, 5);
1619
}
20+
21+
#[test]
22+
fn test_clone_to_uninit_slice_success() {
23+
// Using `String`s to exercise allocation and Drop of the individual elements;
24+
// if something is aliased or double-freed, at least Miri will catch that.
25+
let a: [String; 3] = ["a", "b", "c"].map(String::from);
26+
27+
let mut storage: MaybeUninit<[String; 3]> = MaybeUninit::uninit();
28+
let b: [String; 3] = unsafe {
29+
a[..].clone_to_uninit(storage.as_mut_ptr() as *mut [String]);
30+
storage.assume_init()
31+
};
32+
33+
assert_eq!(a, b);
34+
}
35+
36+
#[test]
37+
#[cfg(panic = "unwind")]
38+
fn test_clone_to_uninit_slice_drops_on_panic() {
39+
use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
40+
41+
/// A static counter is OK to use as long as _this one test_ isn't run several times in
42+
/// multiple threads.
43+
static COUNTER: AtomicUsize = AtomicUsize::new(0);
44+
/// Counts how many instances are live, and panics if a fifth one is created
45+
struct CountsDropsAndPanics {}
46+
impl CountsDropsAndPanics {
47+
fn new() -> Self {
48+
COUNTER.fetch_add(1, Relaxed);
49+
Self {}
50+
}
51+
}
52+
impl Clone for CountsDropsAndPanics {
53+
fn clone(&self) -> Self {
54+
if COUNTER.load(Relaxed) == 4 { panic!("intentional panic") } else { Self::new() }
55+
}
56+
}
57+
impl Drop for CountsDropsAndPanics {
58+
fn drop(&mut self) {
59+
COUNTER.fetch_sub(1, Relaxed);
60+
}
61+
}
62+
63+
let a: [CountsDropsAndPanics; 3] = core::array::from_fn(|_| CountsDropsAndPanics::new());
64+
assert_eq!(COUNTER.load(Relaxed), 3);
65+
66+
let panic_payload = std::panic::catch_unwind(|| {
67+
let mut storage: MaybeUninit<[CountsDropsAndPanics; 3]> = MaybeUninit::uninit();
68+
// This should panic halfway through
69+
unsafe {
70+
a[..].clone_to_uninit(storage.as_mut_ptr() as *mut [CountsDropsAndPanics]);
71+
}
72+
})
73+
.unwrap_err();
74+
assert_eq!(panic_payload.downcast().unwrap(), Box::new("intentional panic"));
75+
76+
// Check for lack of leak, which is what this test is looking for
77+
assert_eq!(COUNTER.load(Relaxed), 3, "leaked during clone!");
78+
79+
// Might as well exercise the rest of the drops
80+
drop(a);
81+
assert_eq!(COUNTER.load(Relaxed), 0);
82+
}

library/core/tests/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#![feature(async_iterator)]
99
#![feature(bigint_helper_methods)]
1010
#![feature(cell_update)]
11+
#![feature(clone_to_uninit)]
1112
#![feature(const_align_offset)]
1213
#![feature(const_align_of_val_raw)]
1314
#![feature(const_black_box)]
@@ -54,6 +55,7 @@
5455
#![feature(slice_split_once)]
5556
#![feature(split_as_slice)]
5657
#![feature(maybe_uninit_fill)]
58+
#![feature(maybe_uninit_slice)]
5759
#![feature(maybe_uninit_uninit_array)]
5860
#![feature(maybe_uninit_write_slice)]
5961
#![feature(maybe_uninit_uninit_array_transpose)]

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy