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}