facet_reflect/wip/
pop.rs

1use crate::trace;
2use facet_core::{
3    Def, EnumType, PtrConst, PtrMut, PtrUninit, Repr, ScalarAffinity, SequenceType, StructType,
4    Type, UserType, Variant,
5};
6#[allow(unused_imports)]
7use owo_colors::OwoColorize;
8
9use crate::{FrameMode, ReflectError};
10
11use super::{Frame, Wip};
12
13impl Wip<'_> {
14    /// Pops the current frame — goes back up one level
15    pub fn pop(mut self) -> Result<Self, ReflectError> {
16        let frame = match self.pop_inner()? {
17            Some(frame) => frame,
18            None => {
19                return Err(ReflectError::InvariantViolation {
20                    invariant: "No frame to pop — it was time to call build()",
21                });
22            }
23        };
24
25        self.track(frame);
26        Ok(self)
27    }
28
29    fn pop_inner(&mut self) -> Result<Option<Frame>, ReflectError> {
30        let mut frame = match self.frames.pop() {
31            Some(f) => f,
32            None => return Ok(None),
33        };
34        #[cfg(feature = "log")]
35        let frame_shape = frame.shape;
36
37        let init = frame.is_fully_initialized();
38        trace!(
39            "[{}] {} popped, {} initialized",
40            self.frames.len(),
41            frame_shape.blue(),
42            if init {
43                "✅ fully".style(owo_colors::Style::new().green())
44            } else {
45                "🚧 partially".style(owo_colors::Style::new().red())
46            }
47        );
48        if init {
49            // If this frame is fully initialized, mark it as such in all tracking states
50
51            // 1. Mark this frame as fully initialized (if it isn't already)
52            unsafe {
53                frame.mark_fully_initialized();
54            }
55
56            // 2. Mark the parent's field as initialized (if this is a field)
57            #[cfg(feature = "log")]
58            let num_frames = self.frames.len();
59            if let Some(parent) = self.frames.last_mut() {
60                if let Some(index) = frame.field_index_in_parent {
61                    trace!(
62                        "[{}] Marking field #{} in parent {} as initialized",
63                        num_frames,
64                        index.to_string().yellow(),
65                        parent.shape.blue()
66                    );
67                    parent.istate.fields.set(index);
68                }
69            }
70
71            // 3. If this is a container type like an array, make sure its internal state reflects that it's complete
72            match frame.shape.def {
73                Def::Array(array_def) => {
74                    // For arrays, they're only fully initialized if all elements are populated
75                    let current_index = frame.istate.list_index.unwrap_or(0);
76                    trace!(
77                        "[{}] Array {} has {}/{} elements populated",
78                        self.frames.len(),
79                        frame_shape.blue(),
80                        current_index.to_string().yellow(),
81                        array_def.n.to_string().green()
82                    );
83
84                    if current_index == array_def.n {
85                        trace!(
86                            "[{}] Array {} fully populated with {} elements, marking as initialized",
87                            self.frames.len(),
88                            frame_shape.blue(),
89                            array_def.n.to_string().green()
90                        );
91                        // Mark the array itself as initialized (field 0)
92                        frame.istate.fields.set(0);
93                    }
94                }
95                // Add other container types here if needed
96                _ => {}
97            }
98        }
99
100        // Handle special frame modes
101        match frame.istate.mode {
102            // Handle list element frames
103            FrameMode::ListElement => {
104                if frame.is_fully_initialized() {
105                    // This was a list or tuple element, so we need to push it to the parent
106                    #[cfg(feature = "log")]
107                    let frame_len = self.frames.len();
108
109                    // Get parent frame
110                    let parent_frame = self.frames.last_mut().unwrap();
111                    let parent_shape = parent_frame.shape;
112
113                    match parent_shape.def {
114                        // Handle List/Array
115                        Def::List(list_def) => {
116                            let list_vtable = list_def.vtable;
117                            trace!(
118                                "[{}] Pushing element to list {}",
119                                frame_len,
120                                parent_shape.blue()
121                            );
122                            unsafe {
123                                (list_vtable.push)(
124                                    PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
125                                    PtrMut::new(frame.data.as_mut_byte_ptr()),
126                                );
127                                self.mark_moved_out_of(&mut frame);
128                            }
129                        }
130                        Def::Scalar(s) if matches!(s.affinity, ScalarAffinity::Empty(_)) => {
131                            trace!(
132                                "[{}] Handling scalar empty unit type {}",
133                                frame_len,
134                                parent_shape.blue()
135                            );
136                            // Mark the parent scalar unit as fully initialized
137                            unsafe {
138                                parent_frame.mark_fully_initialized();
139                                self.mark_moved_out_of(&mut frame);
140                            }
141                        }
142                        _ => match parent_shape.ty {
143                            // Handle Empty Unit Types (including empty tuple structs and tuples)
144                            Type::User(UserType::Struct(sd))
145                                if sd.kind == facet_core::StructKind::Tuple
146                                    && sd.fields.is_empty() =>
147                            {
148                                trace!(
149                                    "[{}] Handling empty tuple struct unit type {}",
150                                    frame_len,
151                                    parent_shape.blue()
152                                );
153                                // Mark the parent unit struct as fully initialized
154                                unsafe {
155                                    parent_frame.mark_fully_initialized();
156                                }
157                                // Element frame is implicitly moved/consumed, but nothing to dealloc if it was also unit
158                                unsafe { self.mark_moved_out_of(&mut frame) };
159                            }
160
161                            // Handle tuples (Type::Sequence(SequenceType::Tuple))
162                            Type::Sequence(SequenceType::Tuple(tt)) => {
163                                // Get the field index from list_index saved during push
164                                let previous_index = parent_frame.istate.list_index.unwrap_or(1);
165                                let field_index = previous_index - 1; // -1 because we incremented *after* using the index in push
166
167                                if field_index >= tt.fields.len() {
168                                    panic!(
169                                        "Field index {} out of bounds for tuple {} with {} fields",
170                                        field_index,
171                                        parent_shape,
172                                        tt.fields.len()
173                                    );
174                                }
175
176                                let field = &tt.fields[field_index];
177                                trace!(
178                                    "[{}] Setting tuple field {} ({}) of {}",
179                                    frame_len,
180                                    field_index.to_string().yellow(),
181                                    field.name.bright_blue(),
182                                    parent_shape.blue()
183                                );
184
185                                unsafe {
186                                    // Copy the element data to the tuple field
187                                    let field_ptr = parent_frame.data.field_uninit_at(field.offset);
188                                    field_ptr
189                                        .copy_from(
190                                            PtrConst::new(frame.data.as_byte_ptr()),
191                                            field.shape(),
192                                        )
193                                        .map_err(|_| ReflectError::Unsized {
194                                            shape: field.shape(),
195                                        })?; // Use ? to propagate potential unsized error
196
197                                    // Mark the specific field as initialized using its index
198                                    parent_frame.istate.fields.set(field_index);
199
200                                    // Mark the element as moved
201                                    self.mark_moved_out_of(&mut frame);
202                                }
203                            }
204
205                            // Handle Tuple Structs
206                            Type::User(UserType::Struct(sd))
207                                if sd.kind == facet_core::StructKind::Tuple =>
208                            {
209                                // Get the field index from list_index saved during push
210                                let previous_index = parent_frame.istate.list_index.unwrap_or(1);
211                                let field_index = previous_index - 1; // -1 because we incremented *after* using the index in push
212
213                                if field_index >= sd.fields.len() {
214                                    panic!(
215                                        "Field index {} out of bounds for tuple struct {} with {} fields",
216                                        field_index,
217                                        parent_shape,
218                                        sd.fields.len()
219                                    );
220                                }
221
222                                let field = &sd.fields[field_index];
223                                trace!(
224                                    "[{}] Setting tuple struct field {} ({}) of {}",
225                                    frame_len,
226                                    field_index.to_string().yellow(),
227                                    field.name.bright_blue(),
228                                    parent_shape.blue()
229                                );
230
231                                unsafe {
232                                    // Copy the element data to the tuple field
233                                    let field_ptr = parent_frame.data.field_uninit_at(field.offset);
234                                    field_ptr
235                                        .copy_from(
236                                            PtrConst::new(frame.data.as_byte_ptr()),
237                                            field.shape(),
238                                        )
239                                        .map_err(|_| ReflectError::Unsized {
240                                            shape: field.shape(),
241                                        })?; // Use ? to propagate potential unsized error
242
243                                    // Mark the specific field as initialized using its index
244                                    parent_frame.istate.fields.set(field_index);
245
246                                    // Mark the element as moved
247                                    self.mark_moved_out_of(&mut frame);
248                                }
249                            }
250
251                            // Handle Tuple Enum Variants
252                            Type::User(UserType::Enum(_)) => {
253                                // Ensure a variant is selected and it's a tuple variant
254                                let variant =
255                                parent_frame.istate.variant.as_ref().unwrap_or_else(|| {
256                                    panic!(
257                                    "Popping element for enum {} but no variant was selected",
258                                    parent_shape
259                                )
260                                });
261
262                                if variant.data.kind != facet_core::StructKind::Tuple {
263                                    panic!(
264                                        "Popping element for enum {}, but selected variant '{}' is not a tuple variant",
265                                        parent_shape, variant.name
266                                    );
267                                }
268
269                                // Get the field index from list_index saved during push
270                                let previous_index = parent_frame.istate.list_index.unwrap_or(1);
271                                let field_index = previous_index - 1; // -1 because we incremented *after* using the index in push
272
273                                if field_index >= variant.data.fields.len() {
274                                    panic!(
275                                        "Field index {} out of bounds for tuple enum variant '{}' of {} with {} fields",
276                                        field_index,
277                                        variant.name,
278                                        parent_shape,
279                                        variant.data.fields.len()
280                                    );
281                                }
282
283                                let field = &variant.data.fields[field_index];
284                                trace!(
285                                    "[{}] Setting tuple enum variant field {} ({}) of variant '{}' in {}",
286                                    frame_len,
287                                    field_index.to_string().yellow(),
288                                    field.name.bright_blue(),
289                                    variant.name.yellow(),
290                                    parent_shape.blue()
291                                );
292
293                                unsafe {
294                                    // Copy the element data to the tuple field within the enum's data payload
295                                    let field_ptr = parent_frame.data.field_uninit_at(field.offset);
296                                    field_ptr
297                                        .copy_from(
298                                            PtrConst::new(frame.data.as_byte_ptr()),
299                                            field.shape(),
300                                        )
301                                        .map_err(|_| ReflectError::Unsized {
302                                            shape: field.shape(),
303                                        })?; // Use ? to propagate potential unsized error
304
305                                    // Mark the specific field as initialized using its index
306                                    parent_frame.istate.fields.set(field_index);
307
308                                    // Mark the element as moved
309                                    self.mark_moved_out_of(&mut frame);
310                                }
311                            }
312
313                            // Handle Arrays
314                            _ if matches!(parent_shape.def, Def::Array(_)) => {
315                                // Get the element index from list_index saved during push
316                                let previous_index = parent_frame.istate.list_index.unwrap_or(1);
317                                let element_index = previous_index - 1; // -1 because we incremented *after* using the index in push
318
319                                let array_def = match parent_shape.def {
320                                    Def::Array(array_def) => array_def,
321                                    _ => unreachable!("Already checked this is an array"),
322                                };
323
324                                if element_index >= array_def.n {
325                                    panic!(
326                                        "Element index {} out of bounds for array {} with {} elements",
327                                        element_index, parent_shape, array_def.n
328                                    );
329                                }
330
331                                trace!(
332                                    "[{}] Setting array element {} of {}",
333                                    frame_len,
334                                    element_index.to_string().yellow(),
335                                    parent_shape.blue()
336                                );
337
338                                unsafe {
339                                    // Get raw pointer to the array data
340                                    let array_ptr = (array_def.vtable.as_mut_ptr)(PtrMut::new(
341                                        parent_frame.data.as_mut_byte_ptr(),
342                                    ));
343
344                                    // Calculate the element size and offset
345                                    let element_size = array_def
346                                        .t
347                                        .layout
348                                        .sized_layout()
349                                        .map_err(|_| ReflectError::Unsized { shape: array_def.t })?
350                                        .size();
351
352                                    // Calculate pointer to the right element in the array
353                                    let element_offset = element_size * element_index;
354                                    let element_ptr = PtrUninit::new(
355                                        array_ptr.as_byte_ptr().add(element_offset) as *mut u8,
356                                    );
357
358                                    // Copy the element data to the array
359                                    element_ptr
360                                        .copy_from(
361                                            PtrConst::new(frame.data.as_byte_ptr()),
362                                            frame.shape,
363                                        )
364                                        .map_err(|_| ReflectError::Unsized {
365                                            shape: frame.shape,
366                                        })?; // Use ? to propagate potential unsized error
367
368                                    // Check if the array is fully populated and mark it specially if it is
369                                    if previous_index == array_def.n {
370                                        trace!(
371                                            "[{}] Array {} fully populated with {} elements, marking as fully initialized",
372                                            frame_len,
373                                            parent_shape.blue(),
374                                            array_def.n.to_string().green()
375                                        );
376                                        // Mark the array itself as fully initialized
377                                        parent_frame.mark_fully_initialized();
378
379                                        // For nested arrays, also mark the parent field as initialized
380                                        if let Some(parent_field_index) =
381                                            parent_frame.field_index_in_parent
382                                        {
383                                            // Find the grandparent (skip to before the parent frame) if it exists
384                                            if self.frames.len() >= 3 {
385                                                let grandparent_index = self.frames.len() - 2;
386                                                if let Some(grandparent_frame) =
387                                                    self.frames.get_mut(grandparent_index)
388                                                {
389                                                    trace!(
390                                                        "[{}] Marking field {} in grandparent {} as initialized",
391                                                        frame_len,
392                                                        parent_field_index.to_string().yellow(),
393                                                        grandparent_frame.shape.blue()
394                                                    );
395                                                    grandparent_frame
396                                                        .istate
397                                                        .fields
398                                                        .set(parent_field_index);
399                                                }
400                                            }
401                                        }
402                                    }
403
404                                    // Mark the element as moved
405                                    self.mark_moved_out_of(&mut frame);
406                                }
407                            }
408
409                            // Unexpected parent type
410                            _ => {
411                                panic!(
412                                    "FrameMode::ListElement pop expected parent to be List, Tuple, Tuple Struct, Tuple Enum Variant, or Array, but got {}",
413                                    parent_shape
414                                );
415                            }
416                        },
417                    }
418                } else {
419                    // Frame not fully initialized, just deallocate if needed (handled by Frame drop later)
420                    trace!(
421                        "Popping uninitialized ListElement frame ({}), potential leak if allocated resources are not managed",
422                        frame.shape.yellow()
423                    );
424                }
425            }
426
427            // Handle map value frames
428            FrameMode::MapValue {
429                index: key_frame_index,
430            } if frame.is_fully_initialized() => {
431                // This was a map value, so we need to insert the key-value pair into the map
432
433                // Now let's remove the key frame from the frames array
434                let mut key_frame = self.frames.remove(key_frame_index);
435
436                // Make sure the key is fully initialized
437                if !key_frame.istate.fields.is_any_set() {
438                    panic!("key is not initialized when popping value frame");
439                }
440
441                // Get parent map frame
442                #[cfg(feature = "log")]
443                let frame_len = self.frames.len();
444                let parent_frame = self.frames.last_mut().unwrap();
445                let parent_shape = parent_frame.shape;
446
447                // Make sure the parent is a map
448                match parent_shape.def {
449                    Def::Map(_) => {
450                        // Get the map vtable from the MapDef
451                        if let Def::Map(map_def) = parent_shape.def {
452                            trace!(
453                                "[{}] Inserting key-value pair into map {}",
454                                frame_len,
455                                parent_shape.blue()
456                            );
457                            unsafe {
458                                // Call the map's insert function with the key and value
459                                (map_def.vtable.insert_fn)(
460                                    parent_frame.data.assume_init(),
461                                    key_frame.data.assume_init(),
462                                    PtrMut::new(frame.data.as_mut_byte_ptr()),
463                                );
464                                self.mark_moved_out_of(&mut key_frame);
465                                self.mark_moved_out_of(&mut frame);
466                            }
467                        } else {
468                            panic!("parent frame is not a map type");
469                        }
470                    }
471                    _ => {
472                        panic!("Expected map or hash map, got {}", frame.shape);
473                    }
474                }
475            }
476
477            // Handle option frames
478            FrameMode::OptionSome => {
479                if frame.is_fully_initialized() {
480                    trace!("Popping OptionSome (fully init'd)");
481
482                    // Get parent frame
483                    #[cfg(feature = "log")]
484                    let frames_len = self.frames.len();
485                    let parent_frame = self.frames.last_mut().unwrap();
486                    let parent_shape = parent_frame.shape;
487
488                    // Make sure the parent is an option
489                    match parent_shape.def {
490                        Def::Option(option_def) => {
491                            trace!(
492                                "[{}] Setting Some value in option {}",
493                                frames_len,
494                                parent_shape.blue()
495                            );
496                            unsafe {
497                                // Call the option's init_some function
498                                (option_def.vtable.init_some_fn)(
499                                    parent_frame.data,
500                                    PtrConst::new(frame.data.as_byte_ptr()),
501                                );
502                                trace!(
503                                    "Marking parent frame as fully initialized — its shape is {}",
504                                    parent_frame.shape
505                                );
506                                let variant = match parent_frame.shape.ty {
507                                    Type::User(UserType::Enum(EnumType { variants, .. })) => {
508                                        variants[1]
509                                    }
510                                    _ => Variant::builder()
511                                        .name("Some")
512                                        .discriminant(1)
513                                        .data(
514                                            StructType::builder()
515                                                .tuple()
516                                                .repr(Repr::default())
517                                                .build(),
518                                        )
519                                        .build(),
520                                };
521                                parent_frame.istate.variant = Some(variant); // the `Some` variant
522                                parent_frame.mark_fully_initialized();
523                                trace!(
524                                    "After marking: shape={} at {:p}, flags={:?}, mode={:?}, fully_initialized={}",
525                                    parent_frame.shape.blue(),
526                                    parent_frame.data.as_byte_ptr(),
527                                    parent_frame.istate.flags.bright_magenta(),
528                                    parent_frame.istate.mode.yellow(),
529                                    if parent_frame.is_fully_initialized() {
530                                        "✅"
531                                    } else {
532                                        "❌"
533                                    }
534                                );
535
536                                self.mark_moved_out_of(&mut frame);
537                            }
538                        }
539                        _ => {
540                            panic!(
541                                "Expected parent frame to be an option type, got {}",
542                                frame.shape
543                            );
544                        }
545                    }
546                } else {
547                    trace!("Popping OptionSome (not fully init'd)");
548                }
549            }
550
551            // Map keys are just tracked, they don't need special handling when popped
552            // FIXME: that's not true, we need to deallocate them at least??
553            FrameMode::MapKey => {}
554
555            // Field frame
556            FrameMode::Field => {}
557
558            // Uninitialized special frames
559            _ => {}
560        }
561
562        Ok(Some(frame))
563    }
564}