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