swamp_code_gen/
iter.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use crate::FlagStateKind;
6use crate::code_bld::CodeBuilder;
7use crate::ctx::Context;
8use crate::transformer::{Collection, Transformer, TransformerResult};
9use source_map_node::Node;
10use swamp_semantic::{Expression, VariableRef};
11use swamp_vm_isa::{InstructionPosition, MemoryOffset};
12use swamp_vm_types::types::{
13    BasicTypeKind, BasicTypeRef, Place, TypedRegister, VmType, pointer_type, u8_type, u32_type,
14};
15use swamp_vm_types::{MemoryLocation, PatchPosition};
16use tracing::error;
17
18impl CodeBuilder<'_> {
19    /// Generates code to iterate over a collection using a transformer (e.g., map, filter, `filter_map`)
20    /// and a lambda expression. Handles creation of result vectors, iterator setup, lambda invocation,
21    /// early exit logic, and result collection.
22    ///
23    /// Steps:
24    /// 1. (Optional) Initialize a target vector for the result, if the transformer produces one.
25    /// 2. Initialize the iterator for the collection.
26    /// 3. Generate code to fetch the next element from the iterator.
27    /// 4. Spill lambda variables to protect them from being clobbered by function calls
28    /// 5. Inline the lambda code for the current element.
29    /// 6. Restore lambda variables from stack after lambda execution
30    /// 7. If the transformer supports early exit (e.g., filter, find), set the P flag based on the lambda result.
31    /// 8. Conditionally skip result insertion if early exit is triggered.
32    /// 9. (Optional) If applicable, insert the (possibly unwrapped) result into the target vector.
33    /// 10. Loop back to fetch the next element.
34    /// 11. Finalize iteration, handling any post-processing (e.g., normalizing boolean results).
35    ///
36    /// # Parameters
37    /// - `node`: The AST node for error reporting and code location.
38    /// - `collection_type`: The type of collection being iterated.
39    /// - `transformer`: The transformer operation (map, filter, find, fold, etc.).
40    /// - `collection_self_region`: Memory region of the collection.
41    /// - `lambda_expression`: The lambda expression to apply.
42    /// - `ctx`: Code generation context. Contains the result target.
43    ///
44    /// # Returns
45    /// - `Ok(())` on success, or an error if code generation fails.
46    ///
47    /// # Errors
48    /// // TODO:
49    /// # Panics
50    /// - If the lambda expression or its kind is not as expected (internal error).
51    #[allow(clippy::too_many_lines)]
52    #[allow(clippy::too_many_arguments)]
53    pub fn emit_iterate_over_collection_with_lambda(
54        &mut self,
55        target_destination: &Place,
56        node: &Node,
57        source_collection_type: Collection,
58        transformer: Transformer,
59        source_collection_reg: &TypedRegister,
60        lambda_tuple: (Vec<VariableRef>, &Expression),
61        ctx: &Context,
62    ) {
63        let (lambda_variables, lambda_expr) = lambda_tuple;
64        let maybe_primary_element_gen_type = source_collection_reg.ty.basic_type.element();
65
66        let target_variables: Vec<_> = lambda_variables
67            .iter()
68            .map(|x| {
69                self.variable_registers
70                    .get(&x.unique_id_within_function)
71                    .unwrap()
72                    .clone()
73            })
74            .collect();
75
76        // Primary is the right most variable
77        let primary_variable = &target_variables[target_variables.len() - 1];
78
79        let hwm = self.temp_registers.save_mark();
80
81        let temp_index_reg_for_mut_source = match transformer.return_type() {
82            TransformerResult::VecMutateSourceCollection => {
83                let temp_reg = self.temp_registers.allocate(
84                    VmType::new_contained_in_register(u32_type()),
85                    "mutate index",
86                );
87                self.builder.add_mov_32_immediate_value(
88                    temp_reg.register(),
89                    0,
90                    node,
91                    "initialize index register to zero",
92                );
93                Some(temp_reg.register().clone())
94            }
95            _ => None,
96        };
97
98        // 1. Initialize the target collection if needed and compute collection pointer
99        let maybe_target_collection_pointer = match transformer.return_type() {
100            TransformerResult::VecFromSourceCollection | TransformerResult::VecWithLambdaResult => {
101                // For transformers that create collections (filter, map, filterMap),
102                // we need to initialize the target vector before adding elements
103                let target_memory_location = target_destination.memory_location_or_pointer_reg();
104
105                self.emit_initialize_memory_for_any_type(
106                    &target_memory_location,
107                    node,
108                    "initialize target collection for transformer",
109                );
110
111                // For VecFromSourceCollection, compute the collection pointer now to avoid
112                // it being clobbered during lambda execution
113                if matches!(
114                    transformer.return_type(),
115                    TransformerResult::VecFromSourceCollection
116                ) {
117                    let computed_pointer = self.emit_compute_effective_address_to_register(
118                        target_destination,
119                        node,
120                        "get pointer to collection (before lambda execution)",
121                    );
122
123                    Some(computed_pointer)
124                } else {
125                    None
126                }
127            }
128            _ => {
129                // Other transformers don't need target initialization
130                None
131            }
132        };
133
134        // 2. Initialize the iterator and generate code to fetch the next element.
135        let (continue_iteration_label, iteration_complete_patch_position) = self
136            .emit_iter_init_and_next(
137                node,
138                source_collection_type,
139                maybe_primary_element_gen_type,
140                source_collection_reg,
141                &target_variables,
142            );
143
144        // 3. Inline the lambda code for the current element(s).
145        let lambda_result = self.emit_scalar_rvalue(lambda_expr, ctx);
146
147        // 4. If the transformer supports early exit, set the P flag based on the lambda result.
148        let transformer_t_flag_state = self.check_if_transformer_sets_t_flag(transformer);
149
150        // 5. Conditionally skip result insertion if early exit is triggered.
151        let maybe_skip_early = if self.check_if_transformer_can_skip_early(transformer)
152            && matches!(
153                transformer_t_flag_state,
154                FlagStateKind::TFlagIsTrueWhenSet | FlagStateKind::TFlagIsTrueWhenClear
155            ) {
156            let skip_early = match transformer.return_type() {
157                TransformerResult::WrappedValueFromSourceCollection => {
158                    let skip_over_some_section = self.builder.add_jmp_if_not_true_placeholder(
159                        &lambda_result,
160                        node,
161                        "skip over setting Some section",
162                    );
163
164                    let destination_type = target_destination.ty();
165                    let BasicTypeKind::Optional(tagged_union) = &destination_type.kind else {
166                        panic!("expected optional {destination_type:?}");
167                    };
168
169                    let tag_some_value_reg = self.temp_registers.allocate(
170                        VmType::new_unknown_placement(u8_type()),
171                        "iterate over collection target",
172                    );
173
174                    self.builder.add_mov8_immediate(
175                        tag_some_value_reg.register(),
176                        1,
177                        node,
178                        "mark tag as Some",
179                    );
180
181                    let result_location = target_destination.memory_location_or_pointer_reg();
182
183                    self.builder.add_st8_using_ptr_with_offset(
184                        &result_location.unsafe_add_offset(tagged_union.tag_offset),
185                        tag_some_value_reg.register(),
186                        node,
187                        "store Tag value of (1) into memory",
188                    );
189
190                    // TODO: let payload_memory_location = result_location.unsafe_add_offset(tagged_union.payload_offset);
191                    let payload_memory_location =
192                        result_location.unsafe_add_offset(MemoryOffset(4));
193                    let payload_destination = Place::Memory(payload_memory_location);
194
195                    let source_location = Place::Register(primary_variable.clone());
196
197                    self.emit_store_value_to_memory_place(
198                        &payload_destination,
199                        &source_location,
200                        node,
201                        "copy result in place",
202                    );
203
204                    // The P flag is set so we can act on it
205                    let skip_early = self.builder.add_jump_placeholder(node, "skip early");
206
207                    self.builder.patch_jump_here(skip_over_some_section);
208
209                    skip_early
210                }
211                _ => {
212                    // The P flag is set so we can act on it
213                    self.builder.add_jmp_if_not_equal_polarity_placeholder(
214                        &lambda_result,
215                        &transformer_t_flag_state.polarity(),
216                        node,
217                        "skip early",
218                    )
219                }
220            };
221
222            Some(skip_early)
223        } else {
224            // P flag is not set, we have to iterate through the whole collection
225            None
226        };
227
228        // 6. If applicable, insert the (possibly unwrapped) result into the target vector.
229
230        match transformer.return_type() {
231            TransformerResult::Unit => {
232
233                // Only alternative is that it is a bool return, so no need to take any action here
234            }
235            TransformerResult::Bool => {
236                // Only alternative is that it is a bool return, so no need to take any action here
237            }
238            TransformerResult::WrappedValueFromSourceCollection => {
239                // Handled elsewhere
240            }
241            TransformerResult::VecWithLambdaResult => {
242                self.transformer_add_to_collection(
243                    &lambda_result,
244                    transformer.needs_tag_removed(),
245                    source_collection_type,
246                    target_destination.register().unwrap(),
247                    node,
248                );
249            }
250            TransformerResult::VecFromSourceCollection => {
251                let skip_if_false_patch_position = self.builder.add_jmp_if_not_true_placeholder(
252                    &lambda_result,
253                    node,
254                    "skip the result if it is false",
255                );
256
257                let absolute_pointer = maybe_target_collection_pointer
258                    .as_ref()
259                    .expect("collection pointer should have been computed before lambda execution");
260
261                self.add_to_collection(
262                    node,
263                    source_collection_type,
264                    absolute_pointer,
265                    primary_variable,
266                );
267
268                self.builder.patch_jump_here(skip_if_false_patch_position);
269            }
270
271            TransformerResult::VecMutateSourceCollection => {
272                let skip_if_false_patch_position = self.builder.add_jmp_if_not_true_placeholder(
273                    &lambda_result,
274                    node,
275                    "skip the result if it is false",
276                );
277
278                // TODO: have a subscript index and overwrite
279                let index_reg = temp_index_reg_for_mut_source.unwrap();
280                let pointer_to_entry_reg = self.temp_registers.allocate(
281                    VmType::new_contained_in_register(pointer_type()),
282                    "holds pointer to vec entry",
283                );
284                self.builder.add_vec_subscript(
285                    pointer_to_entry_reg.register(),
286                    source_collection_reg,
287                    &index_reg,
288                    node,
289                    "get pointer to element",
290                );
291
292                self.builder.patch_jump_here(skip_if_false_patch_position);
293            }
294        }
295
296        // 7. Loop back to fetch the next element.
297        self.builder.add_jmp(
298            continue_iteration_label,
299            &lambda_expr.debug_last_expression().node,
300            "jump to iter_next",
301        );
302
303        self.builder
304            .patch_jump_here(iteration_complete_patch_position);
305
306        if let Some(found_skip_early) = maybe_skip_early {
307            self.builder.patch_jump_here(found_skip_early);
308        }
309
310        self.temp_registers.restore_to_mark(hwm);
311    }
312
313    #[allow(clippy::unnecessary_wraps)]
314    #[allow(clippy::too_many_lines)]
315    fn emit_iter_init_and_next(
316        &mut self,
317        node: &Node,
318        collection_type: Collection,
319        maybe_element_type: Option<BasicTypeRef>,
320        collection_self_addr: &TypedRegister,
321        target_variables: &[TypedRegister],
322    ) -> (InstructionPosition, PatchPosition) {
323        let iterator_gen_type = collection_type.iterator_gen_type();
324
325        let target_iterator_header_reg = self.allocate_frame_space_and_return_absolute_pointer_reg(
326            &iterator_gen_type,
327            false,
328            node,
329            "allocate iterator header space",
330        );
331
332        let primary_register = if target_variables.len() == 2 {
333            &target_variables[1]
334        } else {
335            &target_variables[0]
336        };
337
338        let iter_next_position = InstructionPosition(self.builder.position().0 + 1);
339        let placeholder = match collection_type {
340            Collection::Range => {
341                self.builder.add_range_iter_init(
342                    &target_iterator_header_reg,
343                    collection_self_addr,
344                    node,
345                    "range init",
346                );
347
348                assert_eq!(target_variables.len(), 1);
349                self.builder.add_range_iter_next_placeholder(
350                    &target_iterator_header_reg,
351                    &target_variables[0],
352                    node,
353                    "range iter next single",
354                )
355            }
356
357            Collection::Map => {
358                // For maps, we need to handle both key and value potentially being scalars
359                // Map iteration returns pointers to both key and value, so we need to load scalar values
360                // TODO: Clean this up a bit to be more easy to read
361                // TODO: Also, scalar values that are mutated should be stored back to the value register
362                // Keys are by design not allowed to be mutated in iteration.
363                let (temp_registers, target_variables_to_use) = if target_variables.len() == 2 {
364                    // Map iteration with key-value pairs
365                    let key_register = &target_variables[0];
366                    let value_register = &target_variables[1];
367
368                    let (key_is_scalar, value_is_scalar) =
369                        match &collection_self_addr.ty.basic_type.kind {
370                            BasicTypeKind::MapStorage {
371                                key_type,
372                                value_type,
373                                ..
374                            } => (key_type.is_scalar(), value_type.is_scalar()),
375                            BasicTypeKind::DynamicLengthMapView(key_item, value_item) => {
376                                (key_item.ty.is_scalar(), value_item.ty.is_scalar())
377                            }
378                            _ => (false, false),
379                        };
380
381                    let mut temp_regs = Vec::new();
382                    let mut target_vars = Vec::new();
383
384                    // Handle key
385                    if key_is_scalar {
386                        let temp_key_addr = self
387                            .temp_registers
388                            .allocate(key_register.ty.clone(), "temp address for key");
389                        temp_regs.push((temp_key_addr.register().clone(), key_register.clone()));
390                        target_vars.push(temp_key_addr.register);
391                    } else {
392                        target_vars.push(key_register.clone());
393                    }
394
395                    // Handle value
396                    if value_is_scalar {
397                        let temp_value_addr = self
398                            .temp_registers
399                            .allocate(value_register.ty.clone(), "temp address for value");
400                        temp_regs
401                            .push((temp_value_addr.register().clone(), value_register.clone()));
402                        target_vars.push(temp_value_addr.register);
403                    } else {
404                        target_vars.push(value_register.clone());
405                    }
406
407                    (Some(temp_regs), target_vars)
408                } else {
409                    // Single value iteration (values only)
410                    let value_register = &target_variables[0];
411                    let value_is_scalar = match &collection_self_addr.ty.basic_type.kind {
412                        BasicTypeKind::MapStorage { value_type, .. } => value_type.is_scalar(),
413                        BasicTypeKind::DynamicLengthMapView(_, value_item) => {
414                            value_item.ty.is_scalar()
415                        }
416                        _ => false,
417                    };
418
419                    if value_is_scalar {
420                        let temp_value_addr = self
421                            .temp_registers
422                            .allocate(value_register.ty.clone(), "temp address for value");
423                        (
424                            Some(vec![(
425                                temp_value_addr.register().clone(),
426                                value_register.clone(),
427                            )]),
428                            vec![temp_value_addr.register.clone()],
429                        )
430                    } else {
431                        (None, target_variables.to_vec())
432                    }
433                };
434
435                let patch_position = self.emit_iter_init_and_next_to_memory(
436                    &target_iterator_header_reg,
437                    node,
438                    collection_type,
439                    maybe_element_type,
440                    collection_self_addr,
441                    &target_variables_to_use,
442                );
443
444                // Load scalar values from the addresses returned by the map iterator
445                if let Some(temp_regs) = temp_registers {
446                    for (temp_addr_reg, target_scalar_reg) in temp_regs {
447                        let source_memory_location =
448                            MemoryLocation::new_copy_over_whole_type_with_zero_offset(
449                                temp_addr_reg,
450                            );
451                        self.emit_load_value_from_memory_source(
452                            &target_scalar_reg,
453                            &source_memory_location,
454                            node,
455                            "load scalar value from map iterator address",
456                        );
457                    }
458                }
459
460                patch_position
461            }
462            _ => {
463                let (temp_registers, target_variables_to_use) =
464                    if primary_register.ty.basic_type.is_scalar() {
465                        // For primitives, create temp register to hold the address, since they do not want
466                        // the address
467                        let temp_addr = self.temp_registers.allocate(
468                            VmType::new_contained_in_register(pointer_type()),
469                            "temp address for value",
470                        );
471
472                        if target_variables.len() == 2 {
473                            (
474                                Some(temp_addr.register().clone()),
475                                vec![target_variables[0].clone(), temp_addr.register.clone()],
476                            )
477                        } else {
478                            (
479                                Some(temp_addr.register().clone()),
480                                vec![temp_addr.register.clone()],
481                            )
482                        }
483                    } else {
484                        (None, target_variables.to_vec())
485                    };
486
487                let patch_position = self.emit_iter_init_and_next_to_memory(
488                    &target_iterator_header_reg,
489                    node,
490                    collection_type,
491                    maybe_element_type,
492                    collection_self_addr,
493                    &target_variables_to_use,
494                );
495
496                if let Some(temp_regs) = temp_registers {
497                    if collection_type == Collection::String {
498                        self.builder.add_mov_reg(
499                            primary_register,
500                            &temp_regs,
501                            node,
502                            "copy from temporary to primary scalar register",
503                        );
504                        //self.emit_copy_register(&temp_regs, primary_register, node, "comment");
505                    } else {
506                        let source_memory_location =
507                            MemoryLocation::new_copy_over_whole_type_with_zero_offset(temp_regs);
508                        self.emit_load_value_from_memory_source(
509                            primary_register,
510                            &source_memory_location,
511                            node,
512                            "load primitive from element address",
513                        );
514                    }
515                }
516
517                patch_position
518            }
519        };
520
521        (iter_next_position, placeholder)
522    }
523
524    fn emit_iter_init_and_next_to_memory(
525        &mut self,
526        target_iterator_header_reg: &TypedRegister,
527        node: &Node,
528        collection_type: Collection,
529        maybe_element_type: Option<BasicTypeRef>,
530        collection_self_addr: &TypedRegister,
531        target_variables: &[TypedRegister],
532    ) -> PatchPosition {
533        let primary_register = if target_variables.len() == 2 {
534            &target_variables[1]
535        } else {
536            &target_variables[0]
537        };
538        let is_pair = target_variables.len() == 2;
539        match collection_type {
540            // Low  prio
541            Collection::String => {
542                if maybe_element_type.is_none() {
543                    error!(
544                        ?collection_self_addr,
545                        "can no start iterating with this strange vec collection"
546                    );
547                }
548
549                self.builder.add_string_iter_init(
550                    target_iterator_header_reg,
551                    collection_self_addr,
552                    maybe_element_type.unwrap().total_size,
553                    node,
554                    "vec iter init",
555                );
556
557                let is_pair = target_variables.len() == 2;
558
559                if is_pair {
560                    // For non-primitives, use target directly
561                    self.builder.add_string_iter_next_pair_placeholder(
562                        target_iterator_header_reg,
563                        &target_variables[0],
564                        &target_variables[1],
565                        node,
566                        "vec iter next pair",
567                    )
568                } else {
569                    // For non-primitives, use target directly
570                    self.builder.add_string_iter_next_placeholder(
571                        target_iterator_header_reg,
572                        primary_register,
573                        node,
574                        "vec iter next single",
575                    )
576                }
577            }
578            Collection::Vec => {
579                if maybe_element_type.is_none() {
580                    error!(
581                        ?collection_self_addr,
582                        "can no start iterating with this strange vec collection"
583                    );
584                }
585
586                self.builder.add_vec_iter_init(
587                    target_iterator_header_reg,
588                    collection_self_addr,
589                    node,
590                    "vec iter init",
591                );
592
593                let is_pair = target_variables.len() == 2;
594
595                if is_pair {
596                    // For non-primitives, use target directly
597                    self.builder.add_vec_iter_next_pair_placeholder(
598                        target_iterator_header_reg,
599                        &target_variables[0],
600                        &target_variables[1],
601                        node,
602                        "vec iter next pair",
603                    )
604                } else {
605                    // For non-primitives, use target directly
606                    self.builder.add_vec_iter_next_placeholder(
607                        target_iterator_header_reg,
608                        primary_register,
609                        node,
610                        "vec iter next single",
611                    )
612                }
613            }
614            Collection::Sparse => {
615                self.builder.add_sparse_iter_init(
616                    target_iterator_header_reg,
617                    collection_self_addr,
618                    node,
619                    "sparse init",
620                );
621
622                if is_pair {
623                    self.builder.add_sparse_iter_next_pair_placeholder(
624                        target_iterator_header_reg,
625                        &target_variables[0],
626                        &target_variables[1],
627                        node,
628                        "sparse next_pair",
629                    )
630                } else {
631                    self.builder.add_sparse_iter_next_placeholder(
632                        target_iterator_header_reg,
633                        primary_register,
634                        node,
635                        "sparse next_single",
636                    )
637                }
638            }
639
640            Collection::Map => {
641                self.builder.add_map_iter_init(
642                    target_iterator_header_reg,
643                    collection_self_addr,
644                    node,
645                    "map init",
646                );
647
648                if target_variables.len() == 2 {
649                    self.builder.add_map_iter_next_pair_placeholder(
650                        target_iterator_header_reg,
651                        &target_variables[0],
652                        &target_variables[1],
653                        node,
654                        "map next_pair",
655                    )
656                } else {
657                    self.builder.add_map_iter_next_placeholder(
658                        target_iterator_header_reg,
659                        &target_variables[0],
660                        node,
661                        "map next_single",
662                    )
663                }
664            }
665            Collection::Grid => todo!(),
666            _ => panic!("unknown collection"),
667        }
668    }
669
670    const fn check_if_transformer_sets_t_flag(
671        &mut self,
672        transformer: Transformer,
673    ) -> FlagStateKind {
674        match transformer {
675            Transformer::For => FlagStateKind::TFlagIsIndeterminate,
676            Transformer::While => FlagStateKind::TFlagIsTrueWhenSet,
677            Transformer::Filter => {
678                // TODO: Bring this back //assert_eq!(in_value.size().0, 1); // bool
679                FlagStateKind::TFlagIsTrueWhenSet
680            }
681            Transformer::FilterMut => {
682                // TODO: Bring this back //assert_eq!(in_value.size().0, 1); // bool
683                FlagStateKind::TFlagIsTrueWhenSet
684            }
685            Transformer::Find => {
686                // TODO: Bring this back //assert_eq!(in_value.size().0, 1); // bool
687                FlagStateKind::TFlagIsTrueWhenClear
688            }
689            Transformer::Map => FlagStateKind::TFlagIsIndeterminate,
690            Transformer::Any => FlagStateKind::TFlagIsTrueWhenClear,
691            Transformer::All => FlagStateKind::TFlagIsTrueWhenSet,
692            Transformer::FilterMap => FlagStateKind::TFlagIsTrueWhenSet,
693        }
694    }
695
696    fn add_to_collection(
697        &mut self,
698        node: &Node,
699        collection: Collection,
700        mut_collection: &TypedRegister,
701        value: &TypedRegister,
702    ) {
703        match collection {
704            Collection::Vec => {
705                let target_element_addr_reg = self.temp_registers.allocate(
706                    VmType::new_contained_in_register(u32_type()),
707                    "address for the element entry",
708                );
709
710                self.builder.add_vec_push_addr(
711                    target_element_addr_reg.register(),
712                    mut_collection,
713                    node,
714                    "add_to_collection for transformer",
715                );
716
717                let vec_entry_destination =
718                    Place::Memory(MemoryLocation::new_copy_over_whole_type_with_zero_offset(
719                        target_element_addr_reg.register().clone(),
720                    ));
721
722                // Initialize the allocated space first (like variable definition)
723                if value.ty.basic_type.is_aggregate() {
724                    self.emit_initialize_memory_for_any_type(
725                        vec_entry_destination.grab_memory_location(),
726                        node,
727                        "initialize vec.push allocated space for transformer",
728                    );
729                }
730
731                let source_destination = if value.ty.is_scalar() {
732                    Place::Register(value.clone())
733                } else {
734                    Place::Memory(MemoryLocation::new_copy_over_whole_type_with_zero_offset(
735                        value.clone(),
736                    ))
737                };
738
739                self.emit_store_value_to_memory_place(
740                    &vec_entry_destination,
741                    &source_destination,
742                    node,
743                    "store value in the new vec entry slot",
744                );
745            }
746            Collection::Sparse => panic!("not supported by sparse"),
747            Collection::Map => todo!(),
748            Collection::Grid => todo!(),
749            Collection::String => todo!(),
750            Collection::Range => todo!(),
751        }
752    }
753
754    fn transformer_add_to_collection(
755        &mut self,
756        in_value: &TypedRegister,
757        should_unwrap_value: bool,
758        collection_type: Collection,
759        mut_collection: &TypedRegister,
760        node: &Node,
761    ) {
762        let (register_to_be_inserted_in_collection, _maybe_temp) = if should_unwrap_value {
763            let tagged_union = in_value.underlying().optional_info().unwrap().clone();
764            let some_variant = tagged_union.get_variant_by_index(1);
765            let payload_vm_type = VmType::new_unknown_placement(some_variant.ty.clone());
766            let temp_reg = self
767                .temp_registers
768                .allocate(payload_vm_type, "transform add to collection");
769            let (_tag_offset, _tag_size, _payload_offset, _) =
770                in_value.underlying().unwrap_info().unwrap();
771
772            /*
773            in_value.unsafe_add_offset(payload_offset);
774
775            self.emit_load_from_memory_internal(
776                temp_reg.register(),
777                in_value,
778                payload_offset,
779                &payload_vm_type,
780                node,
781                "transformer add to collection",
782            );
783
784             */
785            (temp_reg.register.clone(), Some(temp_reg))
786        } else {
787            (in_value.clone(), None)
788        };
789
790        self.add_to_collection(
791            node,
792            collection_type,
793            mut_collection,
794            &register_to_be_inserted_in_collection,
795        );
796    }
797
798    const fn check_if_transformer_can_skip_early(&self, transformer: Transformer) -> bool {
799        match transformer {
800            Transformer::For => false,
801            Transformer::Filter => false,
802            Transformer::FilterMut => false,
803            Transformer::Find => true,
804            Transformer::While => true,
805            Transformer::Map => false,
806            Transformer::Any => true,
807            Transformer::All => true,
808            Transformer::FilterMap => false,
809        }
810    }
811}