Skip to content

Commit adaa615

Browse files
committed
Implement From<Box<T>> for Gc<T>
This is especially useful on nightly, where it works for unsized trait objects. Signed-off-by: Anders Kaseorg <[email protected]>
1 parent 86e2fe5 commit adaa615

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

gc/src/gc.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
use crate::set_data_ptr;
12
use crate::trace::Trace;
3+
use std::alloc::{alloc, dealloc, Layout};
24
use std::cell::{Cell, RefCell};
35
use std::mem;
46
use std::ptr::{self, NonNull};
57

8+
#[cfg(feature = "nightly")]
9+
use std::marker::Unsize;
10+
611
struct GcState {
712
stats: GcStats,
813
config: GcConfig,
@@ -103,7 +108,7 @@ impl GcBoxHeader {
103108
}
104109
}
105110

106-
#[repr(C)] // to justify the layout computation in Gc::from_raw
111+
#[repr(C)] // to justify the layout computations in GcBox::from_box, Gc::from_raw
107112
pub(crate) struct GcBox<T: Trace + ?Sized + 'static> {
108113
header: GcBoxHeader,
109114
data: T,
@@ -124,6 +129,37 @@ impl<T: Trace> GcBox<T> {
124129
}
125130
}
126131

132+
impl<
133+
#[cfg(not(feature = "nightly"))] T: Trace,
134+
#[cfg(feature = "nightly")] T: Trace + Unsize<dyn Trace> + ?Sized,
135+
> GcBox<T>
136+
{
137+
pub(crate) fn from_box(value: Box<T>) -> NonNull<Self> {
138+
let header_layout = Layout::new::<GcBoxHeader>();
139+
let value_layout = Layout::for_value::<T>(&*value);
140+
let gcbox_layout = header_layout.extend(value_layout).unwrap().0.pad_to_align();
141+
142+
unsafe {
143+
let gcbox_addr = alloc(gcbox_layout);
144+
let value = Box::into_raw(value);
145+
let gcbox = set_data_ptr(value as *mut GcBox<T>, gcbox_addr);
146+
ptr::addr_of_mut!((*gcbox).header).write(GcBoxHeader::new());
147+
ptr::addr_of_mut!((*gcbox).data)
148+
.cast::<u8>()
149+
.copy_from_nonoverlapping(value.cast::<u8>(), value_layout.size());
150+
let gcbox = NonNull::new_unchecked(gcbox);
151+
insert_gcbox(gcbox);
152+
153+
// Box only allocates for size != 0
154+
if value_layout.size() != 0 {
155+
dealloc(value.cast::<u8>(), value_layout);
156+
}
157+
158+
gcbox
159+
}
160+
}
161+
}
162+
127163
unsafe fn insert_gcbox(gcbox: NonNull<GcBox<dyn Trace>>) {
128164
GC_STATE.with(|st| {
129165
let mut st = st.borrow_mut();

gc/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,16 @@ impl<T: Trace> From<T> for Gc<T> {
374374
}
375375
}
376376

377+
impl<
378+
#[cfg(not(feature = "nightly"))] T: Trace,
379+
#[cfg(feature = "nightly")] T: Trace + Unsize<dyn Trace> + ?Sized,
380+
> From<Box<T>> for Gc<T>
381+
{
382+
fn from(v: Box<T>) -> Gc<T> {
383+
unsafe { Gc::from_gcbox(GcBox::from_box(v)) }
384+
}
385+
}
386+
377387
impl<T: Trace + ?Sized> std::borrow::Borrow<T> for Gc<T> {
378388
fn borrow(&self) -> &T {
379389
self

gc/tests/from_box.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use gc::{Finalize, Gc, Trace};
2+
3+
trait Foo: Trace {}
4+
5+
#[derive(Trace, Finalize)]
6+
struct Bar;
7+
impl Foo for Bar {}
8+
9+
#[test]
10+
fn test_from_box_sized() {
11+
let b: Box<[i32; 3]> = Box::new([1, 2, 3]);
12+
let _: Gc<[i32; 3]> = Gc::from(b);
13+
}
14+
15+
#[cfg(feature = "nightly")]
16+
#[test]
17+
fn test_from_box_dyn() {
18+
let b: Box<dyn Foo> = Box::new(Bar);
19+
let _: Gc<dyn Foo> = Gc::from(b);
20+
}

0 commit comments

Comments
 (0)