facet_reflect/partial/
mod.rs

1//! Partial value construction for dynamic reflection
2//!
3//! This module provides APIs for incrementally building values through reflection,
4//! particularly useful when deserializing data from external formats like JSON or YAML.
5//!
6//! # Overview
7//!
8//! The `Partial` type (formerly known as `Wip` - Work In Progress) allows you to:
9//! - Allocate memory for a value based on its `Shape`
10//! - Initialize fields incrementally in a type-safe manner
11//! - Handle complex nested structures including structs, enums, collections, and smart pointers
12//! - Build the final value once all required fields are initialized
13//!
14//! **Note**: This is the only API for partial value construction. The previous `TypedPartial`
15//! wrapper has been removed in favor of using `Partial` directly.
16//!
17//! # Basic Usage
18//!
19//! ```no_run
20//! # use facet_reflect::Partial;
21//! # use facet_core::{Shape, Facet};
22//! # fn example<T: Facet<'static>>() -> Result<(), Box<dyn std::error::Error>> {
23//! // Allocate memory for a struct
24//! let mut partial = Partial::alloc::<T>()?;
25//!
26//! // Set simple fields
27//! partial = partial.set_field("name", "Alice")?;
28//! partial = partial.set_field("age", 30u32)?;
29//!
30//! // Work with nested structures
31//! partial = partial.begin_field("address")?;
32//! partial = partial.set_field("street", "123 Main St")?;
33//! partial = partial.set_field("city", "Springfield")?;
34//! partial = partial.end()?;
35//!
36//! // Build the final value
37//! let value = partial.build()?;
38//! # Ok(())
39//! # }
40//! ```
41//!
42//! # Chaining Style
43//!
44//! The API supports method chaining for cleaner code:
45//!
46//! ```no_run
47//! # use facet_reflect::Partial;
48//! # use facet_core::{Shape, Facet};
49//! # fn example<T: Facet<'static>>() -> Result<(), Box<dyn std::error::Error>> {
50//! let value = Partial::alloc::<T>()?
51//!     .set_field("name", "Bob")?
52//!     .begin_field("scores")?
53//!         .set(vec![95, 87, 92])?
54//!     .end()?
55//!     .build()?;
56//! # Ok(())
57//! # }
58//! ```
59//!
60//! # Working with Collections
61//!
62//! ```no_run
63//! # use facet_reflect::Partial;
64//! # use facet_core::{Shape, Facet};
65//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
66//! let mut partial = Partial::alloc::<Vec<String>>()?;
67//!
68//! // Add items to a list
69//! partial = partial.begin_list_item()?;
70//! partial = partial.set("first")?;
71//! partial = partial.end()?;
72//!
73//! partial = partial.begin_list_item()?;
74//! partial = partial.set("second")?;
75//! partial = partial.end()?;
76//!
77//! let vec = partial.build()?;
78//! # Ok(())
79//! # }
80//! ```
81//!
82//! # Working with Maps
83//!
84//! ```no_run
85//! # use facet_reflect::Partial;
86//! # use facet_core::{Shape, Facet};
87//! # use std::collections::HashMap;
88//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
89//! let mut partial = Partial::alloc::<HashMap<String, i32>>()?;
90//!
91//! // Insert key-value pairs
92//! partial = partial.begin_key()?;
93//! partial = partial.set("score")?;
94//! partial = partial.end()?;
95//! partial = partial.begin_value()?;
96//! partial = partial.set(100i32)?;
97//! partial = partial.end()?;
98//!
99//! let map = partial.build()?;
100//! # Ok(())
101//! # }
102//! ```
103//!
104//! # Safety and Memory Management
105//!
106//! The `Partial` type ensures memory safety by:
107//! - Tracking initialization state of all fields
108//! - Preventing use-after-build through state tracking
109//! - Properly handling drop semantics for partially initialized values
110//! - Supporting both owned and borrowed values through lifetime parameters
111
112use alloc::{collections::BTreeMap, vec::Vec};
113
114mod iset;
115
116mod partial_api;
117
118use crate::{KeyPath, ReflectError, Resolution, TrackerKind, trace};
119
120use core::marker::PhantomData;
121
122mod heap_value;
123pub use heap_value::*;
124
125use facet_core::{
126    Def, EnumType, Field, PtrUninit, Shape, SliceBuilderVTable, Type, UserType, Variant,
127};
128use iset::ISet;
129
130/// State of a partial value
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132enum PartialState {
133    /// Partial is active and can be modified
134    Active,
135
136    /// Partial has been successfully built and cannot be reused
137    Built,
138}
139
140/// Mode of operation for frame management.
141///
142/// In `Strict` mode, frames must be fully initialized before being popped.
143/// In `Deferred` mode, frames can be stored when popped and restored on re-entry,
144/// with final validation happening in `finish_deferred()`.
145enum FrameMode {
146    /// Strict mode: frames must be fully initialized before popping.
147    Strict {
148        /// Stack of frames for nested initialization.
149        stack: Vec<Frame>,
150    },
151
152    /// Deferred mode: frames are stored when popped, can be re-entered.
153    Deferred {
154        /// Stack of frames for nested initialization.
155        stack: Vec<Frame>,
156
157        /// The resolution from facet-solver describing the field structure.
158        resolution: Resolution,
159
160        /// The frame depth when deferred mode was started.
161        /// Path calculations are relative to this depth.
162        start_depth: usize,
163
164        /// Current path as we navigate (e.g., ["inner", "x"]).
165        // TODO: Intern key paths to avoid repeated allocations. The Resolution
166        // already knows all possible paths, so we could use indices into that.
167        current_path: KeyPath,
168
169        /// Frames saved when popped, keyed by their path.
170        /// When we re-enter a path, we restore the stored frame.
171        // TODO: Consider using path indices instead of cloned KeyPaths as keys.
172        stored_frames: BTreeMap<KeyPath, Frame>,
173    },
174}
175
176impl FrameMode {
177    /// Get a reference to the frame stack.
178    fn stack(&self) -> &Vec<Frame> {
179        match self {
180            FrameMode::Strict { stack } | FrameMode::Deferred { stack, .. } => stack,
181        }
182    }
183
184    /// Get a mutable reference to the frame stack.
185    fn stack_mut(&mut self) -> &mut Vec<Frame> {
186        match self {
187            FrameMode::Strict { stack } | FrameMode::Deferred { stack, .. } => stack,
188        }
189    }
190
191    /// Check if we're in deferred mode.
192    fn is_deferred(&self) -> bool {
193        matches!(self, FrameMode::Deferred { .. })
194    }
195
196    /// Get the start depth if in deferred mode.
197    fn start_depth(&self) -> Option<usize> {
198        match self {
199            FrameMode::Deferred { start_depth, .. } => Some(*start_depth),
200            FrameMode::Strict { .. } => None,
201        }
202    }
203
204    /// Get the current path if in deferred mode.
205    fn current_path(&self) -> Option<&KeyPath> {
206        match self {
207            FrameMode::Deferred { current_path, .. } => Some(current_path),
208            FrameMode::Strict { .. } => None,
209        }
210    }
211
212    /// Get the resolution if in deferred mode.
213    fn resolution(&self) -> Option<&Resolution> {
214        match self {
215            FrameMode::Deferred { resolution, .. } => Some(resolution),
216            FrameMode::Strict { .. } => None,
217        }
218    }
219}
220
221/// A type-erased, heap-allocated, partially-initialized value.
222///
223/// [Partial] keeps track of the state of initialiation of the underlying
224/// value: if we're building `struct S { a: u32, b: String }`, we may
225/// have initialized `a`, or `b`, or both, or neither.
226///
227/// [Partial] allows navigating down nested structs and initializing them
228/// progressively: [Partial::begin_field] pushes a frame onto the stack,
229/// which then has to be initialized, and popped off with [Partial::end].
230///
231/// If [Partial::end] is called but the current frame isn't fully initialized,
232/// an error is returned: in other words, if you navigate down to a field,
233/// you have to fully initialize it one go. You can't go back up and back down
234/// to it again.
235pub struct Partial<'facet, const BORROW: bool = true> {
236    /// Frame management mode (strict or deferred) and associated state.
237    mode: FrameMode,
238
239    /// current state of the Partial
240    state: PartialState,
241
242    invariant: PhantomData<fn(&'facet ()) -> &'facet ()>,
243}
244
245#[derive(Clone, Copy, Debug)]
246pub(crate) enum MapInsertState {
247    /// Not currently inserting
248    Idle,
249
250    /// Pushing key - memory allocated, waiting for initialization
251    PushingKey {
252        /// Temporary storage for the key being built
253        key_ptr: PtrUninit,
254        /// Whether the key has been fully initialized
255        key_initialized: bool,
256        /// Whether the key's TrackedBuffer frame is still on the stack.
257        /// When true, the frame handles cleanup. When false (after end()),
258        /// the Map tracker owns the buffer and must clean it up.
259        key_frame_on_stack: bool,
260    },
261
262    /// Pushing value after key is done
263    PushingValue {
264        /// Temporary storage for the key that was built (always initialized)
265        key_ptr: PtrUninit,
266        /// Temporary storage for the value being built
267        value_ptr: Option<PtrUninit>,
268        /// Whether the value has been fully initialized
269        value_initialized: bool,
270        /// Whether the value's TrackedBuffer frame is still on the stack.
271        /// When true, the frame handles cleanup. When false (after end()),
272        /// the Map tracker owns the buffer and must clean it up.
273        value_frame_on_stack: bool,
274    },
275}
276
277#[derive(Debug, Clone, Copy)]
278pub(crate) enum FrameOwnership {
279    /// This frame owns the allocation and should deallocate it on drop
280    Owned,
281
282    /// This frame points to a field/element within a parent's allocation.
283    /// The parent's `iset[field_idx]` was CLEARED when this frame was created.
284    /// On drop: deinit if initialized, but do NOT deallocate.
285    /// On successful end(): parent's `iset[field_idx]` will be SET.
286    Field { field_idx: usize },
287
288    /// Temporary buffer tracked by parent's MapInsertState.
289    /// Used by begin_key(), begin_value() for map insertions.
290    /// Safe to drop on deinit - parent's cleanup respects is_init propagation.
291    TrackedBuffer,
292
293    /// Pointer into existing collection entry (Value object, Option inner, etc.)
294    /// Used by begin_object_entry() on existing key, begin_some() re-entry.
295    /// NOT safe to drop on deinit - parent collection has no per-entry tracking
296    /// and would try to drop the freed value again (double-free).
297    BorrowedInPlace,
298}
299
300impl FrameOwnership {
301    /// Returns true if this frame is responsible for deallocating its memory.
302    ///
303    /// Both `Owned` and `TrackedBuffer` frames allocated their memory and need
304    /// to deallocate it. `Field` and `BorrowedInPlace` frames borrow from
305    /// parent or existing structures.
306    fn needs_dealloc(&self) -> bool {
307        matches!(self, FrameOwnership::Owned | FrameOwnership::TrackedBuffer)
308    }
309}
310
311/// Immutable pairing of a shape with its actual allocation size.
312///
313/// This ensures that the shape and allocated size are always in sync and cannot
314/// drift apart, preventing the class of bugs where a frame's shape doesn't match
315/// what was actually allocated (see issue #1568).
316pub(crate) struct AllocatedShape {
317    shape: &'static Shape,
318    allocated_size: usize,
319}
320
321impl AllocatedShape {
322    pub(crate) fn new(shape: &'static Shape, allocated_size: usize) -> Self {
323        Self {
324            shape,
325            allocated_size,
326        }
327    }
328
329    pub(crate) fn shape(&self) -> &'static Shape {
330        self.shape
331    }
332
333    pub(crate) fn allocated_size(&self) -> usize {
334        self.allocated_size
335    }
336}
337
338/// Points somewhere in a partially-initialized value. If we're initializing
339/// `a.b.c`, then the first frame would point to the beginning of `a`, the
340/// second to the beginning of the `b` field of `a`, etc.
341///
342/// A frame can point to a complex data structure, like a struct or an enum:
343/// it keeps track of whether a variant was selected, which fields are initialized,
344/// etc. and is able to drop & deinitialize
345#[must_use]
346pub(crate) struct Frame {
347    /// Address of the value being initialized
348    pub(crate) data: PtrUninit,
349
350    /// Shape of the value being initialized, paired with the actual allocation size
351    pub(crate) allocated: AllocatedShape,
352
353    /// Whether this frame's data is fully initialized
354    pub(crate) is_init: bool,
355
356    /// Tracks building mode and partial initialization state
357    pub(crate) tracker: Tracker,
358
359    /// Whether this frame owns the allocation or is just a field pointer
360    pub(crate) ownership: FrameOwnership,
361
362    /// Whether this frame is for a custom deserialization pipeline
363    pub(crate) using_custom_deserialization: bool,
364
365    /// Container-level proxy definition (from `#[facet(proxy = ...)]` on the shape).
366    /// Used during custom deserialization to convert from proxy type to target type.
367    pub(crate) shape_level_proxy: Option<&'static facet_core::ProxyDef>,
368}
369
370#[derive(Debug)]
371pub(crate) enum Tracker {
372    /// Simple scalar value - no partial initialization tracking needed.
373    /// Whether it's initialized is tracked by `Frame::is_init`.
374    Scalar,
375
376    /// Partially initialized array
377    Array {
378        /// Track which array elements are initialized (up to 63 elements)
379        iset: ISet,
380        /// If we're pushing another frame, this is set to the array index
381        current_child: Option<usize>,
382    },
383
384    /// Partially initialized struct/tuple-struct etc.
385    Struct {
386        /// fields need to be individually tracked — we only
387        /// support up to 63 fields.
388        iset: ISet,
389        /// if we're pushing another frame, this is set to the index of the struct field
390        current_child: Option<usize>,
391    },
392
393    /// Smart pointer being initialized.
394    /// Whether it's initialized is tracked by `Frame::is_init`.
395    SmartPointer,
396
397    /// We're initializing an `Arc<[T]>`, `Box<[T]>`, `Rc<[T]>`, etc.
398    ///
399    /// We're using the slice builder API to construct the slice
400    SmartPointerSlice {
401        /// The slice builder vtable
402        vtable: &'static SliceBuilderVTable,
403
404        /// Whether we're currently building an item to push
405        building_item: bool,
406    },
407
408    /// Partially initialized enum (but we picked a variant,
409    /// so it's not Uninit)
410    Enum {
411        /// Variant chosen for the enum
412        variant: &'static Variant,
413        /// tracks enum fields (for the given variant)
414        data: ISet,
415        /// If we're pushing another frame, this is set to the field index
416        current_child: Option<usize>,
417    },
418
419    /// Partially initialized list (Vec, etc.)
420    /// Whether it's initialized is tracked by `Frame::is_init`.
421    List {
422        /// If we're pushing another frame for an element
423        current_child: bool,
424    },
425
426    /// Partially initialized map (HashMap, BTreeMap, etc.)
427    /// Whether it's initialized is tracked by `Frame::is_init`.
428    Map {
429        /// State of the current insertion operation
430        insert_state: MapInsertState,
431    },
432
433    /// Partially initialized set (HashSet, BTreeSet, etc.)
434    /// Whether it's initialized is tracked by `Frame::is_init`.
435    Set {
436        /// If we're pushing another frame for an element
437        current_child: bool,
438    },
439
440    /// Option being initialized with Some(inner_value)
441    Option {
442        /// Whether we're currently building the inner value
443        building_inner: bool,
444    },
445
446    /// Result being initialized with Ok or Err
447    Result {
448        /// Whether we're building Ok (true) or Err (false)
449        is_ok: bool,
450        /// Whether we're currently building the inner value
451        building_inner: bool,
452    },
453
454    /// Dynamic value (e.g., facet_value::Value) being initialized
455    DynamicValue {
456        /// What kind of dynamic value we're building
457        state: DynamicValueState,
458    },
459}
460
461/// State for building a dynamic value
462#[derive(Debug)]
463#[allow(dead_code)] // Some variants are for future use (object support)
464pub(crate) enum DynamicValueState {
465    /// Not yet initialized - will be set to scalar, array, or object
466    Uninit,
467    /// Initialized as a scalar (null, bool, number, string, bytes)
468    Scalar,
469    /// Initialized as an array, currently building an element
470    Array { building_element: bool },
471    /// Initialized as an object
472    Object {
473        insert_state: DynamicObjectInsertState,
474    },
475}
476
477/// State for inserting into a dynamic object
478#[derive(Debug)]
479#[allow(dead_code)] // For future use (object support)
480pub(crate) enum DynamicObjectInsertState {
481    /// Idle - ready for a new key-value pair
482    Idle,
483    /// Currently building the value for a key
484    BuildingValue {
485        /// The key for the current entry
486        key: alloc::string::String,
487    },
488}
489
490impl Tracker {
491    fn kind(&self) -> TrackerKind {
492        match self {
493            Tracker::Scalar => TrackerKind::Scalar,
494            Tracker::Array { .. } => TrackerKind::Array,
495            Tracker::Struct { .. } => TrackerKind::Struct,
496            Tracker::SmartPointer => TrackerKind::SmartPointer,
497            Tracker::SmartPointerSlice { .. } => TrackerKind::SmartPointerSlice,
498            Tracker::Enum { .. } => TrackerKind::Enum,
499            Tracker::List { .. } => TrackerKind::List,
500            Tracker::Map { .. } => TrackerKind::Map,
501            Tracker::Set { .. } => TrackerKind::Set,
502            Tracker::Option { .. } => TrackerKind::Option,
503            Tracker::Result { .. } => TrackerKind::Result,
504            Tracker::DynamicValue { .. } => TrackerKind::DynamicValue,
505        }
506    }
507
508    /// Set the current_child index for trackers that support it
509    fn set_current_child(&mut self, idx: usize) {
510        match self {
511            Tracker::Struct { current_child, .. }
512            | Tracker::Enum { current_child, .. }
513            | Tracker::Array { current_child, .. } => {
514                *current_child = Some(idx);
515            }
516            _ => {}
517        }
518    }
519
520    /// Clear the current_child index for trackers that support it
521    fn clear_current_child(&mut self) {
522        match self {
523            Tracker::Struct { current_child, .. }
524            | Tracker::Enum { current_child, .. }
525            | Tracker::Array { current_child, .. } => {
526                *current_child = None;
527            }
528            _ => {}
529        }
530    }
531}
532
533impl Frame {
534    fn new(data: PtrUninit, allocated: AllocatedShape, ownership: FrameOwnership) -> Self {
535        // For empty structs (structs with 0 fields), start as initialized since there's nothing to initialize
536        // This includes empty tuples () which are zero-sized types with no fields to initialize
537        let is_init = matches!(
538            allocated.shape().ty,
539            Type::User(UserType::Struct(struct_type)) if struct_type.fields.is_empty()
540        );
541
542        Self {
543            data,
544            allocated,
545            is_init,
546            tracker: Tracker::Scalar,
547            ownership,
548            using_custom_deserialization: false,
549            shape_level_proxy: None,
550        }
551    }
552
553    /// Deinitialize any initialized field: calls `drop_in_place` but does not free any
554    /// memory even if the frame owns that memory.
555    ///
556    /// After this call, `is_init` will be false and `tracker` will be [Tracker::Scalar].
557    fn deinit(&mut self) {
558        // For BorrowedInPlace frames, we must NOT drop. These point into existing
559        // collection entries (Value objects, Option inners) where the parent has no
560        // per-entry tracking. Dropping here would cause double-free when parent drops.
561        //
562        // For TrackedBuffer frames, we CAN drop. These are temporary buffers where
563        // the parent's MapInsertState tracks initialization via is_init propagation.
564        if matches!(self.ownership, FrameOwnership::BorrowedInPlace) {
565            self.is_init = false;
566            self.tracker = Tracker::Scalar;
567            return;
568        }
569
570        // Field frames are responsible for their value during cleanup.
571        // The ownership model ensures no double-free:
572        // - begin_field: parent's iset[idx] is cleared (parent relinquishes responsibility)
573        // - end: parent's iset[idx] is set (parent reclaims responsibility), frame is popped
574        // So if Field frame is still on stack during cleanup, parent's iset[idx] is false,
575        // meaning the parent won't drop this field - the Field frame must do it.
576
577        match &self.tracker {
578            Tracker::Scalar => {
579                // Simple scalar - drop if initialized
580                if self.is_init {
581                    unsafe {
582                        self.allocated
583                            .shape()
584                            .call_drop_in_place(self.data.assume_init())
585                    };
586                }
587            }
588            Tracker::Array { iset, .. } => {
589                // Drop initialized array elements
590                if let Type::Sequence(facet_core::SequenceType::Array(array_def)) =
591                    self.allocated.shape().ty
592                {
593                    let element_layout = array_def.t.layout.sized_layout().ok();
594                    if let Some(layout) = element_layout {
595                        for idx in 0..array_def.n {
596                            if iset.get(idx) {
597                                let offset = layout.size() * idx;
598                                let element_ptr = unsafe { self.data.field_init(offset) };
599                                unsafe { array_def.t.call_drop_in_place(element_ptr) };
600                            }
601                        }
602                    }
603                }
604            }
605            Tracker::Struct { iset, .. } => {
606                // Drop initialized struct fields
607                if let Type::User(UserType::Struct(struct_type)) = self.allocated.shape().ty {
608                    if iset.all_set() {
609                        unsafe {
610                            self.allocated
611                                .shape()
612                                .call_drop_in_place(self.data.assume_init())
613                        };
614                    } else {
615                        for (idx, field) in struct_type.fields.iter().enumerate() {
616                            if iset.get(idx) {
617                                // This field was initialized, drop it
618                                let field_ptr = unsafe { self.data.field_init(field.offset) };
619                                unsafe { field.shape().call_drop_in_place(field_ptr) };
620                            }
621                        }
622                    }
623                }
624            }
625            Tracker::Enum { variant, data, .. } => {
626                // Drop initialized enum variant fields
627                for (idx, field) in variant.data.fields.iter().enumerate() {
628                    if data.get(idx) {
629                        // This field was initialized, drop it
630                        let field_ptr = unsafe { self.data.field_init(field.offset) };
631                        unsafe { field.shape().call_drop_in_place(field_ptr) };
632                    }
633                }
634            }
635            Tracker::SmartPointer => {
636                // Drop the initialized Box
637                if self.is_init {
638                    unsafe {
639                        self.allocated
640                            .shape()
641                            .call_drop_in_place(self.data.assume_init())
642                    };
643                }
644                // Note: we don't deallocate the inner value here because
645                // the Box's drop will handle that
646            }
647            Tracker::SmartPointerSlice { vtable, .. } => {
648                // Free the slice builder
649                let builder_ptr = unsafe { self.data.assume_init() };
650                unsafe {
651                    (vtable.free_fn)(builder_ptr);
652                }
653            }
654            Tracker::List { .. } => {
655                // Drop the initialized List
656                if self.is_init {
657                    unsafe {
658                        self.allocated
659                            .shape()
660                            .call_drop_in_place(self.data.assume_init())
661                    };
662                }
663            }
664            Tracker::Map { insert_state } => {
665                // Drop the initialized Map
666                if self.is_init {
667                    unsafe {
668                        self.allocated
669                            .shape()
670                            .call_drop_in_place(self.data.assume_init())
671                    };
672                }
673
674                // Clean up key/value buffers based on whether their TrackedBuffer frames
675                // are still on the stack. If a frame is on the stack, it handles cleanup.
676                // If a frame was already popped (via end()), we own the buffer and must clean it.
677                match insert_state {
678                    MapInsertState::PushingKey {
679                        key_ptr,
680                        key_initialized,
681                        key_frame_on_stack,
682                    } => {
683                        // Only clean up if the frame was already popped.
684                        // If key_frame_on_stack is true, the TrackedBuffer frame above us
685                        // will handle dropping and deallocating the key buffer.
686                        if !*key_frame_on_stack
687                            && let Def::Map(map_def) = self.allocated.shape().def
688                        {
689                            // Drop the key if it was initialized
690                            if *key_initialized {
691                                unsafe { map_def.k().call_drop_in_place(key_ptr.assume_init()) };
692                            }
693                            // Deallocate the key buffer
694                            if let Ok(key_layout) = map_def.k().layout.sized_layout()
695                                && key_layout.size() > 0
696                            {
697                                unsafe {
698                                    alloc::alloc::dealloc(key_ptr.as_mut_byte_ptr(), key_layout)
699                                };
700                            }
701                        }
702                    }
703                    MapInsertState::PushingValue {
704                        key_ptr,
705                        value_ptr,
706                        value_initialized,
707                        value_frame_on_stack,
708                    } => {
709                        if let Def::Map(map_def) = self.allocated.shape().def {
710                            // Key was already popped (that's how we got to PushingValue state),
711                            // so we always own the key buffer and must clean it up.
712                            unsafe { map_def.k().call_drop_in_place(key_ptr.assume_init()) };
713                            if let Ok(key_layout) = map_def.k().layout.sized_layout()
714                                && key_layout.size() > 0
715                            {
716                                unsafe {
717                                    alloc::alloc::dealloc(key_ptr.as_mut_byte_ptr(), key_layout)
718                                };
719                            }
720
721                            // Only clean up value if the frame was already popped.
722                            // If value_frame_on_stack is true, the TrackedBuffer frame above us
723                            // will handle dropping and deallocating the value buffer.
724                            if !*value_frame_on_stack && let Some(value_ptr) = value_ptr {
725                                // Drop the value if it was initialized
726                                if *value_initialized {
727                                    unsafe {
728                                        map_def.v().call_drop_in_place(value_ptr.assume_init())
729                                    };
730                                }
731                                // Deallocate the value buffer
732                                if let Ok(value_layout) = map_def.v().layout.sized_layout()
733                                    && value_layout.size() > 0
734                                {
735                                    unsafe {
736                                        alloc::alloc::dealloc(
737                                            value_ptr.as_mut_byte_ptr(),
738                                            value_layout,
739                                        )
740                                    };
741                                }
742                            }
743                        }
744                    }
745                    MapInsertState::Idle => {}
746                }
747            }
748            Tracker::Set { .. } => {
749                // Drop the initialized Set
750                if self.is_init {
751                    unsafe {
752                        self.allocated
753                            .shape()
754                            .call_drop_in_place(self.data.assume_init())
755                    };
756                }
757            }
758            Tracker::Option { building_inner } => {
759                // If we're building the inner value, it will be handled by the Option vtable
760                // No special cleanup needed here as the Option will either be properly
761                // initialized or remain uninitialized
762                if !building_inner {
763                    // Option is fully initialized, drop it normally
764                    unsafe {
765                        self.allocated
766                            .shape()
767                            .call_drop_in_place(self.data.assume_init())
768                    };
769                }
770            }
771            Tracker::Result { building_inner, .. } => {
772                // If we're building the inner value, it will be handled by the Result vtable
773                // No special cleanup needed here as the Result will either be properly
774                // initialized or remain uninitialized
775                if !building_inner {
776                    // Result is fully initialized, drop it normally
777                    unsafe {
778                        self.allocated
779                            .shape()
780                            .call_drop_in_place(self.data.assume_init())
781                    };
782                }
783            }
784            Tracker::DynamicValue { .. } => {
785                // Drop if initialized
786                if self.is_init {
787                    let result = unsafe {
788                        self.allocated
789                            .shape()
790                            .call_drop_in_place(self.data.assume_init())
791                    };
792                    if result.is_none() {
793                        // This would be a bug - DynamicValue should always have drop_in_place
794                        panic!(
795                            "DynamicValue type {} has no drop_in_place implementation",
796                            self.allocated.shape()
797                        );
798                    }
799                }
800            }
801        }
802
803        self.is_init = false;
804        self.tracker = Tracker::Scalar;
805    }
806
807    /// Deinitialize any initialized value for REPLACEMENT purposes.
808    ///
809    /// Unlike `deinit()` which is used during error cleanup, this method is used when
810    /// we're about to overwrite a value with a new one (e.g., in `set_shape`).
811    ///
812    /// The difference is important for Field frames with simple trackers:
813    /// - During cleanup: parent struct will drop all initialized fields, so Field frames skip dropping
814    /// - During replacement: we're about to overwrite, so we MUST drop the old value
815    ///
816    /// For BorrowedInPlace frames: same logic applies - we must drop when replacing.
817    fn deinit_for_replace(&mut self) {
818        // For BorrowedInPlace frames, deinit() skips dropping (parent owns on cleanup).
819        // But when REPLACING a value, we must drop the old value first.
820        if matches!(self.ownership, FrameOwnership::BorrowedInPlace) && self.is_init {
821            unsafe {
822                self.allocated
823                    .shape()
824                    .call_drop_in_place(self.data.assume_init());
825            }
826
827            // CRITICAL: For DynamicValue (e.g., facet_value::Value), the parent Object's
828            // HashMap entry still points to this location. If we just drop and leave garbage,
829            // the parent will try to drop that garbage when it's cleaned up, causing
830            // use-after-free. We must reinitialize to a safe default (Null) so the parent
831            // can safely drop it later.
832            if let Def::DynamicValue(dyn_def) = &self.allocated.shape().def {
833                unsafe {
834                    (dyn_def.vtable.set_null)(self.data);
835                }
836                // Keep is_init = true since we just initialized it to Null
837                self.tracker = Tracker::DynamicValue {
838                    state: DynamicValueState::Scalar,
839                };
840                return;
841            }
842
843            self.is_init = false;
844            self.tracker = Tracker::Scalar;
845            return;
846        }
847
848        // Field frames handle their own cleanup in deinit() - no special handling needed here.
849
850        // All other cases: use normal deinit
851        self.deinit();
852    }
853
854    /// This must be called after (fully) initializing a value.
855    ///
856    /// This sets `is_init` to `true` to indicate the value is initialized.
857    /// Composite types (structs, enums, etc.) might be handled differently.
858    ///
859    /// # Safety
860    ///
861    /// This should only be called when `self.data` has been actually initialized.
862    unsafe fn mark_as_init(&mut self) {
863        self.is_init = true;
864    }
865
866    /// Deallocate the memory associated with this frame, if it owns it.
867    ///
868    /// The memory has to be deinitialized first, see [Frame::deinit]
869    fn dealloc(self) {
870        // Only deallocate if this frame owns its memory
871        if !self.ownership.needs_dealloc() {
872            return;
873        }
874
875        // If we need to deallocate, the frame must be deinitialized first
876        if self.is_init {
877            unreachable!("a frame has to be deinitialized before being deallocated")
878        }
879
880        // Deallocate using the actual allocated size (not derived from shape)
881        if self.allocated.allocated_size() > 0 {
882            // Use the shape for alignment, but the stored size for the actual allocation
883            if let Ok(layout) = self.allocated.shape().layout.sized_layout() {
884                let actual_layout = core::alloc::Layout::from_size_align(
885                    self.allocated.allocated_size(),
886                    layout.align(),
887                )
888                .expect("allocated_size must be valid");
889                unsafe { alloc::alloc::dealloc(self.data.as_mut_byte_ptr(), actual_layout) };
890            }
891        }
892    }
893
894    /// Fill in defaults for any unset fields that have default values.
895    ///
896    /// This handles:
897    /// - Container-level defaults (when no fields set and struct has Default impl)
898    /// - Fields with `#[facet(default = ...)]` - uses the explicit default function
899    /// - Fields with `#[facet(default)]` - uses the type's Default impl
900    /// - `Option<T>` fields - default to None
901    ///
902    /// Returns Ok(()) if successful, or an error if a field has `#[facet(default)]`
903    /// but no default implementation is available.
904    fn fill_defaults(&mut self) -> Result<(), ReflectError> {
905        // First, check if we need to upgrade from Scalar to Struct tracker
906        // This happens when no fields were visited at all in deferred mode
907        if !self.is_init
908            && matches!(self.tracker, Tracker::Scalar)
909            && let Type::User(UserType::Struct(struct_type)) = self.allocated.shape().ty
910        {
911            // If no fields were visited and the container has a default, use it
912            // SAFETY: We're about to initialize the entire struct with its default value
913            let data_mut = unsafe { self.data.assume_init() };
914            if unsafe { self.allocated.shape().call_default_in_place(data_mut) }.is_some() {
915                self.is_init = true;
916                return Ok(());
917            }
918            // Otherwise initialize the struct tracker with empty iset
919            self.tracker = Tracker::Struct {
920                iset: ISet::new(struct_type.fields.len()),
921                current_child: None,
922            };
923        }
924
925        match &mut self.tracker {
926            Tracker::Struct { iset, .. } => {
927                if let Type::User(UserType::Struct(struct_type)) = self.allocated.shape().ty {
928                    // Check if NO fields have been set and the container has a default
929                    let no_fields_set = (0..struct_type.fields.len()).all(|i| !iset.get(i));
930                    if no_fields_set {
931                        // SAFETY: We're about to initialize the entire struct with its default value
932                        let data_mut = unsafe { self.data.assume_init() };
933                        if unsafe { self.allocated.shape().call_default_in_place(data_mut) }
934                            .is_some()
935                        {
936                            self.tracker = Tracker::Scalar;
937                            self.is_init = true;
938                            return Ok(());
939                        }
940                    }
941
942                    // Fill defaults for individual fields
943                    for (idx, field) in struct_type.fields.iter().enumerate() {
944                        // Skip already-initialized fields
945                        if iset.get(idx) {
946                            continue;
947                        }
948
949                        // Calculate field pointer
950                        let field_ptr = unsafe { self.data.field_uninit(field.offset) };
951
952                        // Try to initialize with default
953                        if unsafe { Self::try_init_field_default(field, field_ptr) } {
954                            // Mark field as initialized
955                            iset.set(idx);
956                        } else if field.has_default() {
957                            // Field has #[facet(default)] but we couldn't find a default function.
958                            // This happens with opaque types that don't have default_in_place.
959                            return Err(ReflectError::DefaultAttrButNoDefaultImpl {
960                                shape: field.shape(),
961                            });
962                        }
963                    }
964                }
965            }
966            Tracker::Enum { variant, data, .. } => {
967                // Handle enum variant fields
968                for (idx, field) in variant.data.fields.iter().enumerate() {
969                    // Skip already-initialized fields
970                    if data.get(idx) {
971                        continue;
972                    }
973
974                    // Calculate field pointer within the variant data
975                    let field_ptr = unsafe { self.data.field_uninit(field.offset) };
976
977                    // Try to initialize with default
978                    if unsafe { Self::try_init_field_default(field, field_ptr) } {
979                        // Mark field as initialized
980                        data.set(idx);
981                    } else if field.has_default() {
982                        // Field has #[facet(default)] but we couldn't find a default function.
983                        return Err(ReflectError::DefaultAttrButNoDefaultImpl {
984                            shape: field.shape(),
985                        });
986                    }
987                }
988            }
989            // Other tracker types don't have fields with defaults
990            _ => {}
991        }
992        Ok(())
993    }
994
995    /// Initialize a field with its default value if one is available.
996    ///
997    /// Priority:
998    /// 1. Explicit field-level default_fn (from `#[facet(default = ...)]`)
999    /// 2. Type-level default_in_place (from Default impl, including `Option<T>`)
1000    ///    but only if the field has the DEFAULT flag
1001    /// 3. Special cases: `Option<T>` (defaults to None), () (unit type)
1002    ///
1003    /// Returns true if a default was applied, false otherwise.
1004    ///
1005    /// # Safety
1006    ///
1007    /// `field_ptr` must point to uninitialized memory of the appropriate type.
1008    unsafe fn try_init_field_default(field: &Field, field_ptr: PtrUninit) -> bool {
1009        use facet_core::DefaultSource;
1010
1011        // First check for explicit field-level default
1012        if let Some(default_source) = field.default {
1013            match default_source {
1014                DefaultSource::Custom(default_fn) => {
1015                    // Custom default function - it expects PtrUninit
1016                    unsafe { default_fn(field_ptr) };
1017                    return true;
1018                }
1019                DefaultSource::FromTrait => {
1020                    // Use the type's Default trait - needs PtrMut
1021                    let field_ptr_mut = unsafe { field_ptr.assume_init() };
1022                    if unsafe { field.shape().call_default_in_place(field_ptr_mut) }.is_some() {
1023                        return true;
1024                    }
1025                }
1026            }
1027        }
1028
1029        // Special case: Option<T> always defaults to None, even without explicit #[facet(default)]
1030        // This is because Option is fundamentally "optional" - if not set, it should be None
1031        if matches!(field.shape().def, Def::Option(_)) {
1032            let field_ptr_mut = unsafe { field_ptr.assume_init() };
1033            if unsafe { field.shape().call_default_in_place(field_ptr_mut) }.is_some() {
1034                return true;
1035            }
1036        }
1037
1038        // Special case: () unit type always defaults to ()
1039        if field.shape().is_type::<()>() {
1040            let field_ptr_mut = unsafe { field_ptr.assume_init() };
1041            if unsafe { field.shape().call_default_in_place(field_ptr_mut) }.is_some() {
1042                return true;
1043            }
1044        }
1045
1046        false
1047    }
1048
1049    /// Returns an error if the value is not fully initialized
1050    fn require_full_initialization(&self) -> Result<(), ReflectError> {
1051        match self.tracker {
1052            Tracker::Scalar => {
1053                if self.is_init {
1054                    Ok(())
1055                } else {
1056                    Err(ReflectError::UninitializedValue {
1057                        shape: self.allocated.shape(),
1058                    })
1059                }
1060            }
1061            Tracker::Array { iset, .. } => {
1062                match self.allocated.shape().ty {
1063                    Type::Sequence(facet_core::SequenceType::Array(array_def)) => {
1064                        // Check if all array elements are initialized
1065                        if (0..array_def.n).all(|idx| iset.get(idx)) {
1066                            Ok(())
1067                        } else {
1068                            Err(ReflectError::UninitializedValue {
1069                                shape: self.allocated.shape(),
1070                            })
1071                        }
1072                    }
1073                    _ => Err(ReflectError::UninitializedValue {
1074                        shape: self.allocated.shape(),
1075                    }),
1076                }
1077            }
1078            Tracker::Struct { iset, .. } => {
1079                if iset.all_set() {
1080                    Ok(())
1081                } else {
1082                    // Attempt to find the first uninitialized field, if possible
1083                    match self.allocated.shape().ty {
1084                        Type::User(UserType::Struct(struct_type)) => {
1085                            // Find index of the first bit not set
1086                            let first_missing_idx =
1087                                (0..struct_type.fields.len()).find(|&idx| !iset.get(idx));
1088                            if let Some(missing_idx) = first_missing_idx {
1089                                let field_name = struct_type.fields[missing_idx].name;
1090                                Err(ReflectError::UninitializedField {
1091                                    shape: self.allocated.shape(),
1092                                    field_name,
1093                                })
1094                            } else {
1095                                // fallback, something went wrong
1096                                Err(ReflectError::UninitializedValue {
1097                                    shape: self.allocated.shape(),
1098                                })
1099                            }
1100                        }
1101                        _ => Err(ReflectError::UninitializedValue {
1102                            shape: self.allocated.shape(),
1103                        }),
1104                    }
1105                }
1106            }
1107            Tracker::Enum { variant, data, .. } => {
1108                // Check if all fields of the variant are initialized
1109                let num_fields = variant.data.fields.len();
1110                if num_fields == 0 {
1111                    // Unit variant, always initialized
1112                    Ok(())
1113                } else if (0..num_fields).all(|idx| data.get(idx)) {
1114                    Ok(())
1115                } else {
1116                    // Find the first uninitialized field
1117                    let first_missing_idx = (0..num_fields).find(|&idx| !data.get(idx));
1118                    if let Some(missing_idx) = first_missing_idx {
1119                        let field_name = variant.data.fields[missing_idx].name;
1120                        Err(ReflectError::UninitializedEnumField {
1121                            shape: self.allocated.shape(),
1122                            field_name,
1123                            variant_name: variant.name,
1124                        })
1125                    } else {
1126                        Err(ReflectError::UninitializedValue {
1127                            shape: self.allocated.shape(),
1128                        })
1129                    }
1130                }
1131            }
1132            Tracker::SmartPointer => {
1133                if self.is_init {
1134                    Ok(())
1135                } else {
1136                    Err(ReflectError::UninitializedValue {
1137                        shape: self.allocated.shape(),
1138                    })
1139                }
1140            }
1141            Tracker::SmartPointerSlice { building_item, .. } => {
1142                if building_item {
1143                    Err(ReflectError::UninitializedValue {
1144                        shape: self.allocated.shape(),
1145                    })
1146                } else {
1147                    Ok(())
1148                }
1149            }
1150            Tracker::List { current_child } => {
1151                if self.is_init && !current_child {
1152                    Ok(())
1153                } else {
1154                    Err(ReflectError::UninitializedValue {
1155                        shape: self.allocated.shape(),
1156                    })
1157                }
1158            }
1159            Tracker::Map { insert_state } => {
1160                if self.is_init && matches!(insert_state, MapInsertState::Idle) {
1161                    Ok(())
1162                } else {
1163                    Err(ReflectError::UninitializedValue {
1164                        shape: self.allocated.shape(),
1165                    })
1166                }
1167            }
1168            Tracker::Set { current_child } => {
1169                if self.is_init && !current_child {
1170                    Ok(())
1171                } else {
1172                    Err(ReflectError::UninitializedValue {
1173                        shape: self.allocated.shape(),
1174                    })
1175                }
1176            }
1177            Tracker::Option { building_inner } => {
1178                if building_inner {
1179                    Err(ReflectError::UninitializedValue {
1180                        shape: self.allocated.shape(),
1181                    })
1182                } else {
1183                    Ok(())
1184                }
1185            }
1186            Tracker::Result { building_inner, .. } => {
1187                if building_inner {
1188                    Err(ReflectError::UninitializedValue {
1189                        shape: self.allocated.shape(),
1190                    })
1191                } else {
1192                    Ok(())
1193                }
1194            }
1195            Tracker::DynamicValue { ref state } => {
1196                if matches!(state, DynamicValueState::Uninit) {
1197                    Err(ReflectError::UninitializedValue {
1198                        shape: self.allocated.shape(),
1199                    })
1200                } else {
1201                    Ok(())
1202                }
1203            }
1204        }
1205    }
1206
1207    /// Get the [EnumType] of the frame's shape, if it is an enum type
1208    pub(crate) fn get_enum_type(&self) -> Result<EnumType, ReflectError> {
1209        match self.allocated.shape().ty {
1210            Type::User(UserType::Enum(e)) => Ok(e),
1211            _ => Err(ReflectError::WasNotA {
1212                expected: "enum",
1213                actual: self.allocated.shape(),
1214            }),
1215        }
1216    }
1217
1218    pub(crate) fn get_field(&self) -> Option<&Field> {
1219        match self.allocated.shape().ty {
1220            Type::User(user_type) => match user_type {
1221                UserType::Struct(struct_type) => {
1222                    // Try to get currently active field index
1223                    if let Tracker::Struct {
1224                        current_child: Some(idx),
1225                        ..
1226                    } = &self.tracker
1227                    {
1228                        struct_type.fields.get(*idx)
1229                    } else {
1230                        None
1231                    }
1232                }
1233                UserType::Enum(_enum_type) => {
1234                    if let Tracker::Enum {
1235                        variant,
1236                        current_child: Some(idx),
1237                        ..
1238                    } = &self.tracker
1239                    {
1240                        variant.data.fields.get(*idx)
1241                    } else {
1242                        None
1243                    }
1244                }
1245                _ => None,
1246            },
1247            _ => None,
1248        }
1249    }
1250}
1251
1252// Convenience methods on Partial for accessing FrameMode internals.
1253// These help minimize changes to the rest of the codebase during the refactor.
1254impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
1255    /// Get a reference to the frame stack.
1256    #[inline]
1257    pub(crate) fn frames(&self) -> &Vec<Frame> {
1258        self.mode.stack()
1259    }
1260
1261    /// Get a mutable reference to the frame stack.
1262    #[inline]
1263    pub(crate) fn frames_mut(&mut self) -> &mut Vec<Frame> {
1264        self.mode.stack_mut()
1265    }
1266
1267    /// Check if we're in deferred mode.
1268    #[inline]
1269    pub fn is_deferred(&self) -> bool {
1270        self.mode.is_deferred()
1271    }
1272
1273    /// Get the start depth if in deferred mode.
1274    #[inline]
1275    pub(crate) fn start_depth(&self) -> Option<usize> {
1276        self.mode.start_depth()
1277    }
1278
1279    /// Get the current path if in deferred mode.
1280    #[inline]
1281    pub(crate) fn current_path(&self) -> Option<&KeyPath> {
1282        self.mode.current_path()
1283    }
1284
1285    /// Get the resolution if in deferred mode.
1286    #[inline]
1287    pub(crate) fn resolution(&self) -> Option<&Resolution> {
1288        self.mode.resolution()
1289    }
1290}
1291
1292impl<'facet, const BORROW: bool> Drop for Partial<'facet, BORROW> {
1293    fn drop(&mut self) {
1294        trace!("🧹 Partial is being dropped");
1295
1296        // With the ownership transfer model:
1297        // - When we enter a field, parent's iset[idx] is cleared
1298        // - Parent won't try to drop fields with iset[idx] = false
1299        // - No double-free possible by construction
1300
1301        // 1. Clean up stored frames from deferred state
1302        if let FrameMode::Deferred { stored_frames, .. } = &mut self.mode {
1303            // Stored frames have ownership of their data (parent's iset was cleared).
1304            // IMPORTANT: Process in deepest-first order so children are dropped before parents.
1305            // Child frames have data pointers into parent memory, so parents must stay valid
1306            // until all their children are cleaned up.
1307            let mut stored_frames = core::mem::take(stored_frames);
1308            let mut paths: Vec<_> = stored_frames.keys().cloned().collect();
1309            paths.sort_by_key(|p| core::cmp::Reverse(p.len()));
1310            for path in paths {
1311                if let Some(mut frame) = stored_frames.remove(&path) {
1312                    frame.deinit();
1313                    frame.dealloc();
1314                }
1315            }
1316        }
1317
1318        // 2. Pop and deinit stack frames
1319        loop {
1320            let stack = self.mode.stack_mut();
1321            if stack.is_empty() {
1322                break;
1323            }
1324
1325            let mut frame = stack.pop().unwrap();
1326            frame.deinit();
1327            frame.dealloc();
1328        }
1329    }
1330}