From 9e497b6071efce3cf22296576f71b7296b0817cc Mon Sep 17 00:00:00 2001 From: Zachary S Date: Fri, 15 Nov 2024 00:47:22 -0600 Subject: [PATCH] Add Box::(try_)clone_from_ref(_in) --- library/alloc/src/boxed.rs | 123 +++++++++++++++++- library/std/src/lib.rs | 1 + tests/ui/privacy/suggest-box-new.stderr | 6 +- .../suggestions/multi-suggestion.ascii.stderr | 4 +- .../multi-suggestion.unicode.stderr | 4 +- 5 files changed, 130 insertions(+), 8 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 2b767ffe02be..f714a87c1868 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -184,7 +184,6 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::borrow::{Borrow, BorrowMut}; -#[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; use core::cmp::Ordering; use core::error::{self, Error}; @@ -733,6 +732,128 @@ impl Box { } } +impl Box { + /// Allocates memory on the heap then clones `src` into it. + /// + /// This doesn't actually allocate if `src` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// + /// let hello: Box = Box::clone_from_ref("hello"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + #[must_use] + #[inline] + pub fn clone_from_ref(src: &T) -> Box { + Box::clone_from_ref_in(src, Global) + } + + /// Allocates memory on the heap then clones `src` into it, returning an error if allocation fails. + /// + /// This doesn't actually allocate if `src` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// + /// let hello: Box = Box::try_clone_from_ref("hello")?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + #[must_use] + #[inline] + pub fn try_clone_from_ref(src: &T) -> Result, AllocError> { + Box::try_clone_from_ref_in(src, Global) + } +} + +impl Box { + /// Allocates memory in the given allocator then clones `src` into it. + /// + /// This doesn't actually allocate if `src` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// let hello: Box = Box::clone_from_ref_in("hello", System); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + #[must_use] + #[inline] + pub fn clone_from_ref_in(src: &T, alloc: A) -> Box { + let layout = Layout::for_value::(src); + match Box::try_clone_from_ref_in(src, alloc) { + Ok(bx) => bx, + Err(_) => handle_alloc_error(layout), + } + } + + /// Allocates memory in the given allocator then clones `src` into it, returning an error if allocation fails. + /// + /// This doesn't actually allocate if `src` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// let hello: Box = Box::try_clone_from_ref_in("hello", System)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + #[must_use] + #[inline] + pub fn try_clone_from_ref_in(src: &T, alloc: A) -> Result, AllocError> { + struct DeallocDropGuard<'a, A: Allocator>(Layout, &'a A, NonNull); + impl<'a, A: Allocator> Drop for DeallocDropGuard<'a, A> { + fn drop(&mut self) { + let &mut DeallocDropGuard(layout, alloc, ptr) = self; + // Safety: `ptr` was allocated by `*alloc` with layout `layout` + unsafe { + alloc.deallocate(ptr, layout); + } + } + } + let layout = Layout::for_value::(src); + let (ptr, guard) = if layout.size() == 0 { + (layout.dangling(), None) + } else { + // Safety: layout is non-zero-sized + let ptr = alloc.allocate(layout)?.cast(); + (ptr, Some(DeallocDropGuard(layout, &alloc, ptr))) + }; + let ptr = ptr.as_ptr(); + // Safety: `*ptr` is newly allocated, correctly aligned to `align_of_val(src)`, + // and is valid for writes for `size_of_val(src)`. + // If this panics, then `guard` will deallocate for us (if allocation occuured) + unsafe { + ::clone_to_uninit(src, ptr); + } + // Defuse the deallocate guard + core::mem::forget(guard); + // Safety: We just initialized `*ptr` as a clone of `src` + Ok(unsafe { Box::from_raw_in(ptr.with_metadata_of(src), alloc) }) + } +} + impl Box<[T]> { /// Constructs a new boxed slice with uninitialized contents. /// diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 07618550a9cb..32f166e112e9 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -374,6 +374,7 @@ // tidy-alphabetical-start #![feature(alloc_layout_extra)] #![feature(allocator_api)] +#![feature(clone_from_ref)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] #![feature(slice_concat_trait)] diff --git a/tests/ui/privacy/suggest-box-new.stderr b/tests/ui/privacy/suggest-box-new.stderr index 7df293e1e1d9..566f31fc305e 100644 --- a/tests/ui/privacy/suggest-box-new.stderr +++ b/tests/ui/privacy/suggest-box-new.stderr @@ -63,7 +63,7 @@ LL - x: (), LL - })), LL + wtf: Some(Box::new_in(_, _)), | - = and 13 other candidates + = and 15 other candidates help: consider using the `Default` trait | LL - wtf: Some(Box(U { @@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed(); LL - let _ = Box {}; LL + let _ = Box::new_in(_, _); | - = and 14 other candidates + = and 16 other candidates help: consider using the `Default` trait | LL - let _ = Box {}; @@ -146,7 +146,7 @@ LL + let _ = Box::::map(_, _); LL - let _ = Box:: {}; LL + let _ = Box::::into_inner(_); | - = and 5 other candidates + = and 7 other candidates help: consider using the `Default` trait | LL - let _ = Box:: {}; diff --git a/tests/ui/suggestions/multi-suggestion.ascii.stderr b/tests/ui/suggestions/multi-suggestion.ascii.stderr index 4bd6c19e0829..bb14eb2fb572 100644 --- a/tests/ui/suggestions/multi-suggestion.ascii.stderr +++ b/tests/ui/suggestions/multi-suggestion.ascii.stderr @@ -63,7 +63,7 @@ LL - x: (), LL - })), LL + wtf: Some(Box::new_in(_, _)), | - = and 13 other candidates + = and 15 other candidates help: consider using the `Default` trait | LL - wtf: Some(Box(U { @@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed(); LL - let _ = Box {}; LL + let _ = Box::new_in(_, _); | - = and 14 other candidates + = and 16 other candidates help: consider using the `Default` trait | LL - let _ = Box {}; diff --git a/tests/ui/suggestions/multi-suggestion.unicode.stderr b/tests/ui/suggestions/multi-suggestion.unicode.stderr index b11570f34161..e7f9e7153d19 100644 --- a/tests/ui/suggestions/multi-suggestion.unicode.stderr +++ b/tests/ui/suggestions/multi-suggestion.unicode.stderr @@ -63,7 +63,7 @@ LL - x: (), LL - })), LL + wtf: Some(Box::new_in(_, _)), │ - ╰ and 13 other candidates + ╰ and 15 other candidates help: consider using the `Default` trait ╭╴ LL - wtf: Some(Box(U { @@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed(); LL - let _ = Box {}; LL + let _ = Box::new_in(_, _); │ - ╰ and 14 other candidates + ╰ and 16 other candidates help: consider using the `Default` trait ╭╴ LL - let _ = Box {};