Skip to content

Commit 17dcadd

Browse files
Rollup merge of #133003 - zachs18:clonetouninit-dyn-compat-u8, r=dtolnay
Make `CloneToUninit` dyn-compatible Make `CloneToUninit` dyn-compatible, by making `clone_to_uninit`'s `dst` parameter `*mut u8` instead of `*mut Self`, so the method does not reference `Self` except in the `self` parameter and is thus dispatchable from a trait object. This allows, among other things, adding `CloneToUninit` as a supertrait bound for `trait Foo` to allow cloning `dyn Foo` in some containers. Currently, this means that `Rc::make_mut` and `Arc::make_mut` can work with `dyn Foo` where `trait Foo: CloneToUninit`. <details><summary>Example</summary> ```rs #![feature(clone_to_uninit)] use std::clone::CloneToUninit; use std::rc::Rc; use std::fmt::Debug; use std::borrow::BorrowMut; trait Foo: BorrowMut<u32> + CloneToUninit + Debug {} impl<T: BorrowMut<u32> + CloneToUninit + Debug> Foo for T {} fn main() { let foo: Rc<dyn Foo> = Rc::new(42_u32); let mut bar = foo.clone(); *Rc::make_mut(&mut bar).borrow_mut() = 37; dbg!(foo, bar); // 42, 37 } ``` </details> Eventually, `Box::<T>::clone` is planned to be converted to use `T::clone_to_uninit`, which when combined with this change, will allow cloning `Box<dyn Foo>` where `trait Foo: CloneToUninit` without any additional `unsafe` code for the author of `trait Foo`.[^1] This PR should have no stable side-effects, as `CloneToUninit` is unstable so cannot be mentioned on stable, and `CloneToUninit` is not used as a supertrait anywhere in the stdlib. This change removes some length checks that could only fail if library UB was already hit (e.g. calling `<[T]>::clone_to_uninit` with a too-small-length `dst` is library UB and was previously detected[^2]; since `dst` does not have a length anymore, this now cannot be detected[^3]). r? libs-api ----- I chose to make the parameter `*mut u8` instead of `*mut ()` because that might make it simpler to pass the result of `alloc` to `clone_to_uninit`, but `*mut ()` would also make sense, and any `*mut ConcreteType` would *work*. The original motivation for [using specifically `*mut ()`](#116113 (comment)) appears to be `std::ptr::from_raw_parts_mut`, but that now [takes `*mut impl Thin`](https://doc.rust-lang.org/nightly/std/ptr/fn.from_raw_parts.html) instead of `*mut ()`. I have another branch where the parameter is `*mut ()`, if that is preferred. It *could* also take something like `&mut [MaybeUninit<u8>]` to be dyn-compatible but still allow size-checking and in some cases safe writing, but this is already an `unsafe` API where misuse is UB, so I'm not sure how many guardrails it's worth adding here, and `&mut [MaybeUninit<u8>]` might be overly cumbersome to construct for callers compared to `*mut u8` [^1]: Note that `impl<T: CloneToUninit + ?Sized> Clone for Box` must be added before or at the same time as when `CloneToUninit` becomes stable, due to `Box` being `#[fundamental]`, as if there is any stable gap between the stabilization of `CloneToUninit` and `impl<T: CloneToUninit + ?Sized> Clone for Box`, then users could implement both `CloneToUninit for dyn LocalTrait` and separately `Clone for Box<dyn LocalTrait>` during that gap, and be broken by the introduction of `impl<T: CloneToUninit + ?Sized> Clone for Box`. [^2]: Using a `debug_assert_eq` in [`core::clone::uninit::CopySpec::clone_slice`](https://doc.rust-lang.org/nightly/src/core/clone/uninit.rs.html#28). [^3]: This PR just uses [the metadata (length) from `self`](https://github.com/zachs18/rust/blob/e0c1c8bc5058cd3f8831b235c5963ab89840b33b/library/core/src/clone.rs#L286) to construct the `*mut [T]` to pass to `CopySpec::clone_slice` in `<[T]>::clone_to_uninit`.
2 parents aa18946 + 6166b0c commit 17dcadd

12 files changed

Lines changed: 45 additions & 44 deletions

File tree

‎library/alloc/src/boxed.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1735,7 +1735,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for Box<T, A> {
17351735
// Pre-allocate memory to allow writing the cloned value directly.
17361736
letmut boxed = Self::new_uninit_in(self.1.clone());
17371737
unsafe{
1738-
(**self).clone_to_uninit(boxed.as_mut_ptr());
1738+
(**self).clone_to_uninit(boxed.as_mut_ptr().cast());
17391739
boxed.assume_init()
17401740
}
17411741
}

‎library/alloc/src/rc.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1876,7 +1876,7 @@ impl<T: ?Sized + CloneToUninit, A: Allocator + Clone> Rc<T, A> {
18761876
// Initialize with clone of this.
18771877
let initialized_clone = unsafe{
18781878
// Clone. If the clone panics, `in_progress` will be dropped and clean up.
1879-
this_data_ref.clone_to_uninit(in_progress.data_ptr());
1879+
this_data_ref.clone_to_uninit(in_progress.data_ptr().cast());
18801880
// Cast type of pointer, now that it is initialized.
18811881
in_progress.into_rc()
18821882
};

‎library/alloc/src/sync.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2272,7 +2272,7 @@ impl<T: ?Sized + CloneToUninit, A: Allocator + Clone> Arc<T, A> {
22722272

22732273
let initialized_clone = unsafe{
22742274
// Clone. If the clone panics, `in_progress` will be dropped and clean up.
2275-
this_data_ref.clone_to_uninit(in_progress.data_ptr());
2275+
this_data_ref.clone_to_uninit(in_progress.data_ptr().cast());
22762276
// Cast type of pointer, now that it is initialized.
22772277
in_progress.into_arc()
22782278
};

‎library/core/src/clone.rs‎

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -232,20 +232,20 @@ pub struct AssertParamIsCopy<T: Copy + ?Sized> {
232232
pubunsafetraitCloneToUninit{
233233
/// Performs copy-assignment from `self` to `dst`.
234234
///
235-
/// This is analogous to `std::ptr::write(dst, self.clone())`,
235+
/// This is analogous to `std::ptr::write(dst.cast(), self.clone())`,
236236
/// except that `self` may be a dynamically-sized type ([`!Sized`](Sized)).
237237
///
238238
/// Before this function is called, `dst` may point to uninitialized memory.
239239
/// After this function is called, `dst` will point to initialized memory; it will be
240-
/// sound to create a `&Self` reference from the pointer.
240+
/// sound to create a `&Self` reference from the pointer with the [pointer metadata]
241+
/// from `self`.
241242
///
242243
/// # Safety
243244
///
244245
/// Behavior is undefined if any of the following conditions are violated:
245246
///
246-
/// * `dst` must be [valid] for writes.
247-
/// * `dst` must be properly aligned.
248-
/// * `dst` must have the same [pointer metadata] (slice length or `dyn` vtable) as `self`.
247+
/// * `dst` must be [valid] for writes for `std::mem::size_of_val(self)` bytes.
248+
/// * `dst` must be properly aligned to `std::mem::align_of_val(self)`.
249249
///
250250
/// [valid]: crate::ptr#safety
251251
/// [pointer metadata]: crate::ptr::metadata()
@@ -266,23 +266,24 @@ pub unsafe trait CloneToUninit {
266266
/// that might have already been created. (For example, if a `[Foo]` of length 3 is being
267267
/// cloned, and the second of the three calls to `Foo::clone()` unwinds, then the first `Foo`
268268
/// cloned should be dropped.)
269-
unsafefnclone_to_uninit(&self,dst:*mutSelf);
269+
unsafefnclone_to_uninit(&self,dst:*mutu8);
270270
}
271271

272272
#[unstable(feature = "clone_to_uninit", issue = "126799")]
273273
unsafeimpl<T:Clone>CloneToUninitforT{
274274
#[inline]
275-
unsafefnclone_to_uninit(&self,dst:*mutSelf){
275+
unsafefnclone_to_uninit(&self,dst:*mutu8){
276276
// SAFETY: we're calling a specialization with the same contract
277-
unsafe{ <Tasself::uninit::CopySpec>::clone_one(self, dst)}
277+
unsafe{ <Tasself::uninit::CopySpec>::clone_one(self, dst.cast::<T>())}
278278
}
279279
}
280280

281281
#[unstable(feature = "clone_to_uninit", issue = "126799")]
282282
unsafeimpl<T:Clone>CloneToUninitfor[T]{
283283
#[inline]
284284
#[cfg_attr(debug_assertions, track_caller)]
285-
unsafefnclone_to_uninit(&self,dst:*mutSelf){
285+
unsafefnclone_to_uninit(&self,dst:*mutu8){
286+
let dst:*mut[T] = dst.with_metadata_of(self);
286287
// SAFETY: we're calling a specialization with the same contract
287288
unsafe{ <Tasself::uninit::CopySpec>::clone_slice(self, dst)}
288289
}
@@ -292,21 +293,21 @@ unsafe impl<T: Clone> CloneToUninit for [T] {
292293
unsafeimplCloneToUninitforstr{
293294
#[inline]
294295
#[cfg_attr(debug_assertions, track_caller)]
295-
unsafefnclone_to_uninit(&self,dst:*mutSelf){
296+
unsafefnclone_to_uninit(&self,dst:*mutu8){
296297
// SAFETY: str is just a [u8] with UTF-8 invariant
297-
unsafe{self.as_bytes().clone_to_uninit(dstas*mut[u8])}
298+
unsafe{self.as_bytes().clone_to_uninit(dst)}
298299
}
299300
}
300301

301302
#[unstable(feature = "clone_to_uninit", issue = "126799")]
302303
unsafeimplCloneToUninitforcrate::ffi::CStr{
303304
#[cfg_attr(debug_assertions, track_caller)]
304-
unsafefnclone_to_uninit(&self,dst:*mutSelf){
305+
unsafefnclone_to_uninit(&self,dst:*mutu8){
305306
// SAFETY: For now, CStr is just a #[repr(trasnsparent)] [c_char] with some invariants.
306307
// 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+
// The pointer metadata properly preserves the length (so NUL is also copied).
308309
// See: `cstr_metadata_is_length_with_nul` in tests.
309-
unsafe{self.to_bytes_with_nul().clone_to_uninit(dstas*mut[u8])}
310+
unsafe{self.to_bytes_with_nul().clone_to_uninit(dst)}
310311
}
311312
}
312313

‎library/core/tests/clone.rs‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ fn test_clone_to_uninit_slice_success() {
2828

2929
letmut storage:MaybeUninit<[String;3]> = MaybeUninit::uninit();
3030
let b:[String;3] = unsafe{
31-
a[..].clone_to_uninit(storage.as_mut_ptr()as*mut[String]);
31+
a[..].clone_to_uninit(storage.as_mut_ptr().cast());
3232
storage.assume_init()
3333
};
3434

@@ -70,7 +70,7 @@ fn test_clone_to_uninit_slice_drops_on_panic() {
7070
letmut storage:MaybeUninit<[CountsDropsAndPanics;3]> = MaybeUninit::uninit();
7171
// This should panic halfway through
7272
unsafe{
73-
a[..].clone_to_uninit(storage.as_mut_ptr()as*mut[CountsDropsAndPanics]);
73+
a[..].clone_to_uninit(storage.as_mut_ptr().cast());
7474
}
7575
})
7676
.unwrap_err();
@@ -89,13 +89,13 @@ fn test_clone_to_uninit_str() {
8989
let a = "hello";
9090

9191
letmut storage:MaybeUninit<[u8;5]> = MaybeUninit::uninit();
92-
unsafe{ a.clone_to_uninit(storage.as_mut_ptr()as*mut[u8]as*mutstr)};
92+
unsafe{ a.clone_to_uninit(storage.as_mut_ptr().cast())};
9393
assert_eq!(a.as_bytes(),unsafe{ storage.assume_init()}.as_slice());
9494

9595
letmut b:Box<str> = "world".into();
9696
assert_eq!(a.len(), b.len());
9797
assert_ne!(a,&*b);
98-
unsafe{ a.clone_to_uninit(ptr::from_mut::<str>(&mut b))};
98+
unsafe{ a.clone_to_uninit(ptr::from_mut::<str>(&mut b).cast())};
9999
assert_eq!(a,&*b);
100100
}
101101

@@ -104,13 +104,13 @@ fn test_clone_to_uninit_cstr() {
104104
let a = c"hello";
105105

106106
letmut storage:MaybeUninit<[u8;6]> = MaybeUninit::uninit();
107-
unsafe{ a.clone_to_uninit(storage.as_mut_ptr()as*mut[u8]as*mutCStr)};
107+
unsafe{ a.clone_to_uninit(storage.as_mut_ptr().cast())};
108108
assert_eq!(a.to_bytes_with_nul(),unsafe{ storage.assume_init()}.as_slice());
109109

110110
letmut b:Box<CStr> = c"world".into();
111111
assert_eq!(a.count_bytes(), b.count_bytes());
112112
assert_ne!(a,&*b);
113-
unsafe{ a.clone_to_uninit(ptr::from_mut::<CStr>(&mut b))};
113+
unsafe{ a.clone_to_uninit(ptr::from_mut::<CStr>(&mut b).cast())};
114114
assert_eq!(a,&*b);
115115
}
116116

‎library/std/src/ffi/os_str.rs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ impl crate::sealed::Sealed for OsString {}
112112
/// [conversions]: super#conversions
113113
#[cfg_attr(not(test), rustc_diagnostic_item = "OsStr")]
114114
#[stable(feature = "rust1", since = "1.0.0")]
115-
// `OsStr::from_inner` current implementation relies
115+
// `OsStr::from_inner` and `impl CloneToUninit for OsStr` current implementation relies
116116
// on `OsStr` being layout-compatible with `Slice`.
117117
// However, `OsStr` layout is considered an implementation detail and must not be relied upon.
118118
#[repr(transparent)]
@@ -1278,9 +1278,9 @@ impl Clone for Box<OsStr> {
12781278
unsafeimplCloneToUninitforOsStr{
12791279
#[inline]
12801280
#[cfg_attr(debug_assertions, track_caller)]
1281-
unsafefnclone_to_uninit(&self,dst:*mutSelf){
1282-
// SAFETY: we're just a wrapper around a platform-specific Slice
1283-
unsafe{self.inner.clone_to_uninit(&rawmut(*dst).inner)}
1281+
unsafefnclone_to_uninit(&self,dst:*mutu8){
1282+
// SAFETY: we're just a transparent wrapper around a platform-specific Slice
1283+
unsafe{self.inner.clone_to_uninit(dst)}
12841284
}
12851285
}
12861286

‎library/std/src/ffi/os_str/tests.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,12 @@ fn clone_to_uninit() {
294294
let a = OsStr::new("hello.txt");
295295

296296
letmut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<OsStr>(a)];
297-
unsafe{ a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice())as*mutOsStr)};
297+
unsafe{ a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast())};
298298
assert_eq!(a.as_encoded_bytes(),unsafe{MaybeUninit::slice_assume_init_ref(&storage)});
299299

300300
letmut b:Box<OsStr> = OsStr::new("world.exe").into();
301301
assert_eq!(size_of_val::<OsStr>(a), size_of_val::<OsStr>(&b));
302302
assert_ne!(a,&*b);
303-
unsafe{ a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b))};
303+
unsafe{ a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b).cast())};
304304
assert_eq!(a,&*b);
305305
}

‎library/std/src/path.rs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,7 +2128,7 @@ impl AsRef<OsStr> for PathBuf {
21282128
/// ```
21292129
#[cfg_attr(not(test), rustc_diagnostic_item = "Path")]
21302130
#[stable(feature = "rust1", since = "1.0.0")]
2131-
// `Path::new` current implementation relies
2131+
// `Path::new` and `impl CloneToUninit for Path` current implementation relies
21322132
// on `Path` being layout-compatible with `OsStr`.
21332133
// However, `Path` layout is considered an implementation detail and must not be relied upon.
21342134
#[repr(transparent)]
@@ -3170,9 +3170,9 @@ impl Path {
31703170
unsafeimplCloneToUninitforPath{
31713171
#[inline]
31723172
#[cfg_attr(debug_assertions, track_caller)]
3173-
unsafefnclone_to_uninit(&self,dst:*mutSelf){
3174-
// SAFETY: Path is just a wrapper around OsStr
3175-
unsafe{self.inner.clone_to_uninit(&rawmut(*dst).inner)}
3173+
unsafefnclone_to_uninit(&self,dst:*mutu8){
3174+
// SAFETY: Path is just a transparent wrapper around OsStr
3175+
unsafe{self.inner.clone_to_uninit(dst)}
31763176
}
31773177
}
31783178

‎library/std/src/path/tests.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,14 +2068,14 @@ fn clone_to_uninit() {
20682068
let a = Path::new("hello.txt");
20692069

20702070
letmut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<Path>(a)];
2071-
unsafe{ a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice())as*mutPath)};
2071+
unsafe{ a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast())};
20722072
assert_eq!(a.as_os_str().as_encoded_bytes(),unsafe{
20732073
MaybeUninit::slice_assume_init_ref(&storage)
20742074
});
20752075

20762076
letmut b:Box<Path> = Path::new("world.exe").into();
20772077
assert_eq!(size_of_val::<Path>(a), size_of_val::<Path>(&b));
20782078
assert_ne!(a,&*b);
2079-
unsafe{ a.clone_to_uninit(ptr::from_mut::<Path>(&mut b))};
2079+
unsafe{ a.clone_to_uninit(ptr::from_mut::<Path>(&mut b).cast())};
20802080
assert_eq!(a,&*b);
20812081
}

‎library/std/src/sys/os_str/bytes.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,8 @@ impl Slice {
352352
unsafeimplCloneToUninitforSlice{
353353
#[inline]
354354
#[cfg_attr(debug_assertions, track_caller)]
355-
unsafefnclone_to_uninit(&self,dst:*mutSelf){
356-
// SAFETY: we're just a wrapper around [u8]
357-
unsafe{self.inner.clone_to_uninit(&rawmut(*dst).inner)}
355+
unsafefnclone_to_uninit(&self,dst:*mutu8){
356+
// SAFETY: we're just a transparent wrapper around [u8]
357+
unsafe{self.inner.clone_to_uninit(dst)}
358358
}
359359
}

0 commit comments

Comments
 (0)