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