Skip to main content

facet_reflect/partial/partial_api/
ptr.rs

1use super::*;
2use crate::AllocatedShape;
3
4////////////////////////////////////////////////////////////////////////////////////////////////////
5// Smart pointers
6////////////////////////////////////////////////////////////////////////////////////////////////////
7impl<const BORROW: bool> Partial<'_, BORROW> {
8    /// Pushes a frame to initialize the inner value of a smart pointer (`Box<T>`, `Arc<T>`, etc.)
9    pub fn begin_smart_ptr(mut self) -> Result<Self, ReflectError> {
10        crate::trace!("begin_smart_ptr()");
11
12        // Check that we have a SmartPointer and get necessary data
13        let (smart_ptr_def, pointee_shape) = {
14            let frame = self.frames().last().unwrap();
15
16            match &frame.allocated.shape().def {
17                Def::Pointer(smart_ptr_def) if smart_ptr_def.constructible_from_pointee() => {
18                    let pointee_shape = match smart_ptr_def.pointee() {
19                        Some(shape) => shape,
20                        None => {
21                            return Err(self.err(ReflectErrorKind::OperationFailed {
22                                shape: frame.allocated.shape(),
23                                operation: "Smart pointer must have a pointee shape",
24                            }));
25                        }
26                    };
27                    (*smart_ptr_def, pointee_shape)
28                }
29                _ => {
30                    return Err(self.err(ReflectErrorKind::OperationFailed {
31                        shape: frame.allocated.shape(),
32                        operation: "push_smart_ptr can only be called on compatible types",
33                    }));
34                }
35            }
36        };
37
38        // Handle re-initialization if the smart pointer is already initialized
39        self.prepare_for_reinitialization();
40
41        // Get shape and type_plan upfront to avoid borrow conflicts
42        let shape = self.frames().last().unwrap().allocated.shape();
43        let parent_type_plan = self.frames().last().unwrap().type_plan;
44
45        if pointee_shape.layout.sized_layout().is_ok() {
46            // pointee is sized, we can allocate it — for `Arc<T>` we'll be allocating a `T` and
47            // holding onto it. We'll build a new Arc with it when ending the smart pointer frame.
48
49            self.mode.stack_mut().last_mut().unwrap().tracker = Tracker::SmartPointer {
50                building_inner: true,
51                pending_inner: None,
52            };
53
54            let inner_layout = match pointee_shape.layout.sized_layout() {
55                Ok(layout) => layout,
56                Err(_) => {
57                    return Err(self.err(ReflectErrorKind::Unsized {
58                        shape: pointee_shape,
59                        operation: "begin_smart_ptr, calculating inner value layout",
60                    }));
61                }
62            };
63            let inner_ptr = facet_core::alloc_for_layout(inner_layout);
64
65            // Push a new frame for the inner value
66            // Get child type plan NodeId for smart pointer pointee
67            let child_plan_id = self
68                .root_plan
69                .pointer_inner_node_id(parent_type_plan)
70                .expect("TypePlan should have pointee node for sized pointer");
71            self.mode.stack_mut().push(Frame::new(
72                inner_ptr,
73                AllocatedShape::new(pointee_shape, inner_layout.size()),
74                FrameOwnership::Owned,
75                child_plan_id,
76            ));
77        } else {
78            // pointee is unsized, we only support a handful of cases there
79            if pointee_shape == str::SHAPE {
80                crate::trace!("Pointee is str");
81
82                // Mark the SmartPointer frame as building its inner value
83                // This is needed for derive_path() to add a Deref step
84                self.mode.stack_mut().last_mut().unwrap().tracker = Tracker::SmartPointer {
85                    building_inner: true,
86                    pending_inner: None,
87                };
88
89                // Allocate space for a String
90                let string_layout = String::SHAPE
91                    .layout
92                    .sized_layout()
93                    .expect("String must have a sized layout");
94                let string_ptr = facet_core::alloc_for_layout(string_layout);
95                let string_size = string_layout.size();
96                // For Arc<str> -> String conversion, TypePlan builds for the conversion source (String)
97                let child_plan_id = self
98                    .root_plan
99                    .pointer_inner_node_id(parent_type_plan)
100                    .expect("TypePlan should have pointee node for str->String conversion");
101                let new_frame = Frame::new(
102                    string_ptr,
103                    AllocatedShape::new(String::SHAPE, string_size),
104                    FrameOwnership::Owned,
105                    child_plan_id,
106                );
107                // Frame::new already sets tracker = Scalar and is_init = false
108                self.mode.stack_mut().push(new_frame);
109            } else if let Type::Sequence(SequenceType::Slice(_st)) = pointee_shape.ty {
110                crate::trace!("Pointee is [{}]", _st.t);
111
112                // Get the slice builder vtable
113                let slice_builder_vtable = match smart_ptr_def.vtable.slice_builder_vtable {
114                    Some(vtable) => vtable,
115                    None => {
116                        return Err(self.err(ReflectErrorKind::OperationFailed {
117                            shape,
118                            operation: "smart pointer does not support slice building",
119                        }));
120                    }
121                };
122
123                // Create a new builder
124                let builder_ptr = (slice_builder_vtable.new_fn)();
125
126                // Deallocate the original Arc allocation before replacing with slice builder
127                let frame = self.mode.stack_mut().last_mut().unwrap();
128                if let FrameOwnership::Owned = frame.ownership
129                    && let Ok(layout) = shape.layout.sized_layout()
130                    && layout.size() > 0
131                {
132                    unsafe { ::alloc::alloc::dealloc(frame.data.as_mut_byte_ptr(), layout) };
133                }
134
135                // Update the current frame to use the slice builder
136                frame.data = builder_ptr.as_uninit();
137                frame.tracker = Tracker::SmartPointerSlice {
138                    vtable: slice_builder_vtable,
139                    building_item: false,
140                    current_child: None,
141                };
142                // Keep the original ownership (e.g., Field) so parent tracking works correctly.
143                // The slice builder memory itself is managed by the vtable's convert_fn/free_fn.
144            } else {
145                return Err(self.err(ReflectErrorKind::OperationFailed {
146                    shape,
147                    operation: "push_smart_ptr can only be called on pointers to supported pointee types",
148                }));
149            }
150        }
151
152        Ok(self)
153    }
154}