facet_reflect/partial/partial_api/
misc.rs

1use super::*;
2
3////////////////////////////////////////////////////////////////////////////////////////////////////
4// Misc.
5////////////////////////////////////////////////////////////////////////////////////////////////////
6impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
7    /// Returns true if the Partial is in an active state (not built or poisoned).
8    ///
9    /// After `build()` succeeds or after an error causes poisoning, the Partial
10    /// becomes inactive and most operations will fail.
11    #[inline]
12    pub fn is_active(&self) -> bool {
13        self.state == PartialState::Active
14    }
15
16    /// Returns the current frame count (depth of nesting)
17    ///
18    /// The initial frame count is 1 — `begin_field` would push a new frame,
19    /// bringing it to 2, then `end` would bring it back to `1`.
20    ///
21    /// This is an implementation detail of `Partial`, kinda, but deserializers
22    /// might use this for debug assertions, to make sure the state is what
23    /// they think it is.
24    #[inline]
25    pub fn frame_count(&self) -> usize {
26        self.frames().len()
27    }
28
29    /// Returns the shape of the current frame.
30    ///
31    /// # Panics
32    ///
33    /// Panics if the Partial has been poisoned or built, or if there are no frames
34    /// (which indicates a bug in the Partial implementation).
35    #[inline]
36    pub fn shape(&self) -> &'static Shape {
37        if self.state != PartialState::Active {
38            panic!(
39                "Partial::shape() called on non-active Partial (state: {:?})",
40                self.state
41            );
42        }
43        self.frames()
44            .last()
45            .expect("Partial::shape() called but no frames exist - this is a bug")
46            .allocated
47            .shape()
48    }
49
50    /// Returns the shape of the current frame, or `None` if the Partial is
51    /// inactive (poisoned or built) or has no frames.
52    ///
53    /// This is useful for debugging/logging where you want to inspect the state
54    /// without risking a panic.
55    #[inline]
56    pub fn try_shape(&self) -> Option<&'static Shape> {
57        if self.state != PartialState::Active {
58            return None;
59        }
60        self.frames().last().map(|f| f.allocated.shape())
61    }
62
63    /// Returns true if the current frame is building a smart pointer slice (Arc<\[T\]>, Rc<\[T\]>, Box<\[T\]>).
64    ///
65    /// This is used by deserializers to determine if they should deserialize as a list
66    /// rather than recursing into the smart pointer type.
67    #[inline]
68    pub fn is_building_smart_ptr_slice(&self) -> bool {
69        if self.state != PartialState::Active {
70            return false;
71        }
72        self.frames()
73            .last()
74            .is_some_and(|f| matches!(f.tracker, Tracker::SmartPointerSlice { .. }))
75    }
76
77    /// Returns the current deferred resolution, if in deferred mode.
78    #[inline]
79    pub fn deferred_resolution(&self) -> Option<&Resolution> {
80        self.resolution()
81    }
82
83    /// Returns the current path in deferred mode as a slice (for debugging/tracing).
84    #[inline]
85    pub fn current_path_slice(&self) -> Option<&[&'static str]> {
86        self.current_path().map(|p| p.as_slice())
87    }
88
89    /// Enables deferred materialization mode with the given Resolution.
90    ///
91    /// When deferred mode is enabled:
92    /// - `end()` stores frames instead of validating them
93    /// - Re-entering a path restores the stored frame with its state intact
94    /// - `finish_deferred()` performs final validation and materialization
95    ///
96    /// This allows deserializers to handle interleaved fields (e.g., TOML dotted
97    /// keys, flattened structs) where nested fields aren't contiguous in the input.
98    ///
99    /// # Use Cases
100    ///
101    /// - TOML dotted keys: `inner.x = 1` followed by `count = 2` then `inner.y = 3`
102    /// - Flattened structs where nested fields appear at the parent level
103    /// - Any format where field order doesn't match struct nesting
104    ///
105    /// # Errors
106    ///
107    /// Returns an error if already in deferred mode.
108    #[inline]
109    pub fn begin_deferred(mut self, resolution: Resolution) -> Result<Self, ReflectError> {
110        // Cannot enable deferred mode if already in deferred mode
111        if self.is_deferred() {
112            return Err(ReflectError::InvariantViolation {
113                invariant: "begin_deferred() called but already in deferred mode",
114            });
115        }
116
117        // Take the stack out of Strict mode and wrap in Deferred mode
118        let FrameMode::Strict { stack } = core::mem::replace(
119            &mut self.mode,
120            FrameMode::Strict { stack: Vec::new() }, // temporary placeholder
121        ) else {
122            unreachable!("just checked we're not in deferred mode");
123        };
124
125        let start_depth = stack.len();
126        self.mode = FrameMode::Deferred {
127            stack,
128            resolution,
129            start_depth,
130            current_path: Vec::new(),
131            stored_frames: BTreeMap::new(),
132        };
133        Ok(self)
134    }
135
136    /// Finishes deferred mode: validates all stored frames and finalizes.
137    ///
138    /// This method:
139    /// 1. Validates that all stored frames are fully initialized
140    /// 2. Processes frames from deepest to shallowest, updating parent ISets
141    /// 3. Validates the root frame
142    ///
143    /// # Errors
144    ///
145    /// Returns an error if any required fields are missing or if the partial is
146    /// not in deferred mode.
147    pub fn finish_deferred(mut self) -> Result<Self, ReflectError> {
148        // Check if we're in deferred mode first, before extracting state
149        if !self.is_deferred() {
150            return Err(ReflectError::InvariantViolation {
151                invariant: "finish_deferred() called but deferred mode is not enabled",
152            });
153        }
154
155        // Extract deferred state, transitioning back to Strict mode
156        let FrameMode::Deferred {
157            stack,
158            start_depth,
159            mut stored_frames,
160            ..
161        } = core::mem::replace(&mut self.mode, FrameMode::Strict { stack: Vec::new() })
162        else {
163            unreachable!("just checked is_deferred()");
164        };
165
166        // Restore the stack to self.mode
167        self.mode = FrameMode::Strict { stack };
168
169        // Sort paths by depth (deepest first) so we process children before parents
170        let mut paths: Vec<_> = stored_frames.keys().cloned().collect();
171        paths.sort_by_key(|b| core::cmp::Reverse(b.len()));
172
173        trace!(
174            "finish_deferred: Processing {} stored frames in order: {:?}",
175            paths.len(),
176            paths
177        );
178
179        // Process each stored frame from deepest to shallowest
180        for path in paths {
181            let mut frame = stored_frames.remove(&path).unwrap();
182
183            trace!(
184                "finish_deferred: Processing frame at {:?}, shape {}, tracker {:?}",
185                path,
186                frame.allocated.shape(),
187                frame.tracker.kind()
188            );
189
190            // Fill in defaults for unset fields that have defaults
191            if let Err(e) = frame.fill_defaults() {
192                frame.deinit();
193                frame.dealloc();
194                for (_, mut remaining_frame) in stored_frames {
195                    remaining_frame.deinit();
196                    remaining_frame.dealloc();
197                }
198                return Err(e);
199            }
200
201            // Validate the frame is fully initialized
202            if let Err(e) = frame.require_full_initialization() {
203                frame.deinit();
204                frame.dealloc();
205                for (_, mut remaining_frame) in stored_frames {
206                    remaining_frame.deinit();
207                    remaining_frame.dealloc();
208                }
209                return Err(e);
210            }
211
212            // Update parent's ISet to mark this field as initialized.
213            // The parent could be:
214            // 1. On the frames stack (if path.len() == 1, parent is at start_depth - 1)
215            // 2. On the frames stack (if parent was pushed but never ended)
216            // 3. In stored_frames (if parent was ended during deferred mode)
217            if let Some(field_name) = path.last() {
218                let parent_path: Vec<_> = path[..path.len() - 1].to_vec();
219
220                // Special handling for Option inner values: when path ends with "Some",
221                // the parent is an Option frame and we need to complete the Option by
222                // writing the inner value into the Option's memory.
223                if *field_name == "Some" {
224                    // Find the Option frame (parent)
225                    let option_frame = if parent_path.is_empty() {
226                        let parent_index = start_depth.saturating_sub(1);
227                        self.frames_mut().get_mut(parent_index)
228                    } else if let Some(parent_frame) = stored_frames.get_mut(&parent_path) {
229                        Some(parent_frame)
230                    } else {
231                        let parent_frame_index = start_depth + parent_path.len() - 1;
232                        self.frames_mut().get_mut(parent_frame_index)
233                    };
234
235                    if let Some(option_frame) = option_frame {
236                        // The frame contains the inner value - write it into the Option's memory
237                        Self::complete_option_frame(option_frame, frame);
238                        // Frame data has been transferred to Option - don't drop it
239                        continue;
240                    }
241                }
242
243                if parent_path.is_empty() {
244                    // Parent is the frame that was current when deferred mode started.
245                    // It's at index (start_depth - 1) because deferred mode stores frames
246                    // relative to the position at start_depth.
247                    let parent_index = start_depth.saturating_sub(1);
248                    if let Some(root_frame) = self.frames_mut().get_mut(parent_index) {
249                        Self::mark_field_initialized(root_frame, field_name);
250                    }
251                } else {
252                    // Try stored_frames first
253                    if let Some(parent_frame) = stored_frames.get_mut(&parent_path) {
254                        Self::mark_field_initialized(parent_frame, field_name);
255                    } else {
256                        // Parent might still be on the frames stack (never ended).
257                        // The frame at index (start_depth + parent_path.len() - 1) should be the parent.
258                        let parent_frame_index = start_depth + parent_path.len() - 1;
259                        if let Some(parent_frame) = self.frames_mut().get_mut(parent_frame_index) {
260                            Self::mark_field_initialized(parent_frame, field_name);
261                        }
262                    }
263                }
264            }
265
266            // Frame is validated and parent is updated - dealloc if needed
267            frame.dealloc();
268        }
269
270        // Invariant check: we must have at least one frame after finish_deferred
271        if self.frames().is_empty() {
272            // No need to poison - returning Err consumes self, Drop will handle cleanup
273            return Err(ReflectError::InvariantViolation {
274                invariant: "finish_deferred() left Partial with no frames",
275            });
276        }
277
278        // Fill defaults and validate the root frame is fully initialized
279        if let Some(frame) = self.frames_mut().last_mut() {
280            // Fill defaults - this can fail if a field has #[facet(default)] but no default impl
281            frame.fill_defaults()?;
282            // Root validation failed. At this point, all stored frames have been
283            // processed and their parent isets updated.
284            // No need to poison - returning Err consumes self, Drop will handle cleanup
285            frame.require_full_initialization()?;
286        }
287
288        Ok(self)
289    }
290
291    /// Mark a field as initialized in a frame's tracker
292    fn mark_field_initialized(frame: &mut Frame, field_name: &str) {
293        if let Some(idx) = Self::find_field_index(frame, field_name) {
294            // If the tracker is Scalar but this is a struct type, upgrade to Struct tracker.
295            // This can happen if the frame was deinit'd (e.g., by a failed set_default)
296            // which resets the tracker to Scalar.
297            if matches!(frame.tracker, Tracker::Scalar)
298                && let Type::User(UserType::Struct(struct_type)) = frame.allocated.shape().ty
299            {
300                frame.tracker = Tracker::Struct {
301                    iset: ISet::new(struct_type.fields.len()),
302                    current_child: None,
303                };
304            }
305
306            match &mut frame.tracker {
307                Tracker::Struct { iset, .. } => {
308                    iset.set(idx);
309                }
310                Tracker::Enum { data, .. } => {
311                    data.set(idx);
312                }
313                Tracker::Array { iset, .. } => {
314                    iset.set(idx);
315                }
316                _ => {}
317            }
318        }
319    }
320
321    /// Complete an Option frame by writing the inner value and marking it initialized.
322    /// Used in finish_deferred when processing a stored frame at a path ending with "Some".
323    fn complete_option_frame(option_frame: &mut Frame, inner_frame: Frame) {
324        if let Def::Option(option_def) = option_frame.allocated.shape().def {
325            // Use the Option vtable to initialize Some(inner_value)
326            let init_some_fn = option_def.vtable.init_some;
327
328            // The inner frame contains the inner value
329            let inner_value_ptr = unsafe { inner_frame.data.assume_init().as_const() };
330
331            // Initialize the Option as Some(inner_value)
332            unsafe {
333                init_some_fn(option_frame.data, inner_value_ptr);
334            }
335
336            // Deallocate the inner value's memory since init_some_fn moved it
337            if let FrameOwnership::Owned = inner_frame.ownership
338                && let Ok(layout) = inner_frame.allocated.shape().layout.sized_layout()
339                && layout.size() > 0
340            {
341                unsafe {
342                    ::alloc::alloc::dealloc(inner_frame.data.as_mut_byte_ptr(), layout);
343                }
344            }
345
346            // Mark the Option as initialized
347            option_frame.tracker = Tracker::Option {
348                building_inner: false,
349            };
350            option_frame.is_init = true;
351        }
352    }
353
354    /// Find the field index for a given field name in a frame
355    fn find_field_index(frame: &Frame, field_name: &str) -> Option<usize> {
356        match frame.allocated.shape().ty {
357            Type::User(UserType::Struct(struct_type)) => {
358                struct_type.fields.iter().position(|f| f.name == field_name)
359            }
360            Type::User(UserType::Enum(_)) => {
361                if let Tracker::Enum { variant, .. } = &frame.tracker {
362                    variant
363                        .data
364                        .fields
365                        .iter()
366                        .position(|f| f.name == field_name)
367                } else {
368                    None
369                }
370            }
371            _ => None,
372        }
373    }
374
375    /// Pops the current frame off the stack, indicating we're done initializing the current field
376    pub fn end(mut self) -> Result<Self, ReflectError> {
377        if let Some(_frame) = self.frames().last() {
378            crate::trace!(
379                "end() called: shape={}, tracker={:?}, is_init={}",
380                _frame.allocated.shape(),
381                _frame.tracker.kind(),
382                _frame.is_init
383            );
384        }
385
386        // Special handling for SmartPointerSlice - convert builder to Arc
387        // Check if the current (top) frame is a SmartPointerSlice that needs conversion
388        let needs_slice_conversion = {
389            let frames = self.frames();
390            if frames.is_empty() {
391                false
392            } else {
393                let top_idx = frames.len() - 1;
394                crate::trace!(
395                    "end(): frames.len()={}, top tracker={:?}",
396                    frames.len(),
397                    frames[top_idx].tracker.kind()
398                );
399                matches!(
400                    frames[top_idx].tracker,
401                    Tracker::SmartPointerSlice {
402                        building_item: false,
403                        ..
404                    }
405                )
406            }
407        };
408
409        crate::trace!("end(): needs_slice_conversion={}", needs_slice_conversion);
410
411        if needs_slice_conversion {
412            let frames = self.frames_mut();
413            let top_idx = frames.len() - 1;
414
415            if let Tracker::SmartPointerSlice { vtable, .. } = &frames[top_idx].tracker {
416                // Convert the builder to Arc<[T]>
417                let vtable = *vtable;
418                let builder_ptr = unsafe { frames[top_idx].data.assume_init() };
419                let arc_ptr = unsafe { (vtable.convert_fn)(builder_ptr) };
420
421                match frames[top_idx].ownership {
422                    FrameOwnership::Field { field_idx } => {
423                        // Arc<[T]> is a field in a struct
424                        // The field frame's original data pointer was overwritten with the builder pointer,
425                        // so we need to reconstruct where the Arc should be written.
426
427                        // Get parent frame and field info
428                        let parent_idx = top_idx - 1;
429                        let parent_frame = &frames[parent_idx];
430                        let current_shape = frames[top_idx].allocated.shape();
431
432                        // Get the field to find its offset
433                        let field = if let Type::User(UserType::Struct(struct_type)) =
434                            parent_frame.allocated.shape().ty
435                        {
436                            &struct_type.fields[field_idx]
437                        } else {
438                            return Err(ReflectError::InvariantViolation {
439                                invariant: "SmartPointerSlice field frame parent must be a struct",
440                            });
441                        };
442
443                        // Calculate where the Arc should be written (parent.data + field.offset)
444                        let field_location =
445                            unsafe { parent_frame.data.field_uninit(field.offset) };
446
447                        // Write the Arc to the parent struct's field location
448                        let arc_layout = current_shape.layout.sized_layout().map_err(|_| {
449                            ReflectError::Unsized {
450                                shape: current_shape,
451                                operation: "SmartPointerSlice conversion requires sized Arc",
452                            }
453                        })?;
454                        let arc_size = arc_layout.size();
455                        unsafe {
456                            core::ptr::copy_nonoverlapping(
457                                arc_ptr.as_byte_ptr(),
458                                field_location.as_mut_byte_ptr(),
459                                arc_size,
460                            );
461                        }
462
463                        // Free the staging allocation from convert_fn (the Arc was copied to field_location)
464                        unsafe {
465                            ::alloc::alloc::dealloc(arc_ptr.as_byte_ptr() as *mut u8, arc_layout);
466                        }
467
468                        // Update the frame to point to the correct field location and mark as initialized
469                        frames[top_idx].data = field_location;
470                        frames[top_idx].tracker = Tracker::Scalar;
471                        frames[top_idx].is_init = true;
472
473                        // Return WITHOUT popping - the field frame will be popped by the next end() call
474                        return Ok(self);
475                    }
476                    FrameOwnership::Owned => {
477                        // Arc<[T]> is the root type or owned independently
478                        // The frame already has the allocation, we just need to update it with the Arc
479
480                        // The frame's data pointer is currently the builder, but we allocated
481                        // the Arc memory in the convert_fn. Update to point to the Arc.
482                        frames[top_idx].data = PtrUninit::new(arc_ptr.as_byte_ptr() as *mut u8);
483                        frames[top_idx].tracker = Tracker::Scalar;
484                        frames[top_idx].is_init = true;
485                        // Keep Owned ownership so Guard will properly deallocate
486
487                        // Return WITHOUT popping - the frame stays and will be built/dropped normally
488                        return Ok(self);
489                    }
490                    FrameOwnership::TrackedBuffer | FrameOwnership::BorrowedInPlace => {
491                        return Err(ReflectError::InvariantViolation {
492                            invariant: "SmartPointerSlice cannot have TrackedBuffer/BorrowedInPlace ownership after conversion",
493                        });
494                    }
495                }
496            }
497        }
498
499        if self.frames().len() <= 1 {
500            // Never pop the last/root frame - this indicates a broken state machine
501            // No need to poison - returning Err consumes self, Drop will handle cleanup
502            return Err(ReflectError::InvariantViolation {
503                invariant: "Partial::end() called with only one frame on the stack",
504            });
505        }
506
507        // In deferred mode, cannot pop below the start depth
508        if let Some(start_depth) = self.start_depth()
509            && self.frames().len() <= start_depth
510        {
511            // No need to poison - returning Err consumes self, Drop will handle cleanup
512            return Err(ReflectError::InvariantViolation {
513                invariant: "Partial::end() called but would pop below deferred start depth",
514            });
515        }
516
517        // Require that the top frame is fully initialized before popping.
518        // Skip this check in deferred mode - validation happens in finish_deferred().
519        // EXCEPT for collection items (map, list, set, option) which must be fully
520        // initialized before insertion/completion.
521        let requires_full_init = if !self.is_deferred() {
522            true
523        } else {
524            // In deferred mode, first check if this frame will be stored (tracked field).
525            // If so, skip full init check - the frame will be stored for later completion.
526            let is_tracked_frame = if let FrameMode::Deferred {
527                stack,
528                start_depth,
529                current_path,
530                ..
531            } = &self.mode
532            {
533                // Path depth should match the relative frame depth for a tracked field.
534                // frames.len() - start_depth should equal path.len() for tracked fields.
535                let relative_depth = stack.len() - *start_depth;
536                !current_path.is_empty() && current_path.len() == relative_depth
537            } else {
538                false
539            };
540
541            if is_tracked_frame {
542                // This frame will be stored in deferred mode - don't require full init
543                false
544            } else {
545                // Check if parent is a collection that requires fully initialized items
546                if self.frames().len() >= 2 {
547                    let frame_len = self.frames().len();
548                    let parent_frame = &self.frames()[frame_len - 2];
549                    matches!(
550                        parent_frame.tracker,
551                        Tracker::Map { .. }
552                            | Tracker::List { .. }
553                            | Tracker::Set { .. }
554                            | Tracker::Option { .. }
555                            | Tracker::Result { .. }
556                            | Tracker::DynamicValue {
557                                state: DynamicValueState::Array { .. }
558                            }
559                    )
560                } else {
561                    false
562                }
563            }
564        };
565
566        if requires_full_init {
567            // Fill defaults before checking full initialization
568            // This is important for structs in deferred mode that are children of collections
569            // (lists, maps, etc.) - they must be fully initialized before being inserted,
570            // but defaults should be applied first.
571            if self.is_deferred()
572                && let Some(frame) = self.frames_mut().last_mut()
573            {
574                crate::trace!(
575                    "end(): Filling defaults before full init check for {}, tracker={:?}",
576                    frame.allocated.shape(),
577                    frame.tracker.kind()
578                );
579                frame.fill_defaults()?;
580            }
581
582            let frame = self.frames().last().unwrap();
583            crate::trace!(
584                "end(): Checking full init for {}, tracker={:?}, is_init={}",
585                frame.allocated.shape(),
586                frame.tracker.kind(),
587                frame.is_init
588            );
589            let result = frame.require_full_initialization();
590            crate::trace!(
591                "end(): require_full_initialization result: {:?}",
592                result.is_ok()
593            );
594            result?
595        }
596
597        // Pop the frame and save its data pointer for SmartPointer handling
598        let mut popped_frame = self.frames_mut().pop().unwrap();
599
600        // In deferred mode, store the frame for potential re-entry and skip
601        // the normal parent-updating logic. The frame will be finalized later
602        // in finish_deferred().
603        //
604        // We only store if the path depth matches the frame depth, meaning we're
605        // ending a tracked struct/enum field, not something like begin_some()
606        // or a field inside a collection item.
607        if let FrameMode::Deferred {
608            stack,
609            start_depth,
610            current_path,
611            stored_frames,
612            ..
613        } = &mut self.mode
614        {
615            // Path depth should match the relative frame depth for a tracked field.
616            // After popping: frames.len() - start_depth + 1 should equal path.len()
617            // for fields entered via begin_field (not begin_some/begin_inner).
618            let relative_depth = stack.len() - *start_depth + 1;
619            let is_tracked_field = !current_path.is_empty() && current_path.len() == relative_depth;
620
621            if is_tracked_field {
622                trace!(
623                    "end(): Storing frame for deferred path {:?}, shape {}",
624                    current_path,
625                    popped_frame.allocated.shape()
626                );
627
628                // Store the frame at the current path
629                let path = current_path.clone();
630                stored_frames.insert(path, popped_frame);
631
632                // Pop from current_path
633                current_path.pop();
634
635                // Clear parent's current_child tracking
636                if let Some(parent_frame) = stack.last_mut() {
637                    parent_frame.tracker.clear_current_child();
638                }
639
640                return Ok(self);
641            }
642        }
643
644        // check if this needs deserialization from a different shape
645        if popped_frame.using_custom_deserialization {
646            // Check for field-level proxy first, then fall back to shape-level proxy
647            let deserialize_with: Option<facet_core::ProxyConvertInFn> = self
648                .parent_field()
649                .and_then(|f| f.proxy().map(|p| p.convert_in));
650
651            // Fall back to shape-level proxy stored in the frame
652            let deserialize_with =
653                deserialize_with.or_else(|| popped_frame.shape_level_proxy.map(|p| p.convert_in));
654
655            if let Some(deserialize_with) = deserialize_with {
656                let parent_frame = self.frames_mut().last_mut().unwrap();
657
658                trace!(
659                    "Detected custom conversion needed from {} to {}",
660                    popped_frame.allocated.shape(),
661                    parent_frame.allocated.shape()
662                );
663
664                unsafe {
665                    let res = {
666                        let inner_value_ptr = popped_frame.data.assume_init().as_const();
667                        (deserialize_with)(inner_value_ptr, parent_frame.data)
668                    };
669                    let popped_frame_shape = popped_frame.allocated.shape();
670
671                    // Note: We do NOT call deinit() here because deserialize_with uses
672                    // ptr::read to take ownership of the source value. Calling deinit()
673                    // would cause a double-free. We mark is_init as false to satisfy
674                    // dealloc()'s assertion, then deallocate the memory.
675                    popped_frame.is_init = false;
676                    popped_frame.dealloc();
677                    let rptr = res.map_err(|message| ReflectError::CustomDeserializationError {
678                        message,
679                        src_shape: popped_frame_shape,
680                        dst_shape: parent_frame.allocated.shape(),
681                    })?;
682                    if rptr.as_uninit() != parent_frame.data {
683                        return Err(ReflectError::CustomDeserializationError {
684                            message: "deserialize_with did not return the expected pointer".into(),
685                            src_shape: popped_frame_shape,
686                            dst_shape: parent_frame.allocated.shape(),
687                        });
688                    }
689                    parent_frame.mark_as_init();
690                }
691                return Ok(self);
692            }
693        }
694
695        // Update parent frame's tracking when popping from a child
696        // Capture deferred state before borrowing frames mutably
697        let is_deferred = self.is_deferred();
698        let parent_frame = self.frames_mut().last_mut().unwrap();
699
700        crate::trace!(
701            "end(): Popped {} (tracker {:?}), Parent {} (tracker {:?})",
702            popped_frame.allocated.shape(),
703            popped_frame.tracker.kind(),
704            parent_frame.allocated.shape(),
705            parent_frame.tracker.kind()
706        );
707
708        // Check if we need to do a conversion - this happens when:
709        // 1. The parent frame has a builder_shape or inner type that matches the popped frame's shape
710        // 2. The parent frame has try_from
711        // 3. The parent frame is not yet initialized
712        // 4. The parent frame's tracker is Scalar (not Option, SmartPointer, etc.)
713        //    This ensures we only do conversion when begin_inner was used, not begin_some
714        let needs_conversion = !parent_frame.is_init
715            && matches!(parent_frame.tracker, Tracker::Scalar)
716            && ((parent_frame.allocated.shape().builder_shape.is_some()
717                && parent_frame.allocated.shape().builder_shape.unwrap()
718                    == popped_frame.allocated.shape())
719                || (parent_frame.allocated.shape().inner.is_some()
720                    && parent_frame.allocated.shape().inner.unwrap()
721                        == popped_frame.allocated.shape()))
722            && match parent_frame.allocated.shape().vtable {
723                facet_core::VTableErased::Direct(vt) => vt.try_from.is_some(),
724                facet_core::VTableErased::Indirect(vt) => vt.try_from.is_some(),
725            };
726
727        if needs_conversion {
728            trace!(
729                "Detected implicit conversion needed from {} to {}",
730                popped_frame.allocated.shape(),
731                parent_frame.allocated.shape()
732            );
733
734            // The conversion requires the source frame to be fully initialized
735            // (we're about to call assume_init() and pass to try_from)
736            if let Err(e) = popped_frame.require_full_initialization() {
737                // Deallocate the memory since the frame wasn't fully initialized
738                if let FrameOwnership::Owned = popped_frame.ownership
739                    && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
740                    && layout.size() > 0
741                {
742                    trace!(
743                        "Deallocating uninitialized conversion frame memory: size={}, align={}",
744                        layout.size(),
745                        layout.align()
746                    );
747                    unsafe {
748                        ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
749                    }
750                }
751                return Err(e);
752            }
753
754            // Perform the conversion
755            let inner_ptr = unsafe { popped_frame.data.assume_init().as_const() };
756            let inner_shape = popped_frame.allocated.shape();
757
758            trace!(
759                "Converting from {} to {}",
760                inner_shape,
761                parent_frame.allocated.shape()
762            );
763
764            // Handle Direct and Indirect vtables differently due to different return types
765            let result = match parent_frame.allocated.shape().vtable {
766                facet_core::VTableErased::Direct(vt) => {
767                    if let Some(try_from_fn) = vt.try_from {
768                        unsafe {
769                            try_from_fn(
770                                parent_frame.data.as_mut_byte_ptr() as *mut (),
771                                inner_shape,
772                                inner_ptr,
773                            )
774                        }
775                    } else {
776                        return Err(ReflectError::OperationFailed {
777                            shape: parent_frame.allocated.shape(),
778                            operation: "try_from not available for this type",
779                        });
780                    }
781                }
782                facet_core::VTableErased::Indirect(vt) => {
783                    if let Some(try_from_fn) = vt.try_from {
784                        // SAFETY: assume_init is valid because we're in a state where the
785                        // parent frame has been initialized, and the shape matches.
786                        let ox_mut = unsafe {
787                            facet_core::OxMut::new(
788                                parent_frame.data.assume_init(),
789                                parent_frame.allocated.shape(),
790                            )
791                        };
792                        match unsafe { try_from_fn(ox_mut.into(), inner_shape, inner_ptr) } {
793                            Some(result) => result,
794                            None => {
795                                return Err(ReflectError::OperationFailed {
796                                    shape: parent_frame.allocated.shape(),
797                                    operation: "try_from not available for inner type",
798                                });
799                            }
800                        }
801                    } else {
802                        return Err(ReflectError::OperationFailed {
803                            shape: parent_frame.allocated.shape(),
804                            operation: "try_from not available for this type",
805                        });
806                    }
807                }
808            };
809
810            if let Err(e) = result {
811                trace!("Conversion failed: {e:?}");
812
813                // Deallocate the inner value's memory since conversion failed
814                if let FrameOwnership::Owned = popped_frame.ownership
815                    && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
816                    && layout.size() > 0
817                {
818                    trace!(
819                        "Deallocating conversion frame memory after failure: size={}, align={}",
820                        layout.size(),
821                        layout.align()
822                    );
823                    unsafe {
824                        ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
825                    }
826                }
827
828                return Err(ReflectError::TryFromError {
829                    src_shape: inner_shape,
830                    dst_shape: parent_frame.allocated.shape(),
831                    inner: facet_core::TryFromError::Generic(e),
832                });
833            }
834
835            trace!("Conversion succeeded, marking parent as initialized");
836            parent_frame.is_init = true;
837
838            // Deallocate the inner value's memory since try_from consumed it
839            if let FrameOwnership::Owned = popped_frame.ownership
840                && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
841                && layout.size() > 0
842            {
843                trace!(
844                    "Deallocating conversion frame memory: size={}, align={}",
845                    layout.size(),
846                    layout.align()
847                );
848                unsafe {
849                    ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
850                }
851            }
852
853            return Ok(self);
854        }
855
856        // For Field-owned frames, reclaim responsibility in parent's tracker
857        // Only mark as initialized if the child frame was actually initialized.
858        // This prevents double-free when begin_inner/begin_some drops a value via
859        // prepare_for_reinitialization but then fails, leaving the child uninitialized.
860        //
861        // We use require_full_initialization() rather than just is_init because:
862        // - Scalar frames use is_init as the source of truth
863        // - Struct/Array/Enum frames use their iset/data as the source of truth
864        //   (is_init may never be set to true for these tracker types)
865        if let FrameOwnership::Field { field_idx } = popped_frame.ownership {
866            // In deferred mode, fill defaults on the child frame before checking initialization.
867            // This handles cases like struct fields inside enum variants inside map values -
868            // the struct's optional fields need defaults filled before we can mark the field
869            // as initialized in the parent enum.
870            if is_deferred {
871                popped_frame.fill_defaults()?;
872            }
873            let child_is_initialized = popped_frame.require_full_initialization().is_ok();
874            match &mut parent_frame.tracker {
875                Tracker::Struct {
876                    iset,
877                    current_child,
878                } => {
879                    if child_is_initialized {
880                        iset.set(field_idx); // Parent reclaims responsibility only if child was init
881                    }
882                    *current_child = None;
883                }
884                Tracker::Array {
885                    iset,
886                    current_child,
887                } => {
888                    if child_is_initialized {
889                        iset.set(field_idx); // Parent reclaims responsibility only if child was init
890                    }
891                    *current_child = None;
892                }
893                Tracker::Enum {
894                    data,
895                    current_child,
896                    ..
897                } => {
898                    if child_is_initialized {
899                        data.set(field_idx); // Parent reclaims responsibility only if child was init
900                    }
901                    *current_child = None;
902                }
903                _ => {}
904            }
905            return Ok(self);
906        }
907
908        match &mut parent_frame.tracker {
909            Tracker::SmartPointer => {
910                // We just popped the inner value frame, so now we need to create the smart pointer
911                if let Def::Pointer(smart_ptr_def) = parent_frame.allocated.shape().def {
912                    // The inner value must be fully initialized before we can create the smart pointer
913                    if let Err(e) = popped_frame.require_full_initialization() {
914                        // Inner value wasn't initialized, deallocate and return error
915                        popped_frame.deinit();
916                        popped_frame.dealloc();
917                        return Err(e);
918                    }
919
920                    let Some(new_into_fn) = smart_ptr_def.vtable.new_into_fn else {
921                        popped_frame.deinit();
922                        popped_frame.dealloc();
923                        return Err(ReflectError::OperationFailed {
924                            shape: parent_frame.allocated.shape(),
925                            operation: "SmartPointer missing new_into_fn",
926                        });
927                    };
928
929                    // The child frame contained the inner value
930                    let inner_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
931
932                    // Use new_into_fn to create the Box
933                    unsafe {
934                        new_into_fn(parent_frame.data, inner_ptr);
935                    }
936
937                    // We just moved out of it
938                    popped_frame.tracker = Tracker::Scalar;
939                    popped_frame.is_init = false;
940
941                    // Deallocate the inner value's memory since new_into_fn moved it
942                    popped_frame.dealloc();
943
944                    parent_frame.is_init = true;
945                }
946            }
947            Tracker::List { current_child } if parent_frame.is_init => {
948                if *current_child {
949                    // We just popped an element frame, now push it to the list
950                    if let Def::List(list_def) = parent_frame.allocated.shape().def {
951                        let Some(push_fn) = list_def.push() else {
952                            return Err(ReflectError::OperationFailed {
953                                shape: parent_frame.allocated.shape(),
954                                operation: "List missing push function",
955                            });
956                        };
957
958                        // The child frame contained the element value
959                        let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
960
961                        // Use push to add element to the list
962                        unsafe {
963                            push_fn(
964                                PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
965                                element_ptr,
966                            );
967                        }
968
969                        // Push moved out of popped_frame
970                        popped_frame.tracker = Tracker::Scalar;
971                        popped_frame.is_init = false;
972                        popped_frame.dealloc();
973
974                        *current_child = false;
975                    }
976                }
977            }
978            Tracker::Map { insert_state } if parent_frame.is_init => {
979                match insert_state {
980                    MapInsertState::PushingKey { key_ptr, .. } => {
981                        // We just popped the key frame - mark key as initialized and transition
982                        // to PushingValue state. key_frame_on_stack = false because the frame
983                        // was just popped, so Map now owns the key buffer.
984                        *insert_state = MapInsertState::PushingValue {
985                            key_ptr: *key_ptr,
986                            value_ptr: None,
987                            value_initialized: false,
988                            value_frame_on_stack: false, // No value frame yet
989                        };
990                    }
991                    MapInsertState::PushingValue {
992                        key_ptr, value_ptr, ..
993                    } => {
994                        // We just popped the value frame, now insert the pair
995                        if let (Some(value_ptr), Def::Map(map_def)) =
996                            (value_ptr, parent_frame.allocated.shape().def)
997                        {
998                            let insert = map_def.vtable.insert;
999
1000                            // Use insert to add key-value pair to the map
1001                            unsafe {
1002                                insert(
1003                                    PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
1004                                    PtrMut::new(key_ptr.as_mut_byte_ptr()),
1005                                    PtrMut::new(value_ptr.as_mut_byte_ptr()),
1006                                );
1007                            }
1008
1009                            // Note: We don't deallocate the key and value memory here.
1010                            // The insert function has semantically moved the values into the map,
1011                            // but we still need to deallocate the temporary buffers.
1012                            // However, since we don't have frames for them anymore (they were popped),
1013                            // we need to handle deallocation here.
1014                            if let Ok(key_shape) = map_def.k().layout.sized_layout()
1015                                && key_shape.size() > 0
1016                            {
1017                                unsafe {
1018                                    ::alloc::alloc::dealloc(key_ptr.as_mut_byte_ptr(), key_shape);
1019                                }
1020                            }
1021                            if let Ok(value_shape) = map_def.v().layout.sized_layout()
1022                                && value_shape.size() > 0
1023                            {
1024                                unsafe {
1025                                    ::alloc::alloc::dealloc(
1026                                        value_ptr.as_mut_byte_ptr(),
1027                                        value_shape,
1028                                    );
1029                                }
1030                            }
1031
1032                            // Reset to idle state
1033                            *insert_state = MapInsertState::Idle;
1034                        }
1035                    }
1036                    MapInsertState::Idle => {
1037                        // Nothing to do
1038                    }
1039                }
1040            }
1041            Tracker::Set { current_child } if parent_frame.is_init => {
1042                if *current_child {
1043                    // We just popped an element frame, now insert it into the set
1044                    if let Def::Set(set_def) = parent_frame.allocated.shape().def {
1045                        let insert = set_def.vtable.insert;
1046
1047                        // The child frame contained the element value
1048                        let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
1049
1050                        // Use insert to add element to the set
1051                        unsafe {
1052                            insert(
1053                                PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
1054                                element_ptr,
1055                            );
1056                        }
1057
1058                        // Insert moved out of popped_frame
1059                        popped_frame.tracker = Tracker::Scalar;
1060                        popped_frame.is_init = false;
1061                        popped_frame.dealloc();
1062
1063                        *current_child = false;
1064                    }
1065                }
1066            }
1067            Tracker::Option { building_inner } => {
1068                crate::trace!(
1069                    "end(): matched Tracker::Option, building_inner={}",
1070                    *building_inner
1071                );
1072                // We just popped the inner value frame for an Option's Some variant
1073                if *building_inner {
1074                    if let Def::Option(option_def) = parent_frame.allocated.shape().def {
1075                        // Use the Option vtable to initialize Some(inner_value)
1076                        let init_some_fn = option_def.vtable.init_some;
1077
1078                        // The popped frame contains the inner value
1079                        let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
1080
1081                        // Initialize the Option as Some(inner_value)
1082                        unsafe {
1083                            init_some_fn(parent_frame.data, inner_value_ptr);
1084                        }
1085
1086                        // Deallocate the inner value's memory since init_some_fn moved it
1087                        if let FrameOwnership::Owned = popped_frame.ownership
1088                            && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
1089                            && layout.size() > 0
1090                        {
1091                            unsafe {
1092                                ::alloc::alloc::dealloc(
1093                                    popped_frame.data.as_mut_byte_ptr(),
1094                                    layout,
1095                                );
1096                            }
1097                        }
1098
1099                        // Mark that we're no longer building the inner value
1100                        *building_inner = false;
1101                        crate::trace!("end(): set building_inner to false");
1102                        // Mark the Option as initialized
1103                        parent_frame.is_init = true;
1104                        crate::trace!("end(): set parent_frame.is_init to true");
1105                    } else {
1106                        return Err(ReflectError::OperationFailed {
1107                            shape: parent_frame.allocated.shape(),
1108                            operation: "Option frame without Option definition",
1109                        });
1110                    }
1111                } else {
1112                    // building_inner is false - the Option was already initialized but
1113                    // begin_some was called again. The popped frame was not used to
1114                    // initialize the Option, so we need to clean it up.
1115                    popped_frame.deinit();
1116                    if let FrameOwnership::Owned = popped_frame.ownership
1117                        && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
1118                        && layout.size() > 0
1119                    {
1120                        unsafe {
1121                            ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
1122                        }
1123                    }
1124                }
1125            }
1126            Tracker::Result {
1127                is_ok,
1128                building_inner,
1129            } => {
1130                crate::trace!(
1131                    "end(): matched Tracker::Result, is_ok={}, building_inner={}",
1132                    *is_ok,
1133                    *building_inner
1134                );
1135                // We just popped the inner value frame for a Result's Ok or Err variant
1136                if *building_inner {
1137                    if let Def::Result(result_def) = parent_frame.allocated.shape().def {
1138                        // The popped frame contains the inner value
1139                        let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
1140
1141                        // Initialize the Result as Ok(inner_value) or Err(inner_value)
1142                        if *is_ok {
1143                            let init_ok_fn = result_def.vtable.init_ok;
1144                            unsafe {
1145                                init_ok_fn(parent_frame.data, inner_value_ptr);
1146                            }
1147                        } else {
1148                            let init_err_fn = result_def.vtable.init_err;
1149                            unsafe {
1150                                init_err_fn(parent_frame.data, inner_value_ptr);
1151                            }
1152                        }
1153
1154                        // Deallocate the inner value's memory since init_ok/err_fn moved it
1155                        if let FrameOwnership::Owned = popped_frame.ownership
1156                            && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
1157                            && layout.size() > 0
1158                        {
1159                            unsafe {
1160                                ::alloc::alloc::dealloc(
1161                                    popped_frame.data.as_mut_byte_ptr(),
1162                                    layout,
1163                                );
1164                            }
1165                        }
1166
1167                        // Mark that we're no longer building the inner value
1168                        *building_inner = false;
1169                        crate::trace!("end(): set building_inner to false");
1170                        // Mark the Result as initialized
1171                        parent_frame.is_init = true;
1172                        crate::trace!("end(): set parent_frame.is_init to true");
1173                    } else {
1174                        return Err(ReflectError::OperationFailed {
1175                            shape: parent_frame.allocated.shape(),
1176                            operation: "Result frame without Result definition",
1177                        });
1178                    }
1179                } else {
1180                    // building_inner is false - the Result was already initialized but
1181                    // begin_ok/begin_err was called again. The popped frame was not used to
1182                    // initialize the Result, so we need to clean it up.
1183                    popped_frame.deinit();
1184                    if let FrameOwnership::Owned = popped_frame.ownership
1185                        && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
1186                        && layout.size() > 0
1187                    {
1188                        unsafe {
1189                            ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
1190                        }
1191                    }
1192                }
1193            }
1194            Tracker::Scalar => {
1195                // the main case here is: the popped frame was a `String` and the
1196                // parent frame is an `Arc<str>`, `Box<str>` etc.
1197                match &parent_frame.allocated.shape().def {
1198                    Def::Pointer(smart_ptr_def) => {
1199                        let pointee =
1200                            smart_ptr_def
1201                                .pointee()
1202                                .ok_or(ReflectError::InvariantViolation {
1203                                    invariant: "pointer type doesn't have a pointee",
1204                                })?;
1205
1206                        if !pointee.is_shape(str::SHAPE) {
1207                            return Err(ReflectError::InvariantViolation {
1208                                invariant: "only T=str is supported when building SmartPointer<T> and T is unsized",
1209                            });
1210                        }
1211
1212                        if !popped_frame.allocated.shape().is_shape(String::SHAPE) {
1213                            return Err(ReflectError::InvariantViolation {
1214                                invariant: "the popped frame should be String when building a SmartPointer<T>",
1215                            });
1216                        }
1217
1218                        popped_frame.require_full_initialization()?;
1219
1220                        // if the just-popped frame was a SmartPointerStr, we have some conversion to do:
1221                        // Special-case: SmartPointer<str> (Box<str>, Arc<str>, Rc<str>) via SmartPointerStr tracker
1222                        // Here, popped_frame actually contains a value for String that should be moved into the smart pointer.
1223                        // We convert the String into Box<str>, Arc<str>, or Rc<str> as appropriate and write it to the parent frame.
1224                        use ::alloc::{rc::Rc, string::String, sync::Arc};
1225                        let parent_shape = parent_frame.allocated.shape();
1226
1227                        let Some(known) = smart_ptr_def.known else {
1228                            return Err(ReflectError::OperationFailed {
1229                                shape: parent_shape,
1230                                operation: "SmartPointerStr for unknown smart pointer kind",
1231                            });
1232                        };
1233
1234                        parent_frame.deinit();
1235
1236                        // Interpret the memory as a String, then convert and write.
1237                        let string_ptr = popped_frame.data.as_mut_byte_ptr() as *mut String;
1238                        let string_value = unsafe { core::ptr::read(string_ptr) };
1239
1240                        match known {
1241                            KnownPointer::Box => {
1242                                let boxed: Box<str> = string_value.into_boxed_str();
1243                                unsafe {
1244                                    core::ptr::write(
1245                                        parent_frame.data.as_mut_byte_ptr() as *mut Box<str>,
1246                                        boxed,
1247                                    );
1248                                }
1249                            }
1250                            KnownPointer::Arc => {
1251                                let arc: Arc<str> = Arc::from(string_value.into_boxed_str());
1252                                unsafe {
1253                                    core::ptr::write(
1254                                        parent_frame.data.as_mut_byte_ptr() as *mut Arc<str>,
1255                                        arc,
1256                                    );
1257                                }
1258                            }
1259                            KnownPointer::Rc => {
1260                                let rc: Rc<str> = Rc::from(string_value.into_boxed_str());
1261                                unsafe {
1262                                    core::ptr::write(
1263                                        parent_frame.data.as_mut_byte_ptr() as *mut Rc<str>,
1264                                        rc,
1265                                    );
1266                                }
1267                            }
1268                            _ => {
1269                                return Err(ReflectError::OperationFailed {
1270                                    shape: parent_shape,
1271                                    operation: "Don't know how to build this pointer type",
1272                                });
1273                            }
1274                        }
1275
1276                        parent_frame.is_init = true;
1277
1278                        popped_frame.tracker = Tracker::Scalar;
1279                        popped_frame.is_init = false;
1280                        popped_frame.dealloc();
1281                    }
1282                    _ => {
1283                        // This can happen if begin_inner() was called on a type that
1284                        // has shape.inner but isn't a SmartPointer (e.g., Option).
1285                        // In this case, we can't complete the conversion, so return error.
1286                        return Err(ReflectError::OperationFailed {
1287                            shape: parent_frame.allocated.shape(),
1288                            operation: "end() called but parent has Uninit/Init tracker and isn't a SmartPointer",
1289                        });
1290                    }
1291                }
1292            }
1293            Tracker::SmartPointerSlice {
1294                vtable,
1295                building_item,
1296            } => {
1297                if *building_item {
1298                    // We just popped an element frame, now push it to the slice builder
1299                    let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
1300
1301                    // Use the slice builder's push_fn to add the element
1302                    crate::trace!("Pushing element to slice builder");
1303                    unsafe {
1304                        let parent_ptr = parent_frame.data.assume_init();
1305                        (vtable.push_fn)(parent_ptr, element_ptr);
1306                    }
1307
1308                    popped_frame.tracker = Tracker::Scalar;
1309                    popped_frame.is_init = false;
1310                    popped_frame.dealloc();
1311
1312                    if let Tracker::SmartPointerSlice {
1313                        building_item: bi, ..
1314                    } = &mut parent_frame.tracker
1315                    {
1316                        *bi = false;
1317                    }
1318                }
1319            }
1320            Tracker::DynamicValue {
1321                state: DynamicValueState::Array { building_element },
1322            } => {
1323                if *building_element {
1324                    // Check that the element is initialized before pushing
1325                    if !popped_frame.is_init {
1326                        // Element was never set - clean up and return error
1327                        let shape = parent_frame.allocated.shape();
1328                        popped_frame.dealloc();
1329                        *building_element = false;
1330                        // No need to poison - returning Err consumes self, Drop will handle cleanup
1331                        return Err(ReflectError::OperationFailed {
1332                            shape,
1333                            operation: "end() called but array element was never initialized",
1334                        });
1335                    }
1336
1337                    // We just popped an element frame, now push it to the dynamic array
1338                    if let Def::DynamicValue(dyn_def) = parent_frame.allocated.shape().def {
1339                        // Get mutable pointers - both array and element need PtrMut
1340                        let array_ptr = unsafe { parent_frame.data.assume_init() };
1341                        let element_ptr = unsafe { popped_frame.data.assume_init() };
1342
1343                        // Use push_array_element to add element to the array
1344                        unsafe {
1345                            (dyn_def.vtable.push_array_element)(array_ptr, element_ptr);
1346                        }
1347
1348                        // Push moved out of popped_frame
1349                        popped_frame.tracker = Tracker::Scalar;
1350                        popped_frame.is_init = false;
1351                        popped_frame.dealloc();
1352
1353                        *building_element = false;
1354                    }
1355                }
1356            }
1357            Tracker::DynamicValue {
1358                state: DynamicValueState::Object { insert_state },
1359            } => {
1360                if let DynamicObjectInsertState::BuildingValue { key } = insert_state {
1361                    // Check that the value is initialized before inserting
1362                    if !popped_frame.is_init {
1363                        // Value was never set - clean up and return error
1364                        let shape = parent_frame.allocated.shape();
1365                        popped_frame.dealloc();
1366                        *insert_state = DynamicObjectInsertState::Idle;
1367                        // No need to poison - returning Err consumes self, Drop will handle cleanup
1368                        return Err(ReflectError::OperationFailed {
1369                            shape,
1370                            operation: "end() called but object entry value was never initialized",
1371                        });
1372                    }
1373
1374                    // We just popped a value frame, now insert it into the dynamic object
1375                    if let Def::DynamicValue(dyn_def) = parent_frame.allocated.shape().def {
1376                        // Get mutable pointers - both object and value need PtrMut
1377                        let object_ptr = unsafe { parent_frame.data.assume_init() };
1378                        let value_ptr = unsafe { popped_frame.data.assume_init() };
1379
1380                        // Use insert_object_entry to add the key-value pair
1381                        unsafe {
1382                            (dyn_def.vtable.insert_object_entry)(object_ptr, key, value_ptr);
1383                        }
1384
1385                        // Insert moved out of popped_frame
1386                        popped_frame.tracker = Tracker::Scalar;
1387                        popped_frame.is_init = false;
1388                        popped_frame.dealloc();
1389
1390                        // Reset insert state to Idle
1391                        *insert_state = DynamicObjectInsertState::Idle;
1392                    }
1393                }
1394            }
1395            _ => {}
1396        }
1397
1398        Ok(self)
1399    }
1400
1401    /// Returns a human-readable path representing the current traversal in the builder,
1402    /// e.g., `RootStruct.fieldName[index].subfield`.
1403    pub fn path(&self) -> String {
1404        let mut out = String::new();
1405
1406        let mut path_components = Vec::new();
1407        // The stack of enum/struct/sequence names currently in context.
1408        // Start from root and build upwards.
1409        for (i, frame) in self.frames().iter().enumerate() {
1410            match frame.allocated.shape().ty {
1411                Type::User(user_type) => match user_type {
1412                    UserType::Struct(struct_type) => {
1413                        // Try to get currently active field index
1414                        let mut field_str = None;
1415                        if let Tracker::Struct {
1416                            current_child: Some(idx),
1417                            ..
1418                        } = &frame.tracker
1419                            && let Some(field) = struct_type.fields.get(*idx)
1420                        {
1421                            field_str = Some(field.name);
1422                        }
1423                        if i == 0 {
1424                            // Use Display for the root struct shape
1425                            path_components.push(format!("{}", frame.allocated.shape()));
1426                        }
1427                        if let Some(field_name) = field_str {
1428                            path_components.push(format!(".{field_name}"));
1429                        }
1430                    }
1431                    UserType::Enum(_enum_type) => {
1432                        // Try to get currently active variant and field
1433                        if let Tracker::Enum {
1434                            variant,
1435                            current_child,
1436                            ..
1437                        } = &frame.tracker
1438                        {
1439                            if i == 0 {
1440                                // Use Display for the root enum shape
1441                                path_components.push(format!("{}", frame.allocated.shape()));
1442                            }
1443                            path_components.push(format!("::{}", variant.name));
1444                            if let Some(idx) = *current_child
1445                                && let Some(field) = variant.data.fields.get(idx)
1446                            {
1447                                path_components.push(format!(".{}", field.name));
1448                            }
1449                        } else if i == 0 {
1450                            // just the enum display
1451                            path_components.push(format!("{}", frame.allocated.shape()));
1452                        }
1453                    }
1454                    UserType::Union(_union_type) => {
1455                        path_components.push(format!("{}", frame.allocated.shape()));
1456                    }
1457                    UserType::Opaque => {
1458                        path_components.push("<opaque>".to_string());
1459                    }
1460                },
1461                Type::Sequence(seq_type) => match seq_type {
1462                    facet_core::SequenceType::Array(_array_def) => {
1463                        // Try to show current element index
1464                        if let Tracker::Array {
1465                            current_child: Some(idx),
1466                            ..
1467                        } = &frame.tracker
1468                        {
1469                            path_components.push(format!("[{idx}]"));
1470                        }
1471                    }
1472                    // You can add more for Slice, Vec, etc., if applicable
1473                    _ => {
1474                        // just indicate "[]" for sequence
1475                        path_components.push("[]".to_string());
1476                    }
1477                },
1478                Type::Pointer(_) => {
1479                    // Indicate deref
1480                    path_components.push("*".to_string());
1481                }
1482                _ => {
1483                    // No structural path
1484                }
1485            }
1486        }
1487        // Merge the path_components into a single string
1488        for component in path_components {
1489            out.push_str(&component);
1490        }
1491        out
1492    }
1493
1494    /// Get the field for the parent frame
1495    pub fn parent_field(&self) -> Option<&Field> {
1496        self.frames()
1497            .iter()
1498            .rev()
1499            .nth(1)
1500            .and_then(|f| f.get_field())
1501    }
1502
1503    /// Gets the field for the current frame
1504    pub fn current_field(&self) -> Option<&Field> {
1505        self.frames().last().and_then(|f| f.get_field())
1506    }
1507
1508    /// Returns a const pointer to the current frame's data.
1509    ///
1510    /// This is useful for validation - after deserializing a field value,
1511    /// validators can read the value through this pointer.
1512    ///
1513    /// # Safety
1514    ///
1515    /// The returned pointer is valid only while the frame exists.
1516    /// The caller must ensure the frame is fully initialized before
1517    /// reading through this pointer.
1518    pub fn data_ptr(&self) -> Option<facet_core::PtrConst> {
1519        if self.state != PartialState::Active {
1520            return None;
1521        }
1522        self.frames().last().map(|f| {
1523            // SAFETY: We're in active state, so the frame is valid.
1524            // The caller is responsible for ensuring the data is initialized.
1525            unsafe { f.data.assume_init().as_const() }
1526        })
1527    }
1528}