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}