facet_core/impls_alloc/
boxed.rs

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