Content-Length: 831895 | pFad | https://github.com/model-checking/verify-rust-std/commit/a4ca461939ee20eb2afcf0fc2bb55bd18752a482

BD Add `core::clone::CloneToUninit`. · model-checking/verify-rust-std@a4ca461 · GitHub
Skip to content

Commit a4ca461

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 dbaf524 commit a4ca461

File tree

3 files changed

+254
-0
lines changed

3 files changed

+254
-0
lines changed

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
//github.com/ A common trait for the ability to explicitly duplicate an object.
4043
//github.com/
4144
//github.com/ 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+
//github.com/ A generalization of [`Clone`] to dynamically-sized types stored in arbitrary containers.
211+
//github.com/
212+
//github.com/ This trait is implemented for all types implementing [`Clone`], and also [slices](slice) of all
213+
//github.com/ such types. You may also implement this trait to enable cloning trait objects and custom DSTs
214+
//github.com/ (structures containing dynamically-sized fields).
215+
//github.com/
216+
//github.com/ # Safety
217+
//github.com/
218+
//github.com/ Implementations must ensure that when `.clone_to_uninit(dst)` returns normally rather than
219+
//github.com/ panicking, it always leaves `*dst` initialized as a valid value of type `Self`.
220+
//github.com/
221+
//github.com/ # See also
222+
//github.com/
223+
//github.com/ * [`Clone::clone_from`] is a safe function which may be used instead when `Self` is a [`Sized`]
224+
//github.com/ and the destination is already initialized; it may be able to reuse allocations owned by
225+
//github.com/ the destination.
226+
//github.com/ * [`ToOwned`], which allocates a new destination container.
227+
//github.com/
228+
//github.com/ [`ToOwned`]: ../../std/borrow/trait.ToOwned.html
229+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
230+
pub unsafe trait CloneToUninit {
231+
//github.com/ Performs copy-assignment from `self` to `dst`.
232+
//github.com/
233+
//github.com/ This is analogous to to `std::ptr::write(dst, self.clone())`,
234+
//github.com/ except that `self` may be a dynamically-sized type ([`!Sized`](Sized)).
235+
//github.com/
236+
//github.com/ Before this function is called, `dst` may point to uninitialized memory.
237+
//github.com/ After this function is called, `dst` will point to initialized memory; it will be
238+
//github.com/ sound to create a `&Self` reference from the pointer.
239+
//github.com/
240+
//github.com/ # Safety
241+
//github.com/
242+
//github.com/ Behavior is undefined if any of the following conditions are violated:
243+
//github.com/
244+
//github.com/ * `dst` must be [valid] for writes.
245+
//github.com/ * `dst` must be properly aligned.
246+
//github.com/ * `dst` must have the same [pointer metadata] (slice length or `dyn` vtable) as `self`.
247+
//github.com/
248+
//github.com/ [valid]: ptr#safety
249+
//github.com/ [pointer metadata]: crate::ptr::metadata()
250+
//github.com/
251+
//github.com/ # Panics
252+
//github.com/
253+
//github.com/ This function may panic. (For example, it might panic if memory allocation for a clone
254+
//github.com/ of a value owned by `self` fails.)
255+
//github.com/ If the call panics, then `*dst` should be treated as uninitialized memory; it must not be
256+
//github.com/ read or dropped, because even if it was previously valid, it may have been partially
257+
//github.com/ overwritten.
258+
//github.com/
259+
//github.com/ The caller may also need to take care to deallocate the allocation pointed to by `dst`,
260+
//github.com/ if applicable, to avoid a memory leak, and may need to take other precautions to ensure
261+
//github.com/ soundness in the presence of unwinding.
262+
//github.com/
263+
//github.com/ Implementors should avoid leaking values by, upon unwinding, dropping all component values
264+
//github.com/ that might have already been created. (For example, if a `[Foo]` of length 3 is being
265+
//github.com/ cloned, and the second of the three calls to `Foo::clone()` unwinds, then the first `Foo`
266+
//github.com/ 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+
//github.com/ Ownership of a collection of values stored in a non-owned `[MaybeUninit<T>]`, some of which
347+
//github.com/ are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation.
348+
//github.com/ Its responsibility is to provide cleanup on unwind by dropping the values that *are*
349+
//github.com/ initialized, unless disarmed by forgetting.
350+
//github.com/
351+
//github.com/ This is a helper for `impl<T: Clone> CloneToUninit for [T]`.
352+
struct InitializingSlice<'a, T> {
353+
data: &'a mut [MaybeUninit<T>],
354+
//github.com/ 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+
//github.com/ Push a value onto the end of the initialized part of the slice.
365+
//github.com/
366+
//github.com/ # Panics
367+
//github.com/
368+
//github.com/ 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
//github.com/ Implementations of `Clone` for primitive types.
208394
//github.com/
209395
//github.com/ Implementations that cannot be described in Rust

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+
//github.com/ A static counter is OK to use as long as _this one test_ isn't run several times in
42+
//github.com/ multiple threads.
43+
static COUNTER: AtomicUsize = AtomicUsize::new(0);
44+
//github.com/ 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+
}

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)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/model-checking/verify-rust-std/commit/a4ca461939ee20eb2afcf0fc2bb55bd18752a482

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy