Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/core/src/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

mod arc;
mod boxed;
mod try_clone;
mod try_collect;
mod try_new;
mod vec;
Expand All @@ -11,6 +12,7 @@ pub use boxed::{
new_boxed_slice_from_fallible_iter, new_boxed_slice_from_iter,
new_boxed_slice_from_iter_with_len, new_uninit_boxed_slice,
};
pub use try_clone::TryClone;
pub use try_collect::{TryCollect, TryExtend, TryFromIterator};
pub use try_new::{TryNew, try_new};
pub use vec::Vec;
Expand Down
27 changes: 26 additions & 1 deletion crates/core/src/alloc/boxed.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{TryNew, Vec, try_alloc};
use super::{TryClone, TryNew, Vec, try_alloc};
use crate::error::OutOfMemory;
use core::{
alloc::Layout,
Expand Down Expand Up @@ -42,6 +42,31 @@ impl<T> TryNew for Box<T> {
}
}

impl<T> TryClone for Box<T>
where
T: TryClone,
{
fn try_clone(&self) -> Result<Self, OutOfMemory> {
let b = new_uninit_box::<T>()?;
let v = (**self).try_clone()?;
Ok(Box::write(b, v))
}
}

impl<T> TryClone for Box<[T]>
where
T: TryClone,
{
fn try_clone(&self) -> Result<Self, OutOfMemory> {
let mut builder = BoxedSliceBuilder::new(self.len())?;
for v in &*self {
builder.push(v.try_clone()?).expect("reserved capacity");
}
debug_assert_eq!(builder.init_len(), builder.capacity());
Ok(builder.finish())
}
}

/// Allocate a new `Box<[MaybeUninit<T>]>` of the given length with
/// uninitialized contents, returning `Err(OutOfMemory)` on allocation failure.
///
Expand Down
74 changes: 74 additions & 0 deletions crates/core/src/alloc/try_clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::error::OutOfMemory;

/// A trait for values that can be cloned, but contain owned, heap-allocated
/// values whose allocations may fail during cloning.
pub trait TryClone: Sized {
/// Attempt to clone `self`, returning an error if any allocation fails
/// during cloning.
fn try_clone(&self) -> Result<Self, OutOfMemory>;
}

impl<T> TryClone for *mut T {
#[inline]
fn try_clone(&self) -> Result<Self, OutOfMemory> {
Ok(*self)
}
}

impl<T> TryClone for core::ptr::NonNull<T> {
#[inline]
fn try_clone(&self) -> Result<Self, OutOfMemory> {
Ok(*self)
}
}

macro_rules! impl_try_clone_via_clone {
( $( $ty:ty ),* $(,)? ) => {
$(
impl TryClone for $ty {
#[inline]
fn try_clone(&self) -> Result<Self, OutOfMemory> {
Ok(self.clone())
}
}
)*
};
}

impl_try_clone_via_clone! {
bool, char,
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
f32, f64,
}

macro_rules! tuples {
( $( $( $t:ident ),* );*) => {
$(
impl<$($t),*> TryClone for ( $($t,)* )
where
$( $t: TryClone ),*
{
#[inline]
fn try_clone(&self) -> Result<Self, OutOfMemory> {
#[allow(non_snake_case, reason = "macro code")]
let ( $($t,)* ) = self;
Ok(( $( $t.try_clone()?, )* ))
}
}
)*
};
}

tuples! {
A;
A, B;
A, B, C;
A, B, C, D;
A, B, C, D, E;
A, B, C, D, E, F;
A, B, C, D, E, F, G;
A, B, C, D, E, F, G, H;
A, B, C, D, E, F, G, H, I;
A, B, C, D, E, F, G, H, I, J;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this perhaps be impl<T: Copy> TryClone for T?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, we would still need these implementations for tuples even with that blanket impl, so that something like (Box<usize>, Vec<u8>), which is not Copy, could still implement TryClone.

I did try to implement TryClone for all T: Copy and ran into conflicting trait impls. I guess because (T0, T1) might be Copy and Rust doesn't know whether to use the blanket Copy impl or the where T0: TryClone, T1: TryClone impl?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Enqueuing to merge now, if you know of a way to resolve this issue, we can do it in a follow up.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah I thought coherence wouldn't trip up here but it's definitely tripping up, so definitely not possible

15 changes: 14 additions & 1 deletion crates/core/src/alloc/vec.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::alloc::try_realloc;
use crate::alloc::{TryClone, try_realloc};
use crate::error::OutOfMemory;
use core::{
fmt, mem,
Expand Down Expand Up @@ -29,6 +29,19 @@ impl<T: fmt::Debug> fmt::Debug for Vec<T> {
}
}

impl<T> TryClone for Vec<T>
where
T: TryClone,
{
fn try_clone(&self) -> Result<Self, OutOfMemory> {
let mut v = Vec::with_capacity(self.len())?;
for x in self {
v.push(x.try_clone()?).expect("reserved capacity");
}
Ok(v)
}
}

impl<T> Vec<T> {
/// Same as [`std::vec::Vec::new`].
pub fn new() -> Self {
Expand Down
2 changes: 1 addition & 1 deletion crates/environ/src/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use entity_set::EntitySet;
pub use hash_set::HashSet;
pub use primary_map::PrimaryMap;
pub use secondary_map::SecondaryMap;
pub use wasmtime_core::alloc::{TryNew, Vec, try_new};
pub use wasmtime_core::alloc::{TryClone, TryNew, Vec, try_new};

/// Collections which abort on OOM.
//
Expand Down