facet_core/impls_alloc/
boxed.rs

1use core::{alloc::Layout, ptr::NonNull};
2
3use alloc::boxed::Box;
4
5use crate::{
6    Def, Facet, KnownPointer, PointerDef, PointerFlags, PointerVTable, PtrConst, PtrMut, PtrUninit,
7    Shape, TryBorrowInnerError, TryFromError, TryIntoInnerError, Type, UserType,
8    shape_util::vtable_builder_for_ptr,
9};
10
11// Define the functions for transparent conversion between Box<T> and T
12unsafe fn try_from<'src, 'dst>(
13    src_ptr: PtrConst<'src>,
14    src_shape: &'static Shape,
15    dst: PtrUninit<'dst>,
16) -> Result<PtrMut<'dst>, TryFromError> {
17    let layout = src_shape.layout.sized_layout().unwrap();
18
19    unsafe {
20        let alloc = alloc::alloc::alloc(layout);
21        if alloc.is_null() {
22            alloc::alloc::handle_alloc_error(layout);
23        }
24
25        let src_ptr = src_ptr.as_ptr::<u8>();
26        core::ptr::copy_nonoverlapping(src_ptr, alloc, layout.size());
27
28        // layout of
29        // Box<T> == *mut T == *mut u8
30        Ok(dst.put(alloc))
31    }
32}
33
34unsafe fn try_into_inner<'a, 'src, 'dst, T: ?Sized + Facet<'a>>(
35    src_ptr: PtrMut<'src>,
36    dst: PtrUninit<'dst>,
37) -> Result<PtrMut<'dst>, TryIntoInnerError> {
38    if const { size_of::<*const T>() == size_of::<*const ()>() } {
39        let boxed = unsafe { src_ptr.read::<Box<T>>() };
40        let layout = Layout::for_value(&*boxed);
41        let ptr = Box::into_raw(boxed) as *mut u8;
42        unsafe {
43            core::ptr::copy_nonoverlapping(ptr, dst.as_mut_byte_ptr(), layout.size());
44            alloc::alloc::dealloc(ptr, layout);
45            Ok(dst.assume_init())
46        }
47    } else {
48        panic!();
49    }
50}
51unsafe impl<'a, T: ?Sized + Facet<'a>> Facet<'a> for Box<T> {
52    const SHAPE: &'static crate::Shape = &const {
53        crate::Shape::builder_for_sized::<Self>()
54            .vtable({
55                unsafe fn try_borrow_inner<'a, 'src, T: ?Sized + Facet<'a>>(
56                    src_ptr: PtrConst<'src>,
57                ) -> Result<PtrConst<'src>, TryBorrowInnerError> {
58                    let boxed = unsafe { src_ptr.get::<Box<T>>() };
59                    Ok(PtrConst::new(NonNull::from(&**boxed)))
60                }
61
62                let mut vtable = vtable_builder_for_ptr::<T, Self>()
63                    .type_name(|f, opts| {
64                        write!(f, "{}", Self::SHAPE.type_identifier)?;
65                        if let Some(opts) = opts.for_children() {
66                            write!(f, "<")?;
67                            (T::SHAPE.vtable.type_name())(f, opts)?;
68                            write!(f, ">")?;
69                        } else {
70                            write!(f, "<…>")?;
71                        }
72                        Ok(())
73                    })
74                    .build();
75                if size_of::<*const T>() == size_of::<*const ()>() {
76                    vtable.try_from = Some(try_from);
77                    vtable.try_into_inner = Some(try_into_inner::<T>);
78                }
79                vtable.try_borrow_inner = Some(try_borrow_inner::<T>);
80                vtable
81            })
82            .type_identifier("Box")
83            .type_params(&[crate::TypeParam {
84                name: "T",
85                shape: T::SHAPE,
86            }])
87            .ty(Type::User(UserType::Opaque))
88            .def(Def::Pointer(
89                PointerDef::builder()
90                    .pointee(T::SHAPE)
91                    .flags(PointerFlags::EMPTY)
92                    .known(KnownPointer::Box)
93                    .vtable(
94                        &const {
95                            let mut vtable = PointerVTable::builder().borrow_fn(|this| unsafe {
96                                let concrete = this.get::<Box<T>>();
97                                let t: &T = concrete.as_ref();
98                                PtrConst::new(NonNull::from(t))
99                            });
100                            if size_of::<*const T>() == size_of::<*const ()>() {
101                                vtable = vtable.new_into_fn(|this, ptr| unsafe {
102                                    try_from(ptr.as_const(), T::SHAPE, this).unwrap()
103                                })
104                            }
105                            vtable.build()
106                        },
107                    )
108                    .build(),
109            ))
110            .inner(T::SHAPE)
111            .build()
112    };
113}
114
115#[cfg(test)]
116mod tests {
117    use core::mem::ManuallyDrop;
118
119    use alloc::boxed::Box;
120    use alloc::string::String;
121
122    use super::*;
123
124    #[test]
125    fn test_box_type_params() {
126        let [type_param_1] = <Box<i32>>::SHAPE.type_params else {
127            panic!("Box<T> should only have 1 type param")
128        };
129        assert_eq!(type_param_1.shape(), i32::SHAPE);
130    }
131
132    #[test]
133    fn test_box_vtable_1_new_borrow_drop() {
134        facet_testhelpers::setup();
135
136        let box_shape = <Box<String>>::SHAPE;
137        let box_def = box_shape
138            .def
139            .into_pointer()
140            .expect("Box<T> should have a smart pointer definition");
141
142        // Allocate memory for the Box
143        let box_uninit_ptr = box_shape.allocate().unwrap();
144
145        // Get the function pointer for creating a new Box from a value
146        let new_into_fn = box_def
147            .vtable
148            .new_into_fn
149            .expect("Box<T> should have new_into_fn");
150
151        // Create the value and initialize the Box
152        let mut value = ManuallyDrop::new(String::from("example"));
153        let box_ptr =
154            unsafe { new_into_fn(box_uninit_ptr, PtrMut::new(NonNull::from(&mut value))) };
155        // The value now belongs to the Box, prevent its drop
156
157        // Get the function pointer for borrowing the inner value
158        let borrow_fn = box_def
159            .vtable
160            .borrow_fn
161            .expect("Box<T> should have borrow_fn");
162
163        // Borrow the inner value and check it
164        let borrowed_ptr = unsafe { borrow_fn(box_ptr.as_const()) };
165        // SAFETY: borrowed_ptr points to a valid String within the Box
166        assert_eq!(unsafe { borrowed_ptr.get::<String>() }, "example");
167
168        // Get the function pointer for dropping the Box
169        let drop_fn = box_shape
170            .vtable
171            .drop_in_place
172            .expect("Box<T> should have drop_in_place");
173
174        // Drop the Box in place
175        // SAFETY: box_ptr points to a valid Box<String>
176        unsafe { drop_fn(box_ptr) };
177
178        // Deallocate the memory
179        // SAFETY: box_ptr was allocated by box_shape and is now dropped (but memory is still valid)
180        unsafe { box_shape.deallocate_mut(box_ptr).unwrap() };
181    }
182}