Skip to content

Commit

Permalink
Implement From<Box<T>> for Gc<T>
Browse files Browse the repository at this point in the history
This is especially useful on nightly, where it works for unsized trait
objects.

Signed-off-by: Anders Kaseorg <[email protected]>
  • Loading branch information
andersk committed Dec 21, 2022
1 parent 86e2fe5 commit 57ab408
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 1 deletion.
38 changes: 37 additions & 1 deletion gc/src/gc.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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<T: Trace + ?Sized + 'static> {
header: GcBoxHeader,
data: T,
Expand All @@ -124,6 +129,37 @@ impl<T: Trace> GcBox<T> {
}
}

impl<
#[cfg(not(feature = "nightly"))] T: Trace,
#[cfg(feature = "nightly")] T: Trace + Unsize<dyn Trace> + ?Sized,
> GcBox<T>
{
pub(crate) fn from_box(value: Box<T>) -> NonNull<Self> {
let header_layout = Layout::new::<GcBoxHeader>();
let value_layout = Layout::for_value::<T>(&*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<T>, gcbox_addr);
ptr::addr_of_mut!((*gcbox).header).write(GcBoxHeader::new());
ptr::addr_of_mut!((*gcbox).data)
.cast::<u8>()
.copy_from_nonoverlapping(value.cast::<u8>(), 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::<u8>(), value_layout);
}

gcbox
}
}
}

unsafe fn insert_gcbox(gcbox: NonNull<GcBox<dyn Trace>>) {
GC_STATE.with(|st| {
let mut st = st.borrow_mut();
Expand Down
10 changes: 10 additions & 0 deletions gc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ impl<T: Trace + ?Sized> Gc<T> {
}
}

impl<
#[cfg(not(feature = "nightly"))] T: Trace,
#[cfg(feature = "nightly")] T: Trace + Unsize<dyn Trace> + ?Sized,
> From<Box<T>> for Gc<T>
{
fn from(v: Box<T>) -> Gc<T> {
unsafe { Gc::from_gcbox(GcBox::from_box(v)) }
}
}

/// Returns the given pointer with its root bit cleared.
unsafe fn clear_root_bit<T: ?Sized + Trace>(ptr: NonNull<GcBox<T>>) -> NonNull<GcBox<T>> {
let ptr = ptr.as_ptr();
Expand Down
20 changes: 20 additions & 0 deletions gc/tests/from_box.rs
Original file line number Diff line number Diff line change
@@ -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<dyn Foo> = Box::new(Bar);
let _: Gc<dyn Foo> = Gc::from(b);
}

0 comments on commit 57ab408

Please sign in to comment.