From 57ab4083eac71c5e1e6fe1668c7bc5ec610f0c8e Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Tue, 20 Dec 2022 15:09:45 -0800 Subject: [PATCH] Implement From> for Gc This is especially useful on nightly, where it works for unsized trait objects. Signed-off-by: Anders Kaseorg --- gc/src/gc.rs | 38 +++++++++++++++++++++++++++++++++++++- gc/src/lib.rs | 10 ++++++++++ gc/tests/from_box.rs | 20 ++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 gc/tests/from_box.rs diff --git a/gc/src/gc.rs b/gc/src/gc.rs index 52874e7..a4d23ed 100644 --- a/gc/src/gc.rs +++ b/gc/src/gc.rs @@ -1,8 +1,13 @@ +use crate::set_data_ptr; use crate::trace::Trace; +use std::alloc::{alloc, dealloc, Layout}; use std::cell::{Cell, RefCell}; use std::mem; use std::ptr::{self, NonNull}; +#[cfg(feature = "nightly")] +use std::marker::Unsize; + struct GcState { stats: GcStats, config: GcConfig, @@ -103,7 +108,7 @@ impl GcBoxHeader { } } -#[repr(C)] // to justify the layout computation in Gc::from_raw +#[repr(C)] // to justify the layout computations in GcBox::from_box, Gc::from_raw pub(crate) struct GcBox { header: GcBoxHeader, data: T, @@ -124,6 +129,37 @@ impl GcBox { } } +impl< + #[cfg(not(feature = "nightly"))] T: Trace, + #[cfg(feature = "nightly")] T: Trace + Unsize + ?Sized, + > GcBox +{ + pub(crate) fn from_box(value: Box) -> NonNull { + let header_layout = Layout::new::(); + let value_layout = Layout::for_value::(&*value); + let gcbox_layout = header_layout.extend(value_layout).unwrap().0.pad_to_align(); + + unsafe { + let gcbox_addr = alloc(gcbox_layout); + let value = Box::into_raw(value); + let gcbox = set_data_ptr(value as *mut GcBox, gcbox_addr); + ptr::addr_of_mut!((*gcbox).header).write(GcBoxHeader::new()); + ptr::addr_of_mut!((*gcbox).data) + .cast::() + .copy_from_nonoverlapping(value.cast::(), value_layout.size()); + let gcbox = NonNull::new_unchecked(gcbox); + insert_gcbox(gcbox); + + // Box only allocates for size != 0 + if value_layout.size() != 0 { + dealloc(value.cast::(), value_layout); + } + + gcbox + } + } +} + unsafe fn insert_gcbox(gcbox: NonNull>) { GC_STATE.with(|st| { let mut st = st.borrow_mut(); diff --git a/gc/src/lib.rs b/gc/src/lib.rs index ff20760..79b9c26 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -101,6 +101,16 @@ impl Gc { } } +impl< + #[cfg(not(feature = "nightly"))] T: Trace, + #[cfg(feature = "nightly")] T: Trace + Unsize + ?Sized, + > From> for Gc +{ + fn from(v: Box) -> Gc { + unsafe { Gc::from_gcbox(GcBox::from_box(v)) } + } +} + /// Returns the given pointer with its root bit cleared. unsafe fn clear_root_bit(ptr: NonNull>) -> NonNull> { let ptr = ptr.as_ptr(); diff --git a/gc/tests/from_box.rs b/gc/tests/from_box.rs new file mode 100644 index 0000000..567b7b5 --- /dev/null +++ b/gc/tests/from_box.rs @@ -0,0 +1,20 @@ +use gc::{Finalize, Gc, Trace}; + +trait Foo: Trace {} + +#[derive(Trace, Finalize)] +struct Bar; +impl Foo for Bar {} + +#[test] +fn test_from_box_sized() { + let b: Box<[i32; 3]> = Box::new([1, 2, 3]); + let _: Gc<[i32; 3]> = Gc::from(b); +} + +#[cfg(feature = "nightly")] +#[test] +fn test_from_box_dyn() { + let b: Box = Box::new(Bar); + let _: Gc = Gc::from(b); +}