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