facet_core/impls_alloc/
rc.rs

1use crate::{
2    Def, Facet, KnownSmartPointer, PtrConst, PtrMut, PtrUninit, Shape, SmartPointerDef,
3    SmartPointerFlags, SmartPointerVTable, TryBorrowInnerError, TryFromError, TryIntoInnerError,
4    Type, UserType, ValueVTable, value_vtable,
5};
6
7unsafe impl<'a, T: Facet<'a>> Facet<'a> for alloc::rc::Rc<T> {
8    const VTABLE: &'static ValueVTable = &const {
9        // Define the functions for transparent conversion between Rc<T> and T
10        unsafe fn try_from<'a, 'shape, 'src, 'dst, T: Facet<'a>>(
11            src_ptr: PtrConst<'src>,
12            src_shape: &'shape Shape<'shape>,
13            dst: PtrUninit<'dst>,
14        ) -> Result<PtrMut<'dst>, TryFromError<'shape>> {
15            if src_shape.id != T::SHAPE.id {
16                return Err(TryFromError::UnsupportedSourceShape {
17                    src_shape,
18                    expected: &[T::SHAPE],
19                });
20            }
21            let t = unsafe { src_ptr.read::<T>() };
22            let rc = alloc::rc::Rc::new(t);
23            Ok(unsafe { dst.put(rc) })
24        }
25
26        unsafe fn try_into_inner<'a, 'src, 'dst, T: Facet<'a>>(
27            src_ptr: PtrMut<'src>,
28            dst: PtrUninit<'dst>,
29        ) -> Result<PtrMut<'dst>, TryIntoInnerError> {
30            let rc = unsafe { src_ptr.get::<alloc::rc::Rc<T>>() };
31            match alloc::rc::Rc::try_unwrap(rc.clone()) {
32                Ok(t) => Ok(unsafe { dst.put(t) }),
33                Err(_) => Err(TryIntoInnerError::Unavailable),
34            }
35        }
36
37        unsafe fn try_borrow_inner<'a, 'src, T: Facet<'a>>(
38            src_ptr: PtrConst<'src>,
39        ) -> Result<PtrConst<'src>, TryBorrowInnerError> {
40            let rc = unsafe { src_ptr.get::<alloc::rc::Rc<T>>() };
41            Ok(PtrConst::new(&**rc))
42        }
43
44        let mut vtable = value_vtable!(alloc::rc::Rc<T>, |f, opts| {
45            write!(f, "{}", Self::SHAPE.type_identifier)?;
46            if let Some(opts) = opts.for_children() {
47                write!(f, "<")?;
48                T::SHAPE.vtable.type_name()(f, opts)?;
49                write!(f, ">")?;
50            } else {
51                write!(f, "<…>")?;
52            }
53            Ok(())
54        });
55        {
56            let vtable = vtable.sized_mut().unwrap();
57            vtable.try_from = || Some(try_from::<T>);
58            vtable.try_into_inner = || Some(try_into_inner::<T>);
59            vtable.try_borrow_inner = || Some(try_borrow_inner::<T>);
60        }
61        vtable
62    };
63
64    const SHAPE: &'static crate::Shape<'static> = &const {
65        // Function to return inner type's shape
66        fn inner_shape<'a, T: Facet<'a>>() -> &'static Shape<'static> {
67            T::SHAPE
68        }
69
70        crate::Shape::builder_for_sized::<Self>()
71            .type_identifier("Rc")
72            .type_params(&[crate::TypeParam {
73                name: "T",
74                shape: || T::SHAPE,
75            }])
76            .ty(Type::User(UserType::Opaque))
77            .def(Def::SmartPointer(
78                SmartPointerDef::builder()
79                    .pointee(|| T::SHAPE)
80                    .flags(SmartPointerFlags::EMPTY)
81                    .known(KnownSmartPointer::Rc)
82                    .weak(|| <alloc::rc::Weak<T> as Facet>::SHAPE)
83                    .vtable(
84                        &const {
85                            SmartPointerVTable::builder()
86                                .borrow_fn(|this| {
87                                    let ptr = Self::as_ptr(unsafe { this.get() });
88                                    PtrConst::new(ptr)
89                                })
90                                .new_into_fn(|this, ptr| {
91                                    let t = unsafe { ptr.read::<T>() };
92                                    let rc = alloc::rc::Rc::new(t);
93                                    unsafe { this.put(rc) }
94                                })
95                                .downgrade_into_fn(|strong, weak| unsafe {
96                                    weak.put(alloc::rc::Rc::downgrade(strong.get::<Self>()))
97                                })
98                                .build()
99                        },
100                    )
101                    .build(),
102            ))
103            .inner(inner_shape::<T>)
104            .build()
105    };
106}
107
108unsafe impl<'a, T: Facet<'a>> Facet<'a> for alloc::rc::Weak<T> {
109    const VTABLE: &'static ValueVTable = &const {
110        value_vtable!(alloc::rc::Weak<T>, |f, opts| {
111            write!(f, "{}", Self::SHAPE.type_identifier)?;
112            if let Some(opts) = opts.for_children() {
113                write!(f, "<")?;
114                T::SHAPE.vtable.type_name()(f, opts)?;
115                write!(f, ">")?;
116            } else {
117                write!(f, "<…>")?;
118            }
119            Ok(())
120        })
121    };
122
123    const SHAPE: &'static crate::Shape<'static> = &const {
124        // Function to return inner type's shape
125        fn inner_shape<'a, T: Facet<'a>>() -> &'static Shape<'static> {
126            T::SHAPE
127        }
128
129        crate::Shape::builder_for_sized::<Self>()
130            .type_identifier("Weak")
131            .type_params(&[crate::TypeParam {
132                name: "T",
133                shape: || T::SHAPE,
134            }])
135            .ty(Type::User(UserType::Opaque))
136            .def(Def::SmartPointer(
137                SmartPointerDef::builder()
138                    .pointee(|| T::SHAPE)
139                    .flags(SmartPointerFlags::WEAK)
140                    .known(KnownSmartPointer::RcWeak)
141                    .strong(|| <alloc::rc::Rc<T> as Facet>::SHAPE)
142                    .vtable(
143                        &const {
144                            SmartPointerVTable::builder()
145                                .upgrade_into_fn(|weak, strong| unsafe {
146                                    Some(strong.put(weak.get::<Self>().upgrade()?))
147                                })
148                                .build()
149                        },
150                    )
151                    .build(),
152            ))
153            .inner(inner_shape::<T>)
154            .build()
155    };
156}
157
158#[cfg(test)]
159mod tests {
160    use alloc::rc::{Rc, Weak as RcWeak};
161    use alloc::string::String;
162
163    use super::*;
164
165    #[test]
166    fn test_rc_type_params() {
167        let [type_param_1] = <Rc<i32>>::SHAPE.type_params else {
168            panic!("Rc<T> should only have 1 type param")
169        };
170        assert_eq!(type_param_1.shape(), i32::SHAPE);
171    }
172
173    #[test]
174    fn test_rc_vtable_1_new_borrow_drop() -> eyre::Result<()> {
175        facet_testhelpers::setup();
176
177        let rc_shape = <Rc<String>>::SHAPE;
178        let rc_def = rc_shape
179            .def
180            .into_smart_pointer()
181            .expect("Rc<T> should have a smart pointer definition");
182
183        // Allocate memory for the Rc
184        let rc_uninit_ptr = rc_shape.allocate()?;
185
186        // Get the function pointer for creating a new Rc from a value
187        let new_into_fn = rc_def
188            .vtable
189            .new_into_fn
190            .expect("Rc<T> should have new_into_fn");
191
192        // Create the value and initialize the Rc
193        let mut value = String::from("example");
194        let rc_ptr = unsafe { new_into_fn(rc_uninit_ptr, PtrMut::new(&raw mut value)) };
195        // The value now belongs to the Rc, prevent its drop
196        core::mem::forget(value);
197
198        // Get the function pointer for borrowing the inner value
199        let borrow_fn = rc_def
200            .vtable
201            .borrow_fn
202            .expect("Rc<T> should have borrow_fn");
203
204        // Borrow the inner value and check it
205        let borrowed_ptr = unsafe { borrow_fn(rc_ptr.as_const()) };
206        // SAFETY: borrowed_ptr points to a valid String within the Rc
207        assert_eq!(unsafe { borrowed_ptr.get::<String>() }, "example");
208
209        // Get the function pointer for dropping the Rc
210        let drop_fn = (rc_shape.vtable.sized().unwrap().drop_in_place)()
211            .expect("Rc<T> should have drop_in_place");
212
213        // Drop the Rc in place
214        // SAFETY: rc_ptr points to a valid Rc<String>
215        unsafe { drop_fn(rc_ptr) };
216
217        // Deallocate the memory
218        // SAFETY: rc_ptr was allocated by rc_shape and is now dropped (but memory is still valid)
219        unsafe { rc_shape.deallocate_mut(rc_ptr)? };
220
221        Ok(())
222    }
223
224    #[test]
225    fn test_rc_vtable_2_downgrade_upgrade_drop() -> eyre::Result<()> {
226        facet_testhelpers::setup();
227
228        let rc_shape = <Rc<String>>::SHAPE;
229        let rc_def = rc_shape
230            .def
231            .into_smart_pointer()
232            .expect("Rc<T> should have a smart pointer definition");
233
234        let weak_shape = <RcWeak<String>>::SHAPE;
235        let weak_def = weak_shape
236            .def
237            .into_smart_pointer()
238            .expect("RcWeak<T> should have a smart pointer definition");
239
240        // 1. Create the first Rc (rc1)
241        let rc1_uninit_ptr = rc_shape.allocate()?;
242        let new_into_fn = rc_def.vtable.new_into_fn.unwrap();
243        let mut value = String::from("example");
244        let rc1_ptr = unsafe { new_into_fn(rc1_uninit_ptr, PtrMut::new(&raw mut value)) };
245        core::mem::forget(value); // Value now owned by rc1
246
247        // 2. Downgrade rc1 to create a weak pointer (weak1)
248        let weak1_uninit_ptr = weak_shape.allocate()?;
249        let downgrade_into_fn = rc_def.vtable.downgrade_into_fn.unwrap();
250        // SAFETY: rc1_ptr points to a valid Rc, weak1_uninit_ptr is allocated for a Weak
251        let weak1_ptr = unsafe { downgrade_into_fn(rc1_ptr, weak1_uninit_ptr) };
252
253        // 3. Upgrade weak1 to create a second Rc (rc2)
254        let rc2_uninit_ptr = rc_shape.allocate()?;
255        let upgrade_into_fn = weak_def.vtable.upgrade_into_fn.unwrap();
256        // SAFETY: weak1_ptr points to a valid Weak, rc2_uninit_ptr is allocated for an Rc.
257        // Upgrade should succeed as rc1 still exists.
258        let rc2_ptr = unsafe { upgrade_into_fn(weak1_ptr, rc2_uninit_ptr) }
259            .expect("Upgrade should succeed while original Rc exists");
260
261        // Check the content of the upgraded Rc
262        let borrow_fn = rc_def.vtable.borrow_fn.unwrap();
263        // SAFETY: rc2_ptr points to a valid Rc<String>
264        let borrowed_ptr = unsafe { borrow_fn(rc2_ptr.as_const()) };
265        // SAFETY: borrowed_ptr points to a valid String
266        assert_eq!(unsafe { borrowed_ptr.get::<String>() }, "example");
267
268        // 4. Drop everything and free memory
269        let rc_drop_fn = (rc_shape.vtable.sized().unwrap().drop_in_place)().unwrap();
270        let weak_drop_fn = (weak_shape.vtable.sized().unwrap().drop_in_place)().unwrap();
271
272        unsafe {
273            // Drop Rcs
274            rc_drop_fn(rc1_ptr);
275            rc_shape.deallocate_mut(rc1_ptr)?;
276            rc_drop_fn(rc2_ptr);
277            rc_shape.deallocate_mut(rc2_ptr)?;
278
279            // Drop Weak
280            weak_drop_fn(weak1_ptr);
281            weak_shape.deallocate_mut(weak1_ptr)?;
282        }
283
284        Ok(())
285    }
286
287    #[test]
288    fn test_rc_vtable_3_downgrade_drop_try_upgrade() -> eyre::Result<()> {
289        facet_testhelpers::setup();
290
291        let rc_shape = <Rc<String>>::SHAPE;
292        let rc_def = rc_shape
293            .def
294            .into_smart_pointer()
295            .expect("Rc<T> should have a smart pointer definition");
296
297        let weak_shape = <RcWeak<String>>::SHAPE;
298        let weak_def = weak_shape
299            .def
300            .into_smart_pointer()
301            .expect("RcWeak<T> should have a smart pointer definition");
302
303        // 1. Create the strong Rc (rc1)
304        let rc1_uninit_ptr = rc_shape.allocate()?;
305        let new_into_fn = rc_def.vtable.new_into_fn.unwrap();
306        let mut value = String::from("example");
307        let rc1_ptr = unsafe { new_into_fn(rc1_uninit_ptr, PtrMut::new(&raw mut value)) };
308        core::mem::forget(value);
309
310        // 2. Downgrade rc1 to create a weak pointer (weak1)
311        let weak1_uninit_ptr = weak_shape.allocate()?;
312        let downgrade_into_fn = rc_def.vtable.downgrade_into_fn.unwrap();
313        // SAFETY: rc1_ptr is valid, weak1_uninit_ptr is allocated for Weak
314        let weak1_ptr = unsafe { downgrade_into_fn(rc1_ptr, weak1_uninit_ptr) };
315
316        // 3. Drop and free the strong pointer (rc1)
317        let rc_drop_fn = (rc_shape.vtable.sized().unwrap().drop_in_place)().unwrap();
318        unsafe {
319            rc_drop_fn(rc1_ptr);
320            rc_shape.deallocate_mut(rc1_ptr)?;
321        }
322
323        // 4. Attempt to upgrade the weak pointer (weak1)
324        let upgrade_into_fn = weak_def.vtable.upgrade_into_fn.unwrap();
325        let rc2_uninit_ptr = rc_shape.allocate()?;
326        // SAFETY: weak1_ptr is valid (though points to dropped data), rc2_uninit_ptr is allocated for Rc
327        let upgrade_result = unsafe { upgrade_into_fn(weak1_ptr, rc2_uninit_ptr) };
328
329        // Assert that the upgrade failed
330        assert!(
331            upgrade_result.is_none(),
332            "Upgrade should fail after the strong Rc is dropped"
333        );
334
335        // 5. Clean up: Deallocate the memory intended for the failed upgrade and drop/deallocate the weak pointer
336        let weak_drop_fn = (weak_shape.vtable.sized().unwrap().drop_in_place)().unwrap();
337        unsafe {
338            // Deallocate the *uninitialized* memory allocated for the failed upgrade attempt
339            rc_shape.deallocate_uninit(rc2_uninit_ptr)?;
340
341            // Drop and deallocate the weak pointer
342            weak_drop_fn(weak1_ptr);
343            weak_shape.deallocate_mut(weak1_ptr)?;
344        }
345
346        Ok(())
347    }
348}