Skip to main content

aiken_lang/
gen_uplc.rs

1pub mod air;
2pub mod builder;
3pub mod decision_tree;
4pub mod interner;
5pub mod stick_break_set;
6pub mod tree;
7
8use self::{
9    air::Air,
10    builder::{
11        AssignmentProperties, CodeGenSpecialFuncs, CycleFunctionNames, HoistableFunction, Variant,
12        cast_validator_args, convert_type_to_data, extract_constant, modify_cyclic_calls,
13        modify_self_calls,
14    },
15    tree::{AirTree, TreePath},
16};
17use crate::{
18    IdGenerator,
19    ast::{
20        AssignmentKind, BinOp, Bls12_381Point, Curve, DataTypeKey, DecoratorKind,
21        FunctionAccessKey, Pattern, Span, TraceLevel, Tracing, TypedArg, TypedDataType,
22        TypedFunction, TypedPattern, TypedValidator, UnOp,
23    },
24    builtins::PRELUDE,
25    expr::TypedExpr,
26    gen_uplc::{
27        air::ExpectLevel,
28        builder::{
29            CodeGenFunction, erase_opaque_type_operations, get_generic_variant_name,
30            get_src_code_by_span, known_data_to_type, monomorphize, wrap_validator_condition,
31        },
32    },
33    line_numbers::LineNumbers,
34    plutus_version::PlutusVersion,
35    tipo::{
36        ModuleValueConstructor, PatternConstructor, Type, TypeInfo, TypeVar, ValueConstructor,
37        ValueConstructorVariant, check_replaceable_opaque_type, convert_opaque_type,
38        find_and_replace_generics, get_generic_id_and_type, lookup_data_type_by_tipo,
39    },
40};
41use builder::{
42    DISCARDED, get_constr_index_variant, introduce_name, introduce_pattern, pop_pattern,
43    softcast_data_to_type_otherwise, unknown_data_to_type,
44};
45use decision_tree::{Assigned, CaseTest, DecisionTree, TreeGen, get_tipo_by_path};
46use indexmap::IndexMap;
47use interner::AirInterner;
48use itertools::Itertools;
49use petgraph::{Graph, algo};
50use std::{collections::HashMap, rc::Rc};
51use stick_break_set::{Builtins, TreeSet};
52use tree::Fields;
53use uplc::{
54    ast::{Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType},
55    builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER, EXPECT_ON_LIST},
56    builtins::DefaultFunction,
57    machine::cost_model::ExBudget,
58    optimize::{aiken_optimize_and_intern, interner::CodeGenInterner, shrinker::NO_INLINE},
59};
60
61type Otherwise = Option<AirTree>;
62
63const DELAY_ERROR: fn() -> AirTree =
64    || AirTree::anon_func(vec![], AirTree::error(Type::void(), false), true);
65
66fn expect_decoder_function_name(tipo: &Type, has_otherwise: bool) -> String {
67    let mut name = "__expect".to_string();
68    push_type_identity(&mut name, tipo);
69
70    if has_otherwise {
71        name.push_str("_otherwise");
72    }
73
74    name
75}
76
77fn push_type_identity(name: &mut String, tipo: &Type) {
78    match tipo {
79        Type::App {
80            module,
81            name: type_name,
82            args,
83            ..
84        } => {
85            name.push_str("_app");
86            push_key_segment(name, module);
87            push_key_segment(name, type_name);
88            push_key_count(name, args.len());
89
90            for arg in args {
91                push_type_identity(name, arg);
92            }
93        }
94        Type::Fn { args, ret, .. } => {
95            name.push_str("_fn");
96            push_key_count(name, args.len());
97
98            for arg in args {
99                push_type_identity(name, arg);
100            }
101
102            push_type_identity(name, ret);
103        }
104        Type::Var { tipo, .. } => {
105            let tipo = tipo.borrow();
106
107            match &*tipo {
108                TypeVar::Link { tipo } => push_type_identity(name, tipo),
109                TypeVar::Unbound { id } => {
110                    unreachable!(
111                        "expect decoder key requires a bound type, found unbound variable {id}"
112                    )
113                }
114                TypeVar::Generic { id } => {
115                    unreachable!(
116                        "expect decoder key requires a concrete type, found generic variable {id}"
117                    )
118                }
119            }
120        }
121        Type::Tuple { elems, .. } => {
122            name.push_str("_tuple");
123            push_key_count(name, elems.len());
124
125            for elem in elems {
126                push_type_identity(name, elem);
127            }
128        }
129        Type::Pair { fst, snd, .. } => {
130            name.push_str("_pair");
131            push_type_identity(name, fst);
132            push_type_identity(name, snd);
133        }
134    }
135}
136
137fn push_key_segment(name: &mut String, segment: &str) {
138    name.push('_');
139    name.push_str(&segment.len().to_string());
140    name.push('_');
141    name.push_str(&hex::encode(segment));
142}
143
144fn push_key_count(name: &mut String, count: usize) {
145    name.push('_');
146    name.push_str(&count.to_string());
147}
148
149#[derive(Clone)]
150pub struct CodeGenerator<'a> {
151    #[allow(dead_code)]
152    plutus_version: PlutusVersion,
153    /// immutable index maps
154    functions: IndexMap<&'a FunctionAccessKey, &'a TypedFunction>,
155    constants: IndexMap<&'a FunctionAccessKey, &'a TypedExpr>,
156    data_types: IndexMap<&'a DataTypeKey, &'a TypedDataType>,
157    module_types: IndexMap<&'a str, &'a TypeInfo>,
158    module_src: IndexMap<&'a str, &'a (String, LineNumbers)>,
159    /// immutable option
160    tracing: TraceLevel,
161    /// mutable index maps that are reset
162    defined_functions: IndexMap<FunctionAccessKey, ()>,
163    special_functions: CodeGenSpecialFuncs,
164    code_gen_functions: IndexMap<String, CodeGenFunction>,
165    cyclic_functions:
166        IndexMap<(FunctionAccessKey, Variant), (CycleFunctionNames, usize, FunctionAccessKey)>,
167    /// mutable and reset as well
168    interner: AirInterner,
169    id_gen: IdGenerator,
170}
171
172impl<'a> CodeGenerator<'a> {
173    pub fn data_types(&self) -> &IndexMap<&'a DataTypeKey, &'a TypedDataType> {
174        &self.data_types
175    }
176
177    pub fn new(
178        plutus_version: PlutusVersion,
179        functions: IndexMap<&'a FunctionAccessKey, &'a TypedFunction>,
180        constants: IndexMap<&'a FunctionAccessKey, &'a TypedExpr>,
181        data_types: IndexMap<&'a DataTypeKey, &'a TypedDataType>,
182        module_types: IndexMap<&'a str, &'a TypeInfo>,
183        module_src: IndexMap<&'a str, &'a (String, LineNumbers)>,
184        tracing: Tracing,
185    ) -> Self {
186        CodeGenerator {
187            plutus_version,
188            functions,
189            constants,
190            data_types,
191            module_types,
192            module_src,
193            tracing: tracing.trace_level(true),
194            defined_functions: IndexMap::new(),
195            special_functions: CodeGenSpecialFuncs::new(),
196            code_gen_functions: IndexMap::new(),
197            cyclic_functions: IndexMap::new(),
198            interner: AirInterner::new(),
199            id_gen: IdGenerator::new(),
200        }
201    }
202
203    pub fn reset(&mut self, reset_special_functions: bool) {
204        self.code_gen_functions = IndexMap::new();
205        self.defined_functions = IndexMap::new();
206        self.cyclic_functions = IndexMap::new();
207        self.interner = AirInterner::new();
208        self.id_gen = IdGenerator::new();
209        if reset_special_functions {
210            self.special_functions = CodeGenSpecialFuncs::new();
211        }
212    }
213
214    pub fn generate(&mut self, validator: &TypedValidator, module_name: &str) -> Program<Name> {
215        let context_name = "__context__".to_string();
216        let context_name_interned = introduce_name(&mut self.interner, &context_name);
217        validator.params.iter().for_each(|arg| {
218            arg.get_variable_name()
219                .iter()
220                .for_each(|arg_name| self.interner.intern(arg_name.to_string()))
221        });
222
223        let air_tree_fun = wrap_validator_condition(
224            self.build(&validator.into_script_context_handler(), module_name, &[]),
225            self.tracing,
226        );
227
228        let air_tree_fun = AirTree::anon_func(vec![context_name_interned], air_tree_fun, true);
229
230        let validator_args_tree = AirTree::no_op(air_tree_fun);
231
232        let full_tree = self.hoist_functions_to_validator(validator_args_tree);
233
234        // optimizations on air tree
235
236        let full_vec = full_tree.to_vec();
237
238        let term = self.uplc_code_gen(full_vec);
239
240        let term = cast_validator_args(term, &validator.params, &self.interner, &self.data_types);
241
242        self.interner.pop_text(context_name);
243        validator.params.iter().for_each(|arg| {
244            arg.get_variable_name()
245                .iter()
246                .for_each(|arg_name| self.interner.pop_text(arg_name.to_string()))
247        });
248
249        self.finalize(term)
250    }
251
252    pub fn generate_raw(
253        &mut self,
254        body: &TypedExpr,
255        args: &[TypedArg],
256        module_name: &str,
257    ) -> Program<Name> {
258        args.iter().for_each(|arg| {
259            arg.get_variable_name()
260                .iter()
261                .for_each(|arg_name| self.interner.intern(arg_name.to_string()))
262        });
263
264        let mut air_tree = self.build(body, module_name, &[]);
265
266        air_tree = AirTree::no_op(air_tree);
267
268        let full_tree = self.hoist_functions_to_validator(air_tree);
269
270        // optimizations on air tree
271        let full_vec = full_tree.to_vec();
272
273        let mut term = self.uplc_code_gen(full_vec);
274
275        term = if args.is_empty() {
276            term
277        } else {
278            cast_validator_args(term, args, &self.interner, &self.data_types)
279        };
280
281        args.iter().for_each(|arg| {
282            arg.get_variable_name()
283                .iter()
284                .for_each(|arg_name| self.interner.pop_text(arg_name.to_string()))
285        });
286
287        self.finalize(term)
288    }
289
290    fn new_program<T>(&self, term: Term<T>) -> Program<T> {
291        let version = match self.plutus_version {
292            PlutusVersion::V1 | PlutusVersion::V2 => (1, 0, 0),
293            PlutusVersion::V3 => (1, 1, 0),
294        };
295
296        Program { version, term }
297    }
298
299    fn finalize(&mut self, mut term: Term<Name>) -> Program<Name> {
300        term = self.special_functions.apply_used_functions(term);
301
302        let program = aiken_optimize_and_intern(self.new_program(term));
303
304        // This is very important to call here.
305        // If this isn't done, re-using the same instance
306        // of the generator will result in free unique errors
307        // among other unpredictable things. In fact,
308        // switching to a shared code generator caused some
309        // instability issues and we fixed it by placing this
310        // method here.
311        self.reset(true);
312
313        program
314    }
315
316    // TODO: pass mono types to build so monomorphization
317    // happens as we build the AIR Tree rather than after
318    fn build(
319        &mut self,
320        body: &TypedExpr,
321        module_build_name: &str,
322        context: &[TypedExpr],
323    ) -> AirTree {
324        if !context.is_empty() {
325            let TypedExpr::Assignment {
326                location,
327                tipo,
328                value,
329                pattern,
330                kind,
331                comment,
332            } = body
333            else {
334                panic!("Dangling expressions without an assignment")
335            };
336
337            let air_value = self.build(value, module_build_name, &[]);
338
339            let otherwise_delayed = {
340                let msg = match (self.tracing, kind) {
341                    (TraceLevel::Silent, _) | (_, AssignmentKind::Let { .. }) => "".to_string(),
342                    (TraceLevel::Compact, _) => {
343                        match comment.as_ref().and_then(|s| s.split(":").next()) {
344                            None => "".to_string(),
345                            Some(label) => format!("<expected> {label}"),
346                        }
347                    }
348                    (TraceLevel::Verbose, _) => match comment.as_ref() {
349                        None => get_src_code_by_span(module_build_name, location, &self.module_src),
350                        Some(comment) => format!("<expected> {comment}"),
351                    },
352                };
353
354                let msg_func_name = msg.split_whitespace().join("");
355
356                if msg_func_name.is_empty() {
357                    None
358                } else {
359                    self.special_functions.insert_new_function(
360                        msg_func_name.clone(),
361                        Term::Error.delayed_trace(Term::string(msg)).delay(),
362                        Type::void(),
363                    );
364
365                    Some(self.special_functions.use_function_tree(msg_func_name))
366                }
367            };
368
369            // Intern vars from pattern here
370            introduce_pattern(&mut self.interner, pattern);
371
372            let (then, context) = context.split_first().unwrap();
373
374            let then = self.build(then, module_build_name, context);
375
376            let tree = self.assignment(
377                pattern,
378                air_value,
379                then,
380                tipo,
381                AssignmentProperties {
382                    value_type: value.tipo(),
383                    kind: *kind,
384                    remove_unused: kind.is_let(),
385                    full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(),
386                    otherwise: otherwise_delayed,
387                },
388            );
389
390            // Now pop off interned pattern
391            pop_pattern(&mut self.interner, pattern);
392
393            tree
394        } else {
395            match body {
396                TypedExpr::Assignment { .. } => {
397                    panic!("Reached assignment with no dangling expressions")
398                }
399                TypedExpr::UInt { value, .. } => AirTree::int(value),
400                TypedExpr::String { value, .. } => AirTree::string(value),
401                TypedExpr::ByteArray { bytes, .. } => AirTree::byte_array(bytes.clone()),
402                TypedExpr::Sequence { expressions, .. }
403                | TypedExpr::Pipeline { expressions, .. } => {
404                    let (expr, dangling_expressions) = expressions
405                        .split_first()
406                        .expect("Sequence or Pipeline should have at least one expression");
407                    self.build(expr, module_build_name, dangling_expressions)
408                }
409
410                TypedExpr::Var {
411                    constructor, name, ..
412                } => match constructor.variant {
413                    ValueConstructorVariant::LocalVariable { .. } => {
414                        if name != CONSTR_INDEX_EXPOSER && name != CONSTR_FIELDS_EXPOSER {
415                            AirTree::var(
416                                constructor.clone(),
417                                self.interner.lookup_interned(name),
418                                "",
419                            )
420                        } else {
421                            AirTree::var(constructor.clone(), name, "")
422                        }
423                    }
424                    _ => AirTree::var(constructor.clone(), name, ""),
425                },
426
427                TypedExpr::Fn { args, body, .. } => {
428                    let params = args
429                        .iter()
430                        .map(|arg| {
431                            arg.get_variable_name()
432                                .map(|arg| introduce_name(&mut self.interner, &arg.to_string()))
433                                .unwrap_or_else(|| DISCARDED.to_string())
434                        })
435                        .collect_vec();
436
437                    let anon =
438                        AirTree::anon_func(params, self.build(body, module_build_name, &[]), false);
439
440                    args.iter()
441                        .filter_map(|arg| arg.get_variable_name())
442                        .for_each(|arg| {
443                            self.interner.pop_text(arg.to_string());
444                        });
445
446                    anon
447                }
448
449                TypedExpr::List {
450                    tipo,
451                    elements,
452                    tail,
453                    ..
454                } => AirTree::list(
455                    elements
456                        .iter()
457                        .map(|elem| self.build(elem, module_build_name, &[]))
458                        .collect_vec(),
459                    tipo.clone(),
460                    tail.as_ref()
461                        .map(|tail| self.build(tail, module_build_name, &[])),
462                ),
463
464                TypedExpr::Call {
465                    tipo, fun, args, ..
466                } => match fun.as_ref() {
467                    TypedExpr::Var {
468                        constructor:
469                            ValueConstructor {
470                                variant:
471                                    ValueConstructorVariant::Record {
472                                        name: constr_name, ..
473                                    },
474                                tipo: constr_tipo,
475                                ..
476                            },
477                        ..
478                    }
479                    | TypedExpr::ModuleSelect {
480                        constructor:
481                            ModuleValueConstructor::Record {
482                                name: constr_name,
483                                tipo: constr_tipo,
484                                ..
485                            },
486                        ..
487                    } => {
488                        let data_type = lookup_data_type_by_tipo(&self.data_types, tipo)
489                            .unwrap_or_else(||
490                                panic!(
491                                    "Creating a record of type {:?} with no record definition. Known definitions: {:?}",
492                                    tipo.to_pretty(0),
493                                    self.data_types.keys()
494                                )
495                            );
496
497                        let err = &format!("Missing constr variant {constr_name}");
498                        let (constr_index, _) =
499                            get_constr_index_variant(&data_type, constr_name).expect(err);
500
501                        let constr_args = args
502                            .iter()
503                            .zip(constr_tipo.arg_types().unwrap())
504                            .map(|(arg, tipo)| {
505                                if tipo.is_data() {
506                                    AirTree::cast_to_data(
507                                        self.build(&arg.value, module_build_name, &[]),
508                                        arg.value.tipo(),
509                                    )
510                                } else {
511                                    self.build(&arg.value, module_build_name, &[])
512                                }
513                            })
514                            .collect_vec();
515
516                        let index = if data_type
517                            .decorators
518                            .iter()
519                            .any(|dec| matches!(dec.kind, DecoratorKind::List))
520                        {
521                            None
522                        } else {
523                            Some(constr_index)
524                        };
525
526                        AirTree::create_constr(index, constr_tipo.clone(), constr_args)
527                    }
528
529                    TypedExpr::Var {
530                        constructor:
531                            ValueConstructor {
532                                variant: ValueConstructorVariant::ModuleFn { builtin, .. },
533                                ..
534                            },
535                        ..
536                    } => {
537                        let fun_arg_types = fun
538                            .tipo()
539                            .arg_types()
540                            .expect("Expected a function type with arguments");
541
542                        assert!(args.len() == fun_arg_types.len());
543
544                        let func_args = args
545                            .iter()
546                            .zip(fun_arg_types)
547                            .map(|(arg, arg_tipo)| {
548                                let mut arg_val = self.build(&arg.value, module_build_name, &[]);
549                                if arg_tipo.is_data() && !arg.value.tipo().is_data() {
550                                    arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo())
551                                }
552                                arg_val
553                            })
554                            .collect_vec();
555
556                        if let Some(func) = builtin {
557                            AirTree::builtin(*func, tipo.clone(), func_args)
558                        } else {
559                            AirTree::call(
560                                self.build(fun.as_ref(), module_build_name, &[]),
561                                tipo.clone(),
562                                func_args,
563                            )
564                        }
565                    }
566
567                    TypedExpr::ModuleSelect {
568                        module_name,
569                        constructor: ModuleValueConstructor::Fn { name, .. },
570                        ..
571                    } => {
572                        let type_info = self.module_types.get(module_name.as_str()).unwrap();
573                        let value = type_info.values.get(name).unwrap();
574
575                        let ValueConstructorVariant::ModuleFn { builtin, .. } = &value.variant
576                        else {
577                            unreachable!("Missing module function definition")
578                        };
579
580                        let fun_arg_types = fun
581                            .tipo()
582                            .arg_types()
583                            .expect("Expected a function type with arguments");
584
585                        assert!(args.len() == fun_arg_types.len());
586
587                        let func_args = args
588                            .iter()
589                            .zip(fun_arg_types)
590                            .map(|(arg, arg_tipo)| {
591                                let mut arg_val = self.build(&arg.value, module_build_name, &[]);
592
593                                if arg_tipo.is_data() && !arg.value.tipo().is_data() {
594                                    arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo())
595                                }
596                                arg_val
597                            })
598                            .collect_vec();
599
600                        if let Some(func) = builtin {
601                            AirTree::builtin(*func, tipo.clone(), func_args)
602                        } else {
603                            AirTree::call(
604                                self.build(fun.as_ref(), module_build_name, &[]),
605                                tipo.clone(),
606                                func_args,
607                            )
608                        }
609                    }
610                    _ => {
611                        let fun_arg_types = fun
612                            .tipo()
613                            .arg_types()
614                            .expect("Expected a function type with arguments");
615
616                        assert!(args.len() == fun_arg_types.len());
617
618                        let func_args = args
619                            .iter()
620                            .zip(fun_arg_types)
621                            .map(|(arg, arg_tipo)| {
622                                let mut arg_val = self.build(&arg.value, module_build_name, &[]);
623                                if arg_tipo.is_data() && !arg.value.tipo().is_data() {
624                                    arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo())
625                                }
626                                arg_val
627                            })
628                            .collect_vec();
629
630                        AirTree::call(
631                            self.build(fun.as_ref(), module_build_name, &[]),
632                            tipo.clone(),
633                            func_args,
634                        )
635                    }
636                },
637                TypedExpr::BinOp {
638                    name,
639                    left,
640                    right,
641                    tipo,
642                    ..
643                } => AirTree::binop(
644                    *name,
645                    tipo.clone(),
646                    self.build(left, module_build_name, &[]),
647                    self.build(right, module_build_name, &[]),
648                    left.tipo(),
649                    right.tipo(),
650                ),
651
652                TypedExpr::Trace {
653                    tipo, then, text, ..
654                } => AirTree::trace(
655                    self.build(text, module_build_name, &[]),
656                    tipo.clone(),
657                    self.build(then, module_build_name, &[]),
658                ),
659
660                TypedExpr::When {
661                    subject,
662                    clauses,
663                    tipo,
664                    ..
665                } => {
666                    if clauses.is_empty() {
667                        unreachable!("We should have one clause at least")
668                    } else if clauses.len() == 1 {
669                        let subject_val = self.build(subject, module_build_name, &[]);
670
671                        let last_clause = &clauses[0];
672
673                        // Intern vars from pattern here
674                        introduce_pattern(&mut self.interner, &last_clause.pattern);
675
676                        let clause_then = self.build(&last_clause.then, module_build_name, &[]);
677
678                        let subject_type = subject.tipo();
679
680                        let tree = self.assignment(
681                            &last_clause.pattern,
682                            subject_val,
683                            clause_then,
684                            &subject_type,
685                            AssignmentProperties {
686                                value_type: subject.tipo(),
687                                kind: AssignmentKind::let_(),
688                                remove_unused: false,
689                                full_check: false,
690                                otherwise: None,
691                            },
692                        );
693
694                        // Now pop off interned pattern
695                        pop_pattern(&mut self.interner, &last_clause.pattern);
696
697                        tree
698                    } else {
699                        let subject_name = format!(
700                            "__subject_var_span_{}_{}",
701                            subject.location().start,
702                            subject.location().end
703                        );
704
705                        self.interner.intern(subject_name.clone());
706
707                        let subject_name_interned = self.interner.lookup_interned(&subject_name);
708
709                        let wild_card = TypedPattern::Discard {
710                            name: "".to_string(),
711                            location: Span::empty(),
712                        };
713
714                        let tree_gen =
715                            TreeGen::new(&mut self.interner, &self.data_types, &wild_card);
716
717                        let tree = tree_gen.build_tree(&subject.tipo(), clauses);
718
719                        let stick_set = TreeSet::new();
720
721                        let clauses = self.handle_decision_tree(
722                            &subject_name_interned,
723                            subject.tipo(),
724                            tipo.clone(),
725                            module_build_name,
726                            tree,
727                            stick_set,
728                        );
729
730                        self.interner.pop_text(subject_name);
731
732                        AirTree::let_assignment(
733                            subject_name_interned,
734                            self.build(subject, module_build_name, &[]),
735                            clauses,
736                        )
737                    }
738                }
739
740                TypedExpr::If {
741                    branches,
742                    final_else,
743                    tipo,
744                    ..
745                } => {
746                    branches.iter().rfold(
747                        self.build(final_else, module_build_name, &[]),
748                        |acc, branch| {
749                            let condition = self.build(&branch.condition, module_build_name, &[]);
750
751                            match &branch.is {
752                                Some((pattern, tipo)) => {
753                                    introduce_pattern(&mut self.interner, pattern);
754                                    self.interner.intern("acc_var".to_string());
755
756                                    let body = self.build(&branch.body, module_build_name, &[]);
757
758                                    let acc_var =
759                                        self.interner.lookup_interned(&"acc_var".to_string());
760
761                                    let tree = AirTree::let_assignment(
762                                        &acc_var,
763                                        // use anon function as a delay to avoid evaluating the acc
764                                        AirTree::anon_func(vec![], acc, true),
765                                        self.assignment(
766                                            pattern,
767                                            condition,
768                                            body,
769                                            tipo,
770                                            AssignmentProperties {
771                                                value_type: branch.condition.tipo(),
772                                                kind: AssignmentKind::Expect { backpassing: () },
773                                                remove_unused: false,
774                                                full_check: true,
775                                                otherwise: Some(AirTree::local_var(
776                                                    &acc_var,
777                                                    tipo.clone(),
778                                                )),
779                                            },
780                                        ),
781                                    );
782
783                                    pop_pattern(&mut self.interner, pattern);
784                                    self.interner.pop_text("acc_var".to_string());
785
786                                    tree
787                                }
788                                None => AirTree::if_branch(
789                                    tipo.clone(),
790                                    condition,
791                                    self.build(&branch.body, module_build_name, &[]),
792                                    acc,
793                                ),
794                            }
795                        },
796                    )
797                }
798                TypedExpr::RecordAccess {
799                    tipo,
800                    index,
801                    record,
802                    ..
803                } => {
804                    assert!(
805                        !record.tipo().is_pair(),
806                        "illegal record access on a Pair. This should have been a pair-index access."
807                    );
808
809                    if check_replaceable_opaque_type(&record.tipo(), &self.data_types) {
810                        self.build(record, module_build_name, &[])
811                    } else {
812                        let function_name = format!("__access_index_{}", *index);
813
814                        if self.code_gen_functions.get(&function_name).is_none() {
815                            let mut body = AirTree::local_var("__fields", Type::list(Type::data()));
816
817                            for _ in 0..*index {
818                                body = AirTree::builtin(
819                                    DefaultFunction::TailList,
820                                    Type::list(Type::data()),
821                                    vec![body],
822                                )
823                            }
824
825                            body = AirTree::builtin(
826                                DefaultFunction::HeadList,
827                                Type::data(),
828                                vec![body],
829                            );
830
831                            self.code_gen_functions.insert(
832                                function_name.clone(),
833                                CodeGenFunction::Function {
834                                    body,
835                                    params: vec!["__fields".to_string()],
836                                },
837                            );
838                        }
839
840                        let err =
841                            format!("Missing record data type for type: {:#?}", record.tipo());
842
843                        let record_data_type =
844                            lookup_data_type_by_tipo(self.data_types(), &record.tipo())
845                                .expect(&err);
846
847                        let list_of_fields = if record_data_type
848                            .decorators
849                            .iter()
850                            .any(|dec| matches!(dec.kind, DecoratorKind::List))
851                        {
852                            self.build(record, module_build_name, &[])
853                        } else {
854                            AirTree::call(
855                                self.special_functions
856                                    .use_function_tree(CONSTR_FIELDS_EXPOSER.to_string()),
857                                Type::list(Type::data()),
858                                vec![self.build(record, module_build_name, &[])],
859                            )
860                        };
861
862                        AirTree::index_access(function_name, tipo.clone(), list_of_fields)
863                    }
864                }
865
866                TypedExpr::ModuleSelect {
867                    tipo,
868                    module_name,
869                    constructor,
870                    ..
871                } => match constructor {
872                    ModuleValueConstructor::Record {
873                        name,
874                        arity,
875                        tipo,
876                        field_map,
877                        ..
878                    } => {
879                        let val_constructor = {
880                            let data_type = lookup_data_type_by_tipo(&self.data_types, tipo);
881
882                            ValueConstructor::public(
883                                tipo.clone(),
884                                ValueConstructorVariant::Record {
885                                    name: name.clone(),
886                                    arity: *arity,
887                                    field_map: field_map.clone(),
888                                    location: Span::empty(),
889                                    module: module_name.clone(),
890                                    constructors_count: data_type
891                                        .expect("Created a module type without a definition?")
892                                        .constructors
893                                        .len()
894                                        as u16,
895                                },
896                            )
897                        };
898
899                        AirTree::var(val_constructor, name, "")
900                    }
901                    ModuleValueConstructor::Fn { name, module, .. } => {
902                        let func = self.functions.get(&FunctionAccessKey {
903                            // NOTE: This is needed because we register prelude functions under an
904                            // empty module name. This is to facilitate their access when used
905                            // directly. Note that, if we weren't doing this particular
906                            // transformation, we would need to do the other direction anyway:
907                            //
908                            //     if module_name.is_empty() { PRELUDE.to_string() } else { module_name.clone() }
909                            //
910                            // So either way, we need to take care of this.
911                            module_name: if module_name == PRELUDE {
912                                String::new()
913                            } else {
914                                module_name.clone()
915                            },
916                            function_name: name.clone(),
917                        });
918
919                        let type_info = self.module_types.get(module_name.as_str()).unwrap();
920
921                        let value = type_info.values.get(name).unwrap();
922
923                        if let Some(_func) = func {
924                            AirTree::var(
925                                ValueConstructor::public(tipo.clone(), value.variant.clone()),
926                                format!("{module}_{name}"),
927                                "",
928                            )
929                        } else {
930                            let ValueConstructorVariant::ModuleFn {
931                                builtin: Some(builtin),
932                                ..
933                            } = &value.variant
934                            else {
935                                unreachable!("Didn't find the function definition.")
936                            };
937
938                            AirTree::builtin(*builtin, tipo.clone(), vec![])
939                        }
940                    }
941                    ModuleValueConstructor::Constant { module, name, .. } => {
942                        let type_info = self.module_types.get(module_name.as_str()).unwrap();
943
944                        let value = type_info.values.get(name).unwrap();
945
946                        AirTree::var(
947                            ValueConstructor::public(tipo.clone(), value.variant.clone()),
948                            format!("{module}_{name}"),
949                            "",
950                        )
951                    }
952                },
953
954                TypedExpr::Pair { tipo, fst, snd, .. } => AirTree::pair(
955                    self.build(fst, module_build_name, &[]),
956                    self.build(snd, module_build_name, &[]),
957                    tipo.clone(),
958                ),
959
960                TypedExpr::Tuple { tipo, elems, .. } => AirTree::tuple(
961                    elems
962                        .iter()
963                        .map(|elem| self.build(elem, module_build_name, &[]))
964                        .collect_vec(),
965                    tipo.clone(),
966                ),
967
968                TypedExpr::TupleIndex {
969                    index, tuple, tipo, ..
970                } => {
971                    if tuple.tipo().is_pair() {
972                        AirTree::pair_index(
973                            *index,
974                            tipo.clone(),
975                            self.build(tuple, module_build_name, &[]),
976                        )
977                    } else {
978                        let function_name = format!("__access_index_{}", *index);
979
980                        if self.code_gen_functions.get(&function_name).is_none() {
981                            let mut body = AirTree::local_var("__fields", Type::list(Type::data()));
982
983                            for _ in 0..*index {
984                                body = AirTree::builtin(
985                                    DefaultFunction::TailList,
986                                    Type::list(Type::data()),
987                                    vec![body],
988                                )
989                            }
990
991                            body = AirTree::builtin(
992                                DefaultFunction::HeadList,
993                                Type::data(),
994                                vec![body],
995                            );
996
997                            self.code_gen_functions.insert(
998                                function_name.clone(),
999                                CodeGenFunction::Function {
1000                                    body,
1001                                    params: vec!["__fields".to_string()],
1002                                },
1003                            );
1004                        }
1005
1006                        AirTree::index_access(
1007                            function_name,
1008                            tipo.clone(),
1009                            self.build(tuple, module_build_name, &[]),
1010                        )
1011                    }
1012                }
1013
1014                TypedExpr::ErrorTerm { tipo, .. } => AirTree::error(tipo.clone(), false),
1015
1016                TypedExpr::RecordUpdate {
1017                    tipo, spread, args, ..
1018                } => {
1019                    let mut index_types = vec![];
1020                    let mut update_args = vec![];
1021
1022                    let mut highest_index = 0;
1023
1024                    for arg in args
1025                        .iter()
1026                        .sorted_by(|arg1, arg2| arg1.index.cmp(&arg2.index))
1027                    {
1028                        let arg_val = self.build(&arg.value, module_build_name, &[]);
1029
1030                        if arg.index > highest_index {
1031                            highest_index = arg.index;
1032                        }
1033
1034                        index_types.push((arg.index, arg.value.tipo()));
1035                        update_args.push(arg_val);
1036                    }
1037
1038                    AirTree::record_update(
1039                        index_types,
1040                        highest_index,
1041                        tipo.clone(),
1042                        self.build(spread, module_build_name, &[]),
1043                        update_args,
1044                    )
1045                }
1046                TypedExpr::UnOp { value, op, .. } => {
1047                    AirTree::unop(*op, self.build(value, module_build_name, &[]))
1048                }
1049                TypedExpr::CurvePoint { point, .. } => AirTree::curve(*point.as_ref()),
1050            }
1051        }
1052    }
1053
1054    pub fn assignment(
1055        &mut self,
1056        pattern: &TypedPattern,
1057        value: AirTree,
1058        then: AirTree,
1059        tipo: &Rc<Type>,
1060        props: AssignmentProperties,
1061    ) -> AirTree {
1062        assert!(
1063            match &value {
1064                AirTree::Var { name, .. } if props.kind.is_let() => {
1065                    name != DISCARDED
1066                }
1067                _ => true,
1068            },
1069            "No discard expressions or let bindings should be in the tree at this point."
1070        );
1071
1072        // Cast value to or from data so we don't have to worry from this point onward
1073        let assign_casted_value = |name, value, then| {
1074            if props.value_type.is_data() && props.kind.is_expect() && !tipo.is_data() {
1075                if let Some(otherwise) = props.otherwise.as_ref() {
1076                    AirTree::soft_cast_assignment(
1077                        name,
1078                        tipo.clone(),
1079                        value,
1080                        then,
1081                        otherwise.clone(),
1082                    )
1083                } else {
1084                    AirTree::let_assignment(
1085                        name,
1086                        AirTree::cast_from_data(value, tipo.clone(), true),
1087                        then,
1088                    )
1089                }
1090            } else if !props.value_type.is_data() && tipo.is_data() {
1091                AirTree::let_assignment(
1092                    name,
1093                    AirTree::cast_to_data(value, props.value_type.clone()),
1094                    then,
1095                )
1096            } else {
1097                AirTree::let_assignment(name, value, then)
1098            }
1099        };
1100
1101        let otherwise = match &props.otherwise {
1102            Some(x) => x.clone(),
1103            // (delay (error ))
1104            None => DELAY_ERROR(),
1105        };
1106
1107        match pattern {
1108            Pattern::Int {
1109                value: expected_int,
1110                location,
1111                ..
1112            } => {
1113                let name = format!(
1114                    "__expected_by_{}_span_{}_{}",
1115                    expected_int, location.start, location.end
1116                );
1117
1118                let expect = AirTree::binop(
1119                    BinOp::Eq,
1120                    Type::bool(),
1121                    AirTree::int(expected_int),
1122                    AirTree::local_var(&name, Type::int()),
1123                    Type::int(),
1124                    Type::int(),
1125                );
1126
1127                assign_casted_value(
1128                    name,
1129                    value,
1130                    AirTree::assert_bool(true, expect, then, otherwise),
1131                )
1132            }
1133
1134            Pattern::ByteArray {
1135                value: expected_bytes,
1136                location,
1137                ..
1138            } => {
1139                let name = format!("__expected_bytes_span_{}_{}", location.start, location.end);
1140
1141                let expect = AirTree::binop(
1142                    BinOp::Eq,
1143                    Type::bool(),
1144                    AirTree::byte_array(expected_bytes.clone()),
1145                    AirTree::local_var(&name, Type::byte_array()),
1146                    Type::byte_array(),
1147                    Type::byte_array(),
1148                );
1149
1150                assign_casted_value(
1151                    name,
1152                    value,
1153                    AirTree::assert_bool(true, expect, then, otherwise),
1154                )
1155            }
1156
1157            Pattern::Var { name, .. } => {
1158                let name = self.interner.lookup_interned(name);
1159
1160                if props.full_check {
1161                    let mut index_map = IndexMap::new();
1162
1163                    let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types, true);
1164
1165                    let val = AirTree::local_var(&name, tipo.clone());
1166
1167                    if non_opaque_tipo.is_primitive() {
1168                        assign_casted_value(name.clone(), value, then)
1169                    } else {
1170                        assign_casted_value(
1171                            name,
1172                            value,
1173                            self.expect_type_assign(
1174                                &non_opaque_tipo,
1175                                val,
1176                                &mut index_map,
1177                                pattern.location(),
1178                                then,
1179                                props.otherwise.clone(),
1180                            ),
1181                        )
1182                    }
1183                } else {
1184                    assign_casted_value(name.clone(), value, then)
1185                }
1186            }
1187
1188            Pattern::Assign { name, pattern, .. } => {
1189                let name = self.interner.lookup_interned(name);
1190
1191                let inner_pattern = self.assignment(
1192                    pattern,
1193                    AirTree::local_var(&name, tipo.clone()),
1194                    then,
1195                    tipo,
1196                    AssignmentProperties {
1197                        value_type: tipo.clone(),
1198                        kind: props.kind,
1199                        remove_unused: props.remove_unused,
1200                        full_check: props.full_check,
1201                        otherwise: props.otherwise.clone(),
1202                    },
1203                );
1204
1205                assign_casted_value(name, value, inner_pattern)
1206            }
1207
1208            Pattern::Discard { name, .. } => {
1209                if props.full_check {
1210                    let name = format!("__discard_expect_{name}");
1211
1212                    let name_interned = introduce_name(&mut self.interner, &name);
1213
1214                    let mut index_map = IndexMap::new();
1215
1216                    let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types, true);
1217
1218                    let val = AirTree::local_var(&name_interned, tipo.clone());
1219
1220                    let tree = if non_opaque_tipo.is_primitive() {
1221                        assign_casted_value(name_interned, value, then)
1222                    } else {
1223                        assign_casted_value(
1224                            name_interned,
1225                            value,
1226                            self.expect_type_assign(
1227                                &non_opaque_tipo,
1228                                val,
1229                                &mut index_map,
1230                                pattern.location(),
1231                                then,
1232                                props.otherwise.clone(),
1233                            ),
1234                        )
1235                    };
1236
1237                    self.interner.pop_text(name);
1238
1239                    tree
1240                } else if !props.remove_unused {
1241                    //No need to intern, name not used
1242                    assign_casted_value(name.clone(), value, then)
1243                } else {
1244                    then
1245                }
1246            }
1247
1248            Pattern::List { elements, tail, .. } => {
1249                assert!(tipo.is_list());
1250                assert!(props.kind.is_expect());
1251
1252                let list_elem_types = tipo.get_inner_types();
1253
1254                let list_elem_type = list_elem_types
1255                    .first()
1256                    .unwrap_or_else(|| unreachable!("No list element type?"));
1257
1258                let mut elems = vec![];
1259
1260                // If Some then push tail onto elems
1261                let then = match tail {
1262                    None => then,
1263                    Some(tail) => {
1264                        let (tail_name, tail_name_interned) = match tail.as_ref() {
1265                            Pattern::Var { name, .. } => {
1266                                (None, self.interner.lookup_interned(name))
1267                            }
1268                            // This Pattern one doesn't even make sense
1269                            Pattern::Assign { .. } => {
1270                                todo!("Has this ever been reached before?")
1271                            }
1272                            Pattern::Discard { name, .. } => {
1273                                if props.full_check {
1274                                    (
1275                                        Some(format!("__discard_{name}_tail")),
1276                                        introduce_name(
1277                                            &mut self.interner,
1278                                            &format!("__discard_{name}_tail"),
1279                                        ),
1280                                    )
1281                                } else {
1282                                    (None, DISCARDED.to_string())
1283                                }
1284                            }
1285                            _ => unreachable!(),
1286                        };
1287
1288                        let val = AirTree::local_var(&tail_name_interned, tipo.clone());
1289
1290                        let then = if tail_name_interned != DISCARDED {
1291                            self.assignment(
1292                                tail,
1293                                val,
1294                                then,
1295                                tipo,
1296                                AssignmentProperties {
1297                                    value_type: tipo.clone(),
1298                                    kind: props.kind,
1299                                    // The reason the top level of recursion might have remove_unused
1300                                    // false is to deal with expect _ = thing
1301                                    //                       next_thing
1302                                    remove_unused: true,
1303                                    full_check: props.full_check,
1304                                    otherwise: props.otherwise.clone(),
1305                                },
1306                            )
1307                        } else {
1308                            then
1309                        };
1310
1311                        elems.push(tail_name_interned);
1312
1313                        if let Some(tail_name) = tail_name {
1314                            self.interner.pop_text(tail_name);
1315                        }
1316
1317                        then
1318                    }
1319                };
1320
1321                let then = elements
1322                    .iter()
1323                    .enumerate()
1324                    .rfold(then, |then, (index, elem)| {
1325                        let (elem_name, elem_name_interned) = match elem {
1326                            Pattern::Var { name, .. } => {
1327                                (None, self.interner.lookup_interned(name))
1328                            }
1329                            Pattern::Assign { name, .. } => {
1330                                (None, self.interner.lookup_interned(name))
1331                            }
1332                            Pattern::Discard { name, .. } => {
1333                                if props.full_check {
1334                                    (
1335                                        Some(format!("__discard_{name}_{index}")),
1336                                        introduce_name(
1337                                            &mut self.interner,
1338                                            &format!("__discard_{name}_{index}"),
1339                                        ),
1340                                    )
1341                                } else {
1342                                    (None, DISCARDED.to_string())
1343                                }
1344                            }
1345                            _ => {
1346                                let name = format!(
1347                                    "elem_{}_span_{}_{}",
1348                                    index,
1349                                    elem.location().start,
1350                                    elem.location().end
1351                                );
1352                                let interned = introduce_name(&mut self.interner, &name);
1353
1354                                (Some(name), interned)
1355                            }
1356                        };
1357
1358                        let val = AirTree::local_var(&elem_name_interned, list_elem_type.clone());
1359
1360                        let then = if elem_name_interned != DISCARDED {
1361                            self.assignment(
1362                                elem,
1363                                val,
1364                                then,
1365                                list_elem_type,
1366                                AssignmentProperties {
1367                                    value_type: list_elem_type.clone(),
1368                                    kind: props.kind,
1369                                    remove_unused: true,
1370                                    full_check: props.full_check,
1371                                    otherwise: props.otherwise.clone(),
1372                                },
1373                            )
1374                        } else {
1375                            then
1376                        };
1377
1378                        elems.push(elem_name_interned);
1379
1380                        if let Some(elem_name) = elem_name {
1381                            self.interner.pop_text(elem_name);
1382                        }
1383
1384                        then
1385                    });
1386
1387                elems.reverse();
1388
1389                let name = format!(
1390                    "__List_span_{}_{}",
1391                    pattern.location().start,
1392                    pattern.location().end
1393                );
1394
1395                let name_interned = introduce_name(&mut self.interner, &name);
1396
1397                let casted_var = AirTree::local_var(&name_interned, tipo.clone());
1398
1399                let tree = if elements.is_empty() {
1400                    assign_casted_value(
1401                        name_interned,
1402                        value,
1403                        AirTree::list_empty(casted_var, then, otherwise),
1404                    )
1405                } else {
1406                    assign_casted_value(
1407                        name_interned,
1408                        value,
1409                        AirTree::list_access(
1410                            elems,
1411                            tipo.clone(),
1412                            tail.is_some(),
1413                            casted_var,
1414                            if props.full_check {
1415                                ExpectLevel::Full
1416                            } else {
1417                                ExpectLevel::Items
1418                            },
1419                            then,
1420                            otherwise,
1421                        ),
1422                    )
1423                };
1424
1425                self.interner.pop_text(name);
1426
1427                tree
1428            }
1429
1430            Pattern::Pair {
1431                fst,
1432                snd,
1433                location: _,
1434            } => {
1435                let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
1436
1437                for (index, arg) in tipo.get_inner_types().iter().enumerate() {
1438                    let field_type = arg.clone();
1439                    type_map.insert(index, field_type);
1440                }
1441
1442                assert!(type_map.len() == 2);
1443
1444                let mut fields = vec![];
1445
1446                let then = [fst, snd]
1447                    .iter()
1448                    .enumerate()
1449                    .rfold(then, |then, (field_index, arg)| {
1450                        let (field_name, field_name_interned) = match arg.as_ref() {
1451                            Pattern::Var { name, .. } => {
1452                                (None, self.interner.lookup_interned(name))
1453                            }
1454                            Pattern::Assign { name, .. } => {
1455                                (None, self.interner.lookup_interned(name))
1456                            }
1457                            Pattern::Discard { name, .. } => {
1458                                if props.full_check {
1459                                    (
1460                                        Some(format!("__discard_{name}_{field_index}")),
1461                                        introduce_name(
1462                                            &mut self.interner,
1463                                            &format!("__discard_{name}_{field_index}"),
1464                                        ),
1465                                    )
1466                                } else {
1467                                    (None, DISCARDED.to_string())
1468                                }
1469                            }
1470                            _ => {
1471                                let name = format!(
1472                                    "field_{}_span_{}_{}",
1473                                    field_index,
1474                                    arg.location().start,
1475                                    arg.location().end
1476                                );
1477                                let interned = introduce_name(&mut self.interner, &name);
1478
1479                                (Some(name), interned)
1480                            }
1481                        };
1482
1483                        let arg_type = type_map.get(&field_index).unwrap_or_else(|| {
1484                            unreachable!("Missing type for field {} of Pair", field_index,)
1485                        });
1486
1487                        let val = AirTree::local_var(&field_name_interned, arg_type.clone());
1488
1489                        let then = if field_name_interned != DISCARDED {
1490                            self.assignment(
1491                                arg,
1492                                val,
1493                                then,
1494                                arg_type,
1495                                AssignmentProperties {
1496                                    value_type: arg_type.clone(),
1497                                    kind: props.kind,
1498                                    remove_unused: true,
1499                                    full_check: props.full_check,
1500                                    otherwise: props.otherwise.clone(),
1501                                },
1502                            )
1503                        } else {
1504                            then
1505                        };
1506
1507                        fields.push((field_index, field_name_interned, arg_type.clone()));
1508
1509                        if let Some(field_name) = field_name {
1510                            self.interner.pop_text(field_name);
1511                        }
1512
1513                        then
1514                    });
1515
1516                fields.reverse();
1517
1518                // This `value` is either value param that was passed in or
1519                // local var
1520                let constructor_name = format!(
1521                    "Pair_span_{}_{}",
1522                    pattern.location().start,
1523                    pattern.location().end
1524                );
1525
1526                let constructor_name_interned =
1527                    introduce_name(&mut self.interner, &constructor_name);
1528
1529                let local_value = AirTree::local_var(&constructor_name_interned, tipo.clone());
1530
1531                let then = AirTree::pair_access(
1532                    fields
1533                        .first()
1534                        .map(|x| {
1535                            if x.1 == DISCARDED {
1536                                None
1537                            } else {
1538                                Some(x.1.clone())
1539                            }
1540                        })
1541                        .unwrap(),
1542                    fields
1543                        .last()
1544                        .map(|x| {
1545                            if x.1 == DISCARDED {
1546                                None
1547                            } else {
1548                                Some(x.1.clone())
1549                            }
1550                        })
1551                        .unwrap(),
1552                    tipo.clone(),
1553                    local_value,
1554                    props.full_check,
1555                    then,
1556                    otherwise,
1557                );
1558
1559                let tree = assign_casted_value(constructor_name_interned, value, then);
1560
1561                self.interner.pop_text(constructor_name);
1562
1563                tree
1564            }
1565
1566            Pattern::Constructor {
1567                constructor: PatternConstructor::Record { name, .. },
1568                ..
1569            } if tipo.is_bool() => {
1570                assert!(props.kind.is_expect());
1571
1572                let name_var = format!(
1573                    "__Bool_{}_{}",
1574                    pattern.location().start,
1575                    pattern.location().end
1576                );
1577
1578                let local_var = AirTree::local_var(&name_var, tipo.clone());
1579
1580                assign_casted_value(
1581                    name_var,
1582                    value,
1583                    AirTree::assert_bool(name == "True", local_var, then, otherwise),
1584                )
1585            }
1586
1587            Pattern::Constructor { .. } if tipo.is_void() => {
1588                // Void type is checked when casting from data
1589                // So we just assign the value and move on
1590                assign_casted_value(DISCARDED.to_string(), value, then)
1591            }
1592
1593            Pattern::Constructor {
1594                arguments,
1595                constructor: PatternConstructor::Record { name, field_map },
1596                tipo: constr_tipo,
1597                ..
1598            } => {
1599                // Constr execution branch
1600                let field_map = field_map.clone();
1601
1602                let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
1603
1604                for (index, arg) in constr_tipo
1605                    .arg_types()
1606                    .expect("Mismatched type")
1607                    .iter()
1608                    .enumerate()
1609                {
1610                    let field_type = arg.clone();
1611
1612                    type_map.insert(index, field_type);
1613                }
1614
1615                assert!(
1616                    type_map.len() >= arguments.len(),
1617                    "type map len: {}, arguments len: {}; for constructor {:?}",
1618                    type_map.len(),
1619                    arguments.len(),
1620                    name,
1621                );
1622
1623                let mut fields = vec![];
1624
1625                let then = arguments
1626                    .iter()
1627                    .enumerate()
1628                    .rfold(then, |then, (index, arg)| {
1629                        let label = arg.label.clone().unwrap_or_default();
1630
1631                        let field_index = if let Some(field_map) = &field_map {
1632                            *field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index)
1633                        } else {
1634                            index
1635                        };
1636
1637                        let (field_name, field_name_interned) = match &arg.value {
1638                            Pattern::Var { name, .. } => {
1639                                (None, self.interner.lookup_interned(name))
1640                            }
1641                            Pattern::Assign { name, .. } => {
1642                                (None, self.interner.lookup_interned(name))
1643                            }
1644                            Pattern::Discard { name, .. } => {
1645                                if props.full_check {
1646                                    (
1647                                        Some(format!("__discard_{name}_{index}")),
1648                                        introduce_name(
1649                                            &mut self.interner,
1650                                            &format!("__discard_{name}_{index}"),
1651                                        ),
1652                                    )
1653                                } else {
1654                                    (None, DISCARDED.to_string())
1655                                }
1656                            }
1657                            _ => {
1658                                let name = format!(
1659                                    "field_{}_span_{}_{}",
1660                                    field_index,
1661                                    arg.value.location().start,
1662                                    arg.value.location().end
1663                                );
1664                                let interned = introduce_name(&mut self.interner, &name);
1665
1666                                (Some(name), interned)
1667                            }
1668                        };
1669
1670                        let arg_type = type_map.get(&field_index).unwrap_or_else(|| {
1671                            unreachable!(
1672                                "Missing type for field {} of constr {}",
1673                                field_index, name
1674                            )
1675                        });
1676
1677                        let val = AirTree::local_var(&field_name_interned, arg_type.clone());
1678
1679                        let then = if field_name_interned != DISCARDED {
1680                            self.assignment(
1681                                &arg.value,
1682                                val,
1683                                then,
1684                                arg_type,
1685                                AssignmentProperties {
1686                                    value_type: arg_type.clone(),
1687                                    kind: props.kind,
1688                                    remove_unused: true,
1689                                    full_check: props.full_check,
1690                                    otherwise: props.otherwise.clone(),
1691                                },
1692                            )
1693                        } else {
1694                            then
1695                        };
1696
1697                        fields.push((field_index, field_name_interned, arg_type.clone()));
1698
1699                        if let Some(field_name) = field_name {
1700                            self.interner.pop_text(field_name);
1701                        }
1702
1703                        then
1704                    });
1705
1706                fields.reverse();
1707
1708                // This `value` is either value param that was passed in or
1709                // local var
1710                let constructor_name = format!(
1711                    "__constructor_{}_span_{}_{}",
1712                    name,
1713                    pattern.location().start,
1714                    pattern.location().end
1715                );
1716
1717                let subject_name = format!(
1718                    "__subject_{}_span_{}_{}",
1719                    name,
1720                    pattern.location().start,
1721                    pattern.location().end
1722                );
1723
1724                let constructor_name_interned =
1725                    introduce_name(&mut self.interner, &constructor_name);
1726
1727                let subject_name_interned = introduce_name(&mut self.interner, &subject_name);
1728
1729                let local_value = AirTree::local_var(&constructor_name_interned, tipo.clone());
1730
1731                let data_type = lookup_data_type_by_tipo(&self.data_types, tipo)
1732                    .unwrap_or_else(|| unreachable!("Failed to find definition for {}", name));
1733
1734                let list_decorator = data_type
1735                    .decorators
1736                    .iter()
1737                    .any(|dec| matches!(dec.kind, DecoratorKind::List));
1738
1739                let then = if check_replaceable_opaque_type(tipo, &self.data_types) {
1740                    AirTree::let_assignment(&fields[0].1, local_value.clone(), then)
1741                } else {
1742                    AirTree::fields_expose(
1743                        fields,
1744                        local_value.clone(),
1745                        props.full_check,
1746                        then,
1747                        otherwise.clone(),
1748                        list_decorator,
1749                    )
1750                };
1751
1752                let then = if props.kind.is_expect()
1753                    && !list_decorator
1754                    && (data_type.constructors.len() > 1
1755                        || props.full_check
1756                        // Never check is not needed in theory since never has 2 constr variants
1757                        || data_type.is_never())
1758                {
1759                    let (index, _) =
1760                        get_constr_index_variant(&data_type, name).unwrap_or_else(|| {
1761                            panic!("Found constructor type {name} with 0 matching constructors")
1762                        });
1763
1764                    AirTree::when(
1765                        &subject_name_interned,
1766                        Type::void(),
1767                        tipo.clone(),
1768                        local_value,
1769                        AirTree::clause(
1770                            &subject_name_interned,
1771                            AirTree::int(index),
1772                            tipo.clone(),
1773                            then,
1774                            otherwise,
1775                        ),
1776                    )
1777                } else {
1778                    assert!(
1779                        data_type.constructors.len() == 1 || data_type.is_never(),
1780                        "attempted let-assignment on a type with more or less than 1 constructor: \nis_expect? {}\nfull_check? {}\ndata_type={data_type:#?}\n{}",
1781                        props.kind.is_expect(),
1782                        props.full_check,
1783                        name,
1784                    );
1785
1786                    then
1787                };
1788
1789                let tree = assign_casted_value(constructor_name_interned, value, then);
1790
1791                self.interner.pop_text(constructor_name);
1792                self.interner.pop_text(subject_name);
1793
1794                tree
1795            }
1796
1797            Pattern::Tuple {
1798                elems, location, ..
1799            } => {
1800                let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
1801
1802                for (index, arg) in tipo.get_inner_types().iter().enumerate() {
1803                    let field_type = arg.clone();
1804                    type_map.insert(index, field_type);
1805                }
1806
1807                assert!(type_map.len() == elems.len());
1808
1809                let mut fields = vec![];
1810
1811                let then = elems.iter().enumerate().rfold(then, |then, (index, arg)| {
1812                    let (tuple_name, tuple_name_interned) = match &arg {
1813                        Pattern::Var { name, .. } => (None, self.interner.lookup_interned(name)),
1814                        Pattern::Assign { name, .. } => (None, self.interner.lookup_interned(name)),
1815                        Pattern::Discard { name, .. } => {
1816                            if props.full_check {
1817                                (
1818                                    Some(format!("__discard_{name}_{index}")),
1819                                    introduce_name(
1820                                        &mut self.interner,
1821                                        &format!("__discard_{name}_{index}"),
1822                                    ),
1823                                )
1824                            } else {
1825                                (None, DISCARDED.to_string())
1826                            }
1827                        }
1828                        _ => {
1829                            let name = format!(
1830                                "tuple_{}_span_{}_{}",
1831                                index,
1832                                arg.location().start,
1833                                arg.location().end
1834                            );
1835
1836                            let interned = introduce_name(&mut self.interner, &name);
1837
1838                            (Some(name), interned)
1839                        }
1840                    };
1841
1842                    let arg_type = type_map.get(&index).unwrap_or_else(|| {
1843                        unreachable!(
1844                            "Missing type for tuple index {} of tuple_span_{}_{}",
1845                            index, location.start, location.end
1846                        )
1847                    });
1848
1849                    let val = AirTree::local_var(&tuple_name_interned, arg_type.clone());
1850
1851                    let then = if DISCARDED != tuple_name_interned {
1852                        self.assignment(
1853                            arg,
1854                            val,
1855                            then,
1856                            arg_type,
1857                            AssignmentProperties {
1858                                value_type: arg_type.clone(),
1859                                kind: props.kind,
1860                                remove_unused: true,
1861                                full_check: props.full_check,
1862                                otherwise: props.otherwise.clone(),
1863                            },
1864                        )
1865                    } else {
1866                        then
1867                    };
1868
1869                    fields.push(tuple_name_interned);
1870
1871                    if let Some(tuple_name) = tuple_name {
1872                        self.interner.pop_text(tuple_name);
1873                    }
1874
1875                    then
1876                });
1877
1878                fields.reverse();
1879
1880                // This `value` is either value param that was passed in or local var
1881
1882                let name = format!(
1883                    "__Tuple_span_{}_{}",
1884                    pattern.location().start,
1885                    pattern.location().end
1886                );
1887
1888                let name_interned = introduce_name(&mut self.interner, &name);
1889
1890                let local_var = AirTree::local_var(&name_interned, tipo.clone());
1891
1892                let tree = assign_casted_value(
1893                    name_interned,
1894                    value,
1895                    AirTree::tuple_access(
1896                        fields,
1897                        tipo.clone(),
1898                        local_var,
1899                        props.full_check,
1900                        then,
1901                        otherwise,
1902                    ),
1903                );
1904
1905                self.interner.pop_text(name);
1906
1907                tree
1908            }
1909        }
1910    }
1911
1912    pub fn expect_type_assign(
1913        &mut self,
1914        tipo: &Rc<Type>,
1915        value: AirTree,
1916        defined_data_types: &mut IndexMap<String, u64>,
1917        location: Span,
1918        then: AirTree,
1919        otherwise: Otherwise,
1920    ) -> AirTree {
1921        assert!(
1922            tipo.get_generic_id().is_none(),
1923            "left-hand side of expect is generic: {}",
1924            tipo.to_pretty(0)
1925        );
1926        // Shouldn't be needed but still here just in case
1927        // this function is called from anywhere else besides assignment
1928        let tipo = &convert_opaque_type(tipo, &self.data_types, true);
1929
1930        let uplc_type = tipo.get_uplc_type();
1931
1932        match uplc_type {
1933            // primitives
1934            // Untyped Data
1935            Some(
1936                UplcType::Integer
1937                | UplcType::String
1938                | UplcType::Bool
1939                | UplcType::ByteString
1940                | UplcType::Unit
1941                | UplcType::Bls12_381G1Element
1942                | UplcType::Bls12_381G2Element
1943                | UplcType::Bls12_381MlResult
1944                | UplcType::Data,
1945            ) => then,
1946
1947            // Map type
1948            Some(UplcType::List(_)) if tipo.is_map() => {
1949                assert!(!tipo.get_inner_types().is_empty());
1950
1951                let inner_list_type = &tipo.get_inner_types()[0];
1952                let inner_pair_types = inner_list_type.get_inner_types();
1953
1954                assert!(inner_pair_types.len() == 2);
1955
1956                let map_name = format!("__map_span_{}_{}", location.start, location.end);
1957                let pair_name = format!("__pair_span_{}_{}", location.start, location.end);
1958                let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end);
1959                let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end);
1960                let curried_expect_on_list = "__curried_expect_on_list".to_string();
1961                let list = "__list".to_string();
1962
1963                let map_name_interned = introduce_name(&mut self.interner, &map_name);
1964                let pair_name_interned = introduce_name(&mut self.interner, &pair_name);
1965                let fst_name_interned = introduce_name(&mut self.interner, &fst_name);
1966                let snd_name_interned = introduce_name(&mut self.interner, &snd_name);
1967                let curried_expect_on_list_interned =
1968                    introduce_name(&mut self.interner, &curried_expect_on_list);
1969                let list_interned = introduce_name(&mut self.interner, &list);
1970
1971                let expect_snd = self.expect_type_assign(
1972                    &inner_pair_types[1],
1973                    AirTree::local_var(snd_name_interned.clone(), inner_pair_types[1].clone()),
1974                    defined_data_types,
1975                    location,
1976                    AirTree::call(
1977                        AirTree::local_var(&curried_expect_on_list_interned, Type::void()),
1978                        Type::void(),
1979                        vec![AirTree::builtin(
1980                            DefaultFunction::TailList,
1981                            Type::list(Type::data()),
1982                            vec![AirTree::local_var(&list_interned, tipo.clone())],
1983                        )],
1984                    ),
1985                    otherwise.clone(),
1986                );
1987
1988                let expect_fst = self.expect_type_assign(
1989                    &inner_pair_types[0],
1990                    AirTree::local_var(fst_name_interned.clone(), inner_pair_types[0].clone()),
1991                    defined_data_types,
1992                    location,
1993                    expect_snd,
1994                    otherwise.clone(),
1995                );
1996
1997                let unwrap_function = AirTree::anon_func(
1998                    vec![list_interned.clone(), curried_expect_on_list_interned],
1999                    AirTree::list_empty(
2000                        AirTree::local_var(&list_interned, tipo.clone()),
2001                        then,
2002                        AirTree::anon_func(
2003                            vec![],
2004                            AirTree::let_assignment(
2005                                &pair_name_interned,
2006                                AirTree::builtin(
2007                                    DefaultFunction::HeadList,
2008                                    Type::pair(Type::data(), Type::data()),
2009                                    vec![AirTree::local_var(list_interned, tipo.clone())],
2010                                ),
2011                                AirTree::pair_access(
2012                                    Some(fst_name_interned),
2013                                    Some(snd_name_interned),
2014                                    inner_list_type.clone(),
2015                                    AirTree::local_var(
2016                                        &pair_name_interned,
2017                                        inner_list_type.clone(),
2018                                    ),
2019                                    true,
2020                                    expect_fst,
2021                                    otherwise.unwrap_or_else(DELAY_ERROR),
2022                                ),
2023                            ),
2024                            true,
2025                        ),
2026                    ),
2027                    false,
2028                );
2029
2030                let function = self.code_gen_functions.get(EXPECT_ON_LIST);
2031
2032                // This function can be defined here on in the branch below
2033                if function.is_none() {
2034                    let expect_list_func = AirTree::expect_on_list2();
2035                    self.code_gen_functions.insert(
2036                        EXPECT_ON_LIST.to_string(),
2037                        CodeGenFunction::Function {
2038                            body: expect_list_func,
2039                            params: vec!["__list_to_check".to_string(), "__check_with".to_string()],
2040                        },
2041                    );
2042                }
2043
2044                if let Some(counter) = defined_data_types.get_mut(EXPECT_ON_LIST) {
2045                    *counter += 1
2046                } else {
2047                    defined_data_types.insert(EXPECT_ON_LIST.to_string(), 1);
2048                }
2049
2050                let func_call = AirTree::call(
2051                    AirTree::var(
2052                        ValueConstructor::public(
2053                            Type::void(),
2054                            ValueConstructorVariant::ModuleFn {
2055                                name: EXPECT_ON_LIST.to_string(),
2056                                field_map: None,
2057                                module: "".to_string(),
2058                                arity: 1,
2059                                location,
2060                                builtin: None,
2061                            },
2062                        ),
2063                        EXPECT_ON_LIST,
2064                        "",
2065                    ),
2066                    Type::void(),
2067                    vec![
2068                        AirTree::local_var(&map_name_interned, tipo.clone()),
2069                        unwrap_function,
2070                    ],
2071                );
2072
2073                let tree = AirTree::let_assignment(map_name_interned, value, func_call);
2074
2075                self.interner.pop_text(map_name);
2076                self.interner.pop_text(pair_name);
2077                self.interner.pop_text(fst_name);
2078                self.interner.pop_text(snd_name);
2079                self.interner.pop_text(curried_expect_on_list);
2080                self.interner.pop_text(list);
2081
2082                tree
2083            }
2084            // Tuple type
2085            Some(UplcType::List(_)) if tipo.is_tuple() => {
2086                let tuple_inner_types = tipo.get_inner_types();
2087
2088                assert!(!tuple_inner_types.is_empty());
2089
2090                let tuple_name = format!("__tuple_span_{}_{}", location.start, location.end);
2091
2092                let tuple_name_interned = introduce_name(&mut self.interner, &tuple_name);
2093
2094                let mut tuple_expect_items = vec![];
2095
2096                let then =
2097                    tuple_inner_types
2098                        .iter()
2099                        .enumerate()
2100                        .rfold(then, |then, (index, arg)| {
2101                            let tuple_index_name = format!(
2102                                "__tuple_index_{}_span_{}_{}",
2103                                index, location.start, location.end
2104                            );
2105
2106                            let tuple_index_name_interned =
2107                                introduce_name(&mut self.interner, &tuple_index_name);
2108
2109                            let expect_tuple_item = self.expect_type_assign(
2110                                arg,
2111                                AirTree::local_var(&tuple_index_name_interned, arg.clone()),
2112                                defined_data_types,
2113                                location,
2114                                then,
2115                                otherwise.clone(),
2116                            );
2117
2118                            tuple_expect_items.push(tuple_index_name_interned);
2119
2120                            self.interner.pop_text(tuple_index_name);
2121
2122                            expect_tuple_item
2123                        });
2124
2125                tuple_expect_items.reverse();
2126
2127                let tuple_access = AirTree::tuple_access(
2128                    tuple_expect_items,
2129                    tipo.clone(),
2130                    AirTree::local_var(&tuple_name_interned, tipo.clone()),
2131                    true,
2132                    then,
2133                    otherwise.unwrap_or_else(DELAY_ERROR),
2134                );
2135
2136                let tree = AirTree::let_assignment(tuple_name_interned, value, tuple_access);
2137
2138                self.interner.pop_text(tuple_name);
2139
2140                tree
2141            }
2142            // Regular List type
2143            Some(UplcType::List(_)) => {
2144                assert!(!tipo.get_inner_types().is_empty());
2145
2146                let inner_list_type = &tipo.get_inner_types()[0];
2147
2148                if inner_list_type.is_data() {
2149                    then
2150                } else {
2151                    let list_name = format!("__list_span_{}_{}", location.start, location.end);
2152                    let item_name = format!("__item_span_{}_{}", location.start, location.end);
2153                    let list = "__list".to_string();
2154                    let curried_func = "__curried_expect_on_list".to_string();
2155
2156                    let list_name_interned = introduce_name(&mut self.interner, &list_name);
2157                    let item_name_interned = introduce_name(&mut self.interner, &item_name);
2158                    let list_interned = introduce_name(&mut self.interner, &list);
2159                    let curried_func_interned = introduce_name(&mut self.interner, &curried_func);
2160
2161                    let unwrap_function = AirTree::anon_func(
2162                        vec![list_interned.clone(), curried_func_interned.clone()],
2163                        AirTree::list_empty(
2164                            AirTree::local_var(&list_interned, tipo.clone()),
2165                            then,
2166                            AirTree::anon_func(
2167                                vec![],
2168                                AirTree::let_assignment(
2169                                    &item_name_interned,
2170                                    AirTree::builtin(
2171                                        DefaultFunction::HeadList,
2172                                        Type::data(),
2173                                        vec![AirTree::local_var(&list_interned, tipo.clone())],
2174                                    ),
2175                                    AirTree::soft_cast_assignment(
2176                                        &item_name_interned,
2177                                        inner_list_type.clone(),
2178                                        AirTree::local_var(&item_name_interned, Type::data()),
2179                                        self.expect_type_assign(
2180                                            inner_list_type,
2181                                            AirTree::local_var(
2182                                                &item_name_interned,
2183                                                inner_list_type.clone(),
2184                                            ),
2185                                            defined_data_types,
2186                                            location,
2187                                            AirTree::call(
2188                                                AirTree::local_var(
2189                                                    curried_func_interned,
2190                                                    Type::void(),
2191                                                ),
2192                                                Type::void(),
2193                                                vec![AirTree::builtin(
2194                                                    DefaultFunction::TailList,
2195                                                    Type::list(Type::data()),
2196                                                    vec![AirTree::local_var(
2197                                                        list_interned,
2198                                                        tipo.clone(),
2199                                                    )],
2200                                                )],
2201                                            ),
2202                                            otherwise.clone(),
2203                                        ),
2204                                        otherwise.unwrap_or_else(DELAY_ERROR),
2205                                    ),
2206                                ),
2207                                true,
2208                            ),
2209                        ),
2210                        false,
2211                    );
2212
2213                    let function = self.code_gen_functions.get(EXPECT_ON_LIST);
2214
2215                    if function.is_none() {
2216                        let expect_list_func = AirTree::expect_on_list2();
2217                        self.code_gen_functions.insert(
2218                            EXPECT_ON_LIST.to_string(),
2219                            CodeGenFunction::Function {
2220                                body: expect_list_func,
2221                                params: vec![
2222                                    "__list_to_check".to_string(),
2223                                    "__check_with".to_string(),
2224                                ],
2225                            },
2226                        );
2227                    }
2228
2229                    if let Some(counter) = defined_data_types.get_mut(EXPECT_ON_LIST) {
2230                        *counter += 1
2231                    } else {
2232                        defined_data_types.insert(EXPECT_ON_LIST.to_string(), 1);
2233                    }
2234
2235                    let func_call = AirTree::call(
2236                        AirTree::var(
2237                            ValueConstructor::public(
2238                                Type::void(),
2239                                ValueConstructorVariant::ModuleFn {
2240                                    name: EXPECT_ON_LIST.to_string(),
2241                                    field_map: None,
2242                                    module: "".to_string(),
2243                                    arity: 1,
2244                                    location,
2245                                    builtin: None,
2246                                },
2247                            ),
2248                            EXPECT_ON_LIST,
2249                            "",
2250                        ),
2251                        Type::void(),
2252                        vec![
2253                            AirTree::local_var(&list_name_interned, tipo.clone()),
2254                            unwrap_function,
2255                        ],
2256                    );
2257
2258                    let tree = AirTree::let_assignment(list_name_interned, value, func_call);
2259
2260                    self.interner.pop_text(list_name);
2261                    self.interner.pop_text(item_name);
2262                    self.interner.pop_text(list);
2263                    self.interner.pop_text(curried_func);
2264
2265                    tree
2266                }
2267            }
2268            // Pair type
2269            Some(UplcType::Pair(_, _)) => {
2270                let tuple_inner_types = tipo.get_inner_types();
2271
2272                assert!(tuple_inner_types.len() == 2);
2273
2274                let pair_name = format!("__pair_span_{}_{}", location.start, location.end);
2275                let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end);
2276                let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end);
2277
2278                let pair_name_interned = introduce_name(&mut self.interner, &pair_name);
2279                let fst_name_interned = introduce_name(&mut self.interner, &fst_name);
2280                let snd_name_interned = introduce_name(&mut self.interner, &snd_name);
2281
2282                let expect_snd = self.expect_type_assign(
2283                    &tuple_inner_types[1],
2284                    AirTree::local_var(snd_name_interned.clone(), tuple_inner_types[1].clone()),
2285                    defined_data_types,
2286                    location,
2287                    then,
2288                    otherwise.clone(),
2289                );
2290
2291                let expect_fst = self.expect_type_assign(
2292                    &tuple_inner_types[0],
2293                    AirTree::local_var(fst_name_interned.clone(), tuple_inner_types[0].clone()),
2294                    defined_data_types,
2295                    location,
2296                    expect_snd,
2297                    otherwise.clone(),
2298                );
2299
2300                let pair_access = AirTree::pair_access(
2301                    Some(fst_name_interned),
2302                    Some(snd_name_interned),
2303                    tipo.clone(),
2304                    AirTree::local_var(&pair_name_interned, tipo.clone()),
2305                    true,
2306                    expect_fst,
2307                    otherwise.unwrap_or_else(DELAY_ERROR),
2308                );
2309
2310                let tree = AirTree::let_assignment(pair_name_interned, value, pair_access);
2311
2312                self.interner.pop_text(pair_name);
2313                self.interner.pop_text(fst_name);
2314                self.interner.pop_text(snd_name);
2315
2316                tree
2317            }
2318
2319            // Constr type
2320            None => {
2321                let data_type =
2322                    lookup_data_type_by_tipo(&self.data_types, tipo).unwrap_or_else(|| {
2323                        unreachable!("We need a data type definition for type {:#?}", tipo)
2324                    });
2325
2326                assert!(data_type.typed_parameters.len() == tipo.arg_types().unwrap().len());
2327
2328                let mono_types: IndexMap<u64, Rc<Type>> = if !data_type.typed_parameters.is_empty()
2329                {
2330                    data_type
2331                        .typed_parameters
2332                        .iter()
2333                        .zip(tipo.arg_types().unwrap())
2334                        .flat_map(|item| get_generic_id_and_type(item.0, &item.1))
2335                        .collect()
2336                } else {
2337                    IndexMap::new()
2338                };
2339
2340                let data_type_name = expect_decoder_function_name(tipo, otherwise.is_some());
2341                let function = self.code_gen_functions.get(&data_type_name);
2342
2343                // mutate code_gen_funcs and defined_data_types in this if branch
2344                if function.is_none() && defined_data_types.get(&data_type_name).is_none() {
2345                    defined_data_types.insert(data_type_name.clone(), 1);
2346
2347                    let var_then = AirTree::call(
2348                        AirTree::local_var("then_delayed", Type::void()),
2349                        Type::void(),
2350                        vec![],
2351                    );
2352
2353                    let otherwise_delayed = otherwise
2354                        .as_ref()
2355                        .map(|_| AirTree::local_var("otherwise_delayed", Type::void()));
2356
2357                    let is_never = data_type.is_never();
2358
2359                    let list_decorator = data_type
2360                        .decorators
2361                        .iter()
2362                        .any(|dec| matches!(dec.kind, DecoratorKind::List));
2363
2364                    let constr_clauses = data_type.constructors.iter().enumerate().rfold(
2365                        otherwise_delayed.clone().unwrap_or_else(DELAY_ERROR),
2366                        |acc, (index, constr)| {
2367                            // NOTE: For the Never type, we have an placeholder first constructor
2368                            // that must be ignored. The Never type is considered to have only one
2369                            // constructor starting at index 1 so it shouldn't be possible to
2370                            // cast from Data into the first constructor. There's virtually no
2371                            // constructor at index 0.
2372                            if is_never && index == 0 {
2373                                return acc;
2374                            }
2375
2376                            let index = if let Some(tag) =
2377                                constr.decorators.iter().find_map(|d| match &d.kind {
2378                                    DecoratorKind::Tag { value, .. } => Some(value),
2379                                    _ => None,
2380                                }) {
2381                                *tag
2382                            } else {
2383                                index
2384                            };
2385
2386                            let mut constr_args = vec![];
2387
2388                            let constr_then = constr.arguments.iter().enumerate().rfold(
2389                                var_then.clone(),
2390                                |then, (index, arg)| {
2391                                    let arg_name =
2392                                        arg.label.clone().unwrap_or(format!("__field_{index}"));
2393
2394                                    let arg_tipo =
2395                                        find_and_replace_generics(&arg.tipo, &mono_types);
2396
2397                                    constr_args.push((index, arg_name.clone(), arg_tipo.clone()));
2398
2399                                    self.expect_type_assign(
2400                                        &arg_tipo.clone(),
2401                                        AirTree::local_var(arg_name, arg_tipo),
2402                                        defined_data_types,
2403                                        location,
2404                                        then,
2405                                        otherwise_delayed.clone(),
2406                                    )
2407                                },
2408                            );
2409                            constr_args.reverse();
2410
2411                            let then = if constr_args.is_empty() {
2412                                AirTree::fields_empty(
2413                                    AirTree::local_var(
2414                                        format!(
2415                                            "__constr_var_span_{}_{}",
2416                                            location.start, location.end
2417                                        ),
2418                                        tipo.clone(),
2419                                    ),
2420                                    constr_then,
2421                                    otherwise_delayed.clone().unwrap_or_else(DELAY_ERROR),
2422                                    list_decorator,
2423                                )
2424                            } else {
2425                                AirTree::fields_expose(
2426                                    constr_args,
2427                                    AirTree::local_var(
2428                                        format!(
2429                                            "__constr_var_span_{}_{}",
2430                                            location.start, location.end
2431                                        ),
2432                                        tipo.clone(),
2433                                    ),
2434                                    true,
2435                                    constr_then,
2436                                    otherwise_delayed.clone().unwrap_or_else(DELAY_ERROR),
2437                                    list_decorator,
2438                                )
2439                            };
2440
2441                            if list_decorator {
2442                                AirTree::anon_func(vec![], then, true)
2443                            } else {
2444                                AirTree::anon_func(
2445                                    vec![],
2446                                    AirTree::clause(
2447                                        format!(
2448                                            "__subject_span_{}_{}",
2449                                            location.start, location.end
2450                                        ),
2451                                        AirTree::int(index),
2452                                        tipo.clone(),
2453                                        then,
2454                                        acc,
2455                                    ),
2456                                    true,
2457                                )
2458                            }
2459                        },
2460                    );
2461
2462                    let when_expr = AirTree::when(
2463                        format!("__subject_span_{}_{}", location.start, location.end),
2464                        Type::void(),
2465                        tipo.clone(),
2466                        AirTree::local_var(
2467                            format!("__constr_var_span_{}_{}", location.start, location.end),
2468                            tipo.clone(),
2469                        ),
2470                        AirTree::call(constr_clauses, Type::void(), vec![]),
2471                    );
2472
2473                    let func_body = AirTree::let_assignment(
2474                        format!("__constr_var_span_{}_{}", location.start, location.end),
2475                        AirTree::local_var("__param_0", tipo.clone()),
2476                        when_expr,
2477                    );
2478
2479                    let code_gen_func = CodeGenFunction::Function {
2480                        body: func_body,
2481                        params: if otherwise.is_some() {
2482                            vec![
2483                                "__param_0".to_string(),
2484                                "then_delayed".to_string(),
2485                                "otherwise_delayed".to_string(),
2486                            ]
2487                        } else {
2488                            vec!["__param_0".to_string(), "then_delayed".to_string()]
2489                        },
2490                    };
2491
2492                    self.code_gen_functions
2493                        .insert(data_type_name.clone(), code_gen_func);
2494                } else if defined_data_types.get(&data_type_name).is_none() {
2495                    defined_data_types.insert(data_type_name.to_string(), 1);
2496                }
2497
2498                let args = if let Some(otherwise) = otherwise {
2499                    vec![value, AirTree::anon_func(vec![], then, true), otherwise]
2500                } else {
2501                    vec![value, AirTree::anon_func(vec![], then, true)]
2502                };
2503
2504                let module_fn = ValueConstructorVariant::ModuleFn {
2505                    name: data_type_name.to_string(),
2506                    field_map: None,
2507                    module: "".to_string(),
2508                    arity: args.len(),
2509                    location,
2510                    builtin: None,
2511                };
2512
2513                let func_var = AirTree::var(
2514                    ValueConstructor::public(tipo.clone(), module_fn),
2515                    data_type_name,
2516                    "",
2517                );
2518
2519                AirTree::call(func_var, Type::void(), args)
2520            }
2521        }
2522    }
2523
2524    fn handle_decision_tree(
2525        &mut self,
2526        subject_name: &String,
2527        subject_tipo: Rc<Type>,
2528        return_tipo: Rc<Type>,
2529        module_build_name: &str,
2530        tree: decision_tree::DecisionTree<'_>,
2531        mut stick_set: TreeSet,
2532    ) -> AirTree {
2533        match tree {
2534            DecisionTree::Switch {
2535                path,
2536                mut cases,
2537                default,
2538            } => {
2539                //Current path to test
2540                let current_tipo = get_tipo_by_path(subject_tipo.clone(), &path);
2541                let builtins_path = Builtins::new_from_path(subject_tipo.clone(), path);
2542                let current_subject_name = if builtins_path.is_empty() {
2543                    subject_name.clone()
2544                } else {
2545                    format!("{subject_name}_{builtins_path}")
2546                };
2547
2548                // Transition process from previous to current
2549                let builtins_to_add = stick_set.diff_union_builtins(builtins_path.clone());
2550
2551                // Previous path to apply the transition process too
2552                let prev_builtins = Builtins {
2553                    vec: builtins_path.vec[0..(builtins_path.len() - builtins_to_add.len())]
2554                        .to_vec(),
2555                };
2556
2557                let prev_subject_name = if prev_builtins.is_empty() {
2558                    subject_name.clone()
2559                } else {
2560                    format!("{subject_name}_{prev_builtins}")
2561                };
2562                let prev_tipo = prev_builtins
2563                    .vec
2564                    .last()
2565                    .map_or(subject_tipo.clone(), |last| last.tipo());
2566
2567                let data_type = lookup_data_type_by_tipo(&self.data_types, &current_tipo);
2568
2569                let last_clause = if data_type
2570                    .as_ref()
2571                    .is_none_or(|d| d.constructors.len() != cases.len())
2572                {
2573                    *default.unwrap()
2574                } else {
2575                    cases.pop().unwrap().1
2576                };
2577
2578                let last_clause = self.handle_decision_tree(
2579                    subject_name,
2580                    subject_tipo.clone(),
2581                    return_tipo.clone(),
2582                    module_build_name,
2583                    last_clause,
2584                    stick_set.clone(),
2585                );
2586
2587                let test_subject_name = if data_type.is_some() {
2588                    format!("{}_index", current_subject_name.clone(),)
2589                } else {
2590                    current_subject_name.clone()
2591                };
2592
2593                let clauses = cases.into_iter().rfold(last_clause, |acc, (case, then)| {
2594                    let case_air = self.handle_decision_tree(
2595                        subject_name,
2596                        subject_tipo.clone(),
2597                        return_tipo.clone(),
2598                        module_build_name,
2599                        then,
2600                        stick_set.clone(),
2601                    );
2602
2603                    AirTree::clause(
2604                        test_subject_name.clone(),
2605                        case.get_air_pattern(current_tipo.clone()),
2606                        current_tipo.clone(),
2607                        case_air,
2608                        AirTree::anon_func(vec![], acc, true),
2609                    )
2610                });
2611
2612                let when_air_clauses = AirTree::when(
2613                    test_subject_name,
2614                    return_tipo.clone(),
2615                    current_tipo.clone(),
2616                    AirTree::local_var(current_subject_name, current_tipo.clone()),
2617                    clauses,
2618                );
2619
2620                builtins_to_add.produce_air(prev_subject_name, prev_tipo, when_air_clauses)
2621            }
2622            DecisionTree::ListSwitch {
2623                path,
2624                cases,
2625                tail_cases,
2626                default,
2627            } => {
2628                //Current path to test
2629                let current_tipo = get_tipo_by_path(subject_tipo.clone(), &path);
2630                let builtins_path = Builtins::new_from_path(subject_tipo.clone(), path);
2631                let current_subject_name = if builtins_path.is_empty() {
2632                    subject_name.clone()
2633                } else {
2634                    format!("{subject_name}_{builtins_path}")
2635                };
2636
2637                // Transition process from previous to current
2638                let builtins_to_add = stick_set.diff_union_builtins(builtins_path.clone());
2639
2640                // Previous path to apply the transition process too
2641                let prev_builtins = Builtins {
2642                    vec: builtins_path.vec[0..(builtins_path.len() - builtins_to_add.len())]
2643                        .to_vec(),
2644                };
2645
2646                let prev_subject_name = if prev_builtins.is_empty() {
2647                    subject_name.clone()
2648                } else {
2649                    format!("{subject_name}_{prev_builtins}")
2650                };
2651                let prev_tipo = prev_builtins
2652                    .vec
2653                    .last()
2654                    .map_or(subject_tipo.clone(), |last| last.tipo());
2655
2656                let longest_pattern = cases.iter().chain(tail_cases.iter()).fold(
2657                    0,
2658                    |longest, (case, _)| match case {
2659                        CaseTest::List(i) => {
2660                            if longest < *i {
2661                                *i
2662                            } else {
2663                                longest
2664                            }
2665                        }
2666                        CaseTest::ListWithTail(i) => {
2667                            if longest < *i {
2668                                *i - 1
2669                            } else {
2670                                longest
2671                            }
2672                        }
2673                        _ => unreachable!(),
2674                    },
2675                );
2676
2677                let last_pattern = if tail_cases.is_empty() {
2678                    *default.as_ref().unwrap().clone()
2679                } else {
2680                    let tree = tail_cases.last().unwrap();
2681
2682                    tree.1.clone()
2683                };
2684
2685                let builtins_for_pattern = builtins_path.merge(Builtins::new_from_list_case(
2686                    CaseTest::List(longest_pattern),
2687                ));
2688
2689                stick_set.diff_union_builtins(builtins_for_pattern.clone());
2690
2691                let last_pattern = self.handle_decision_tree(
2692                    subject_name,
2693                    subject_tipo.clone(),
2694                    return_tipo.clone(),
2695                    module_build_name,
2696                    last_pattern,
2697                    stick_set.clone(),
2698                );
2699
2700                let list_clauses = (0..=longest_pattern).rev().with_position().fold(
2701                    (builtins_for_pattern, last_pattern),
2702                    |(mut builtins_for_pattern, acc), list_item| match list_item {
2703                        itertools::Position::First(index) | itertools::Position::Only(index) => {
2704                            let (_, tree) = cases
2705                                .iter()
2706                                .chain(tail_cases.iter())
2707                                .find(|x| match x.0 {
2708                                    CaseTest::List(i) => i == index,
2709                                    CaseTest::ListWithTail(i) => i <= index,
2710                                    _ => unreachable!(),
2711                                })
2712                                .cloned()
2713                                .unwrap_or_else(|| {
2714                                    (CaseTest::Wild, *default.as_ref().unwrap().clone())
2715                                });
2716
2717                            let tail_name = if builtins_for_pattern.is_empty() {
2718                                subject_name.clone()
2719                            } else {
2720                                format!("{subject_name}_{builtins_for_pattern}")
2721                            };
2722
2723                            let then = self.handle_decision_tree(
2724                                subject_name,
2725                                subject_tipo.clone(),
2726                                return_tipo.clone(),
2727                                module_build_name,
2728                                tree,
2729                                stick_set.clone(),
2730                            );
2731
2732                            let acc = AirTree::list_clause(
2733                                tail_name.clone(),
2734                                subject_tipo.clone(),
2735                                then,
2736                                AirTree::anon_func(vec![], acc, true),
2737                                None,
2738                            );
2739
2740                            builtins_for_pattern.pop();
2741
2742                            (builtins_for_pattern, acc)
2743                        }
2744
2745                        itertools::Position::Middle(index) | itertools::Position::Last(index) => {
2746                            let (_, tree) = cases
2747                                .iter()
2748                                .chain(tail_cases.iter())
2749                                .find(|x| match x.0 {
2750                                    CaseTest::List(i) => i == index,
2751                                    CaseTest::ListWithTail(i) => i <= index,
2752                                    _ => unreachable!(),
2753                                })
2754                                .cloned()
2755                                .unwrap_or_else(|| {
2756                                    (CaseTest::Wild, *default.as_ref().unwrap().clone())
2757                                });
2758
2759                            let tail_name = if builtins_for_pattern.is_empty() {
2760                                subject_name.clone()
2761                            } else {
2762                                format!("{subject_name}_{builtins_for_pattern}")
2763                            };
2764
2765                            // TODO: change this in the future to use the Builtins to_string method
2766                            // to ensure future changes don't break things
2767                            let next_tail_name = Some(format!("{tail_name}_tail"));
2768
2769                            let then = self.handle_decision_tree(
2770                                subject_name,
2771                                subject_tipo.clone(),
2772                                return_tipo.clone(),
2773                                module_build_name,
2774                                tree,
2775                                stick_set.clone(),
2776                            );
2777
2778                            let acc = AirTree::list_clause(
2779                                tail_name.clone(),
2780                                subject_tipo.clone(),
2781                                then,
2782                                AirTree::anon_func(vec![], acc, true),
2783                                next_tail_name.map(|next| (tail_name, next)),
2784                            );
2785
2786                            // since we iterate over the list cases in reverse
2787                            // We pop off a builtin to make it easier to get the name of
2788                            // prev_tested list case since each name is based off the builtins
2789                            builtins_for_pattern.pop();
2790
2791                            (builtins_for_pattern, acc)
2792                        }
2793                    },
2794                );
2795
2796                let when_list_cases = AirTree::when(
2797                    current_subject_name.clone(),
2798                    return_tipo.clone(),
2799                    current_tipo.clone(),
2800                    AirTree::local_var(current_subject_name, current_tipo.clone()),
2801                    list_clauses.1,
2802                );
2803
2804                builtins_to_add.produce_air(prev_subject_name, prev_tipo, when_list_cases)
2805            }
2806            DecisionTree::HoistedLeaf(name, args) => {
2807                let air_args = args
2808                    .iter()
2809                    .map(|item| {
2810                        let current_tipo = get_tipo_by_path(subject_tipo.clone(), &item.path);
2811
2812                        (
2813                            current_tipo.clone(),
2814                            AirTree::local_var(item.assigned.clone(), current_tipo),
2815                        )
2816                    })
2817                    .collect_vec();
2818
2819                let then = AirTree::call(
2820                    AirTree::local_var(
2821                        name,
2822                        Type::function(
2823                            air_args.iter().map(|i| i.0.clone()).collect_vec(),
2824                            return_tipo.clone(),
2825                        ),
2826                    ),
2827                    Type::void(),
2828                    air_args.into_iter().map(|i| i.1).collect_vec(),
2829                );
2830
2831                handle_assigns(subject_name, subject_tipo, &args, &mut stick_set, then)
2832            }
2833            DecisionTree::HoistThen {
2834                name,
2835                assigns,
2836                pattern,
2837                then,
2838            } => {
2839                let assign = AirTree::let_assignment(
2840                    name,
2841                    AirTree::anon_func(
2842                        assigns
2843                            .iter()
2844                            .map(|i| introduce_name(&mut self.interner, &i.assigned))
2845                            .collect_vec(),
2846                        // The one reason we have to pass in mutable self
2847                        // So we can build the TypedExpr into Air
2848                        self.build(then, module_build_name, &[]),
2849                        true,
2850                    ),
2851                    self.handle_decision_tree(
2852                        subject_name,
2853                        subject_tipo,
2854                        return_tipo,
2855                        module_build_name,
2856                        *pattern,
2857                        stick_set,
2858                    ),
2859                );
2860
2861                assigns.into_iter().for_each(|x| {
2862                    self.interner.pop_text(x.assigned);
2863                });
2864
2865                assign
2866            }
2867        }
2868    }
2869
2870    fn hoist_functions_to_validator(&mut self, mut air_tree: AirTree) -> AirTree {
2871        let mut functions_to_hoist = IndexMap::new();
2872        let mut used_functions = vec![];
2873        let mut defined_functions = vec![];
2874        let mut hoisted_functions = vec![];
2875        let mut validator_hoistable;
2876
2877        // TODO change subsequent tree traversals to be more like a stream.
2878        air_tree.traverse_tree_with(&mut |air_tree: &mut AirTree, _| {
2879            erase_opaque_type_operations(air_tree, &self.data_types);
2880        });
2881
2882        self.find_function_vars_and_depth(
2883            &mut air_tree,
2884            &mut functions_to_hoist,
2885            &mut used_functions,
2886            &mut TreePath::new(),
2887            0,
2888            Fields::FirstField,
2889        );
2890
2891        validator_hoistable = used_functions.clone();
2892
2893        while let Some((key, variant_name)) = used_functions.pop() {
2894            defined_functions.push((key.clone(), variant_name.clone()));
2895
2896            let function_variants = functions_to_hoist
2897                .get(&key)
2898                .unwrap_or_else(|| panic!("Missing Function Definition"));
2899
2900            let (tree_path, function) = function_variants
2901                .get(&variant_name)
2902                .unwrap_or_else(|| panic!("Missing Function Variant Definition"));
2903
2904            match function {
2905                HoistableFunction::Function { body, deps, params } => {
2906                    let mut hoist_body = body.clone();
2907                    let mut hoist_deps = deps.clone();
2908                    let params = params.clone();
2909                    let tree_path = tree_path.clone();
2910
2911                    self.define_dependent_functions(
2912                        &mut hoist_body,
2913                        &mut functions_to_hoist,
2914                        &mut used_functions,
2915                        &defined_functions,
2916                        &mut hoist_deps,
2917                        tree_path,
2918                    );
2919
2920                    let function_variants = functions_to_hoist
2921                        .get_mut(&key)
2922                        .unwrap_or_else(|| panic!("Missing Function Definition"));
2923
2924                    let (_, function) = function_variants
2925                        .get_mut(&variant_name)
2926                        .expect("Missing Function Variant Definition");
2927
2928                    *function = HoistableFunction::Function {
2929                        body: hoist_body,
2930                        deps: hoist_deps,
2931                        params,
2932                    };
2933                }
2934                HoistableFunction::Link(_) => todo!("Deal with Link later"),
2935                _ => unreachable!(),
2936            }
2937        }
2938        validator_hoistable.dedup();
2939
2940        // First we need to sort functions by dependencies
2941        // here's also where we deal with mutual recursion
2942
2943        // Mutual Recursion
2944        let inputs = functions_to_hoist
2945            .iter()
2946            .flat_map(|(function_name, val)| {
2947                val.into_iter()
2948                    .map(|(variant, (_, function))| {
2949                        if let HoistableFunction::Function { deps, .. } = function {
2950                            ((function_name.clone(), variant.clone()), deps)
2951                        } else {
2952                            todo!("Deal with Link later")
2953                        }
2954                    })
2955                    .collect_vec()
2956            })
2957            .collect_vec();
2958
2959        let capacity = inputs.len();
2960
2961        let mut graph = Graph::<(), ()>::with_capacity(capacity, capacity * 5);
2962
2963        let mut indices = HashMap::with_capacity(capacity);
2964        let mut values = HashMap::with_capacity(capacity);
2965
2966        for (value, _) in &inputs {
2967            let index = graph.add_node(());
2968
2969            indices.insert(value.clone(), index);
2970
2971            values.insert(index, value.clone());
2972        }
2973
2974        for (value, deps) in inputs {
2975            if let Some(from_index) = indices.get(&value) {
2976                let deps = deps.iter().filter_map(|dep| indices.get(dep));
2977
2978                for to_index in deps {
2979                    graph.add_edge(*from_index, *to_index, ());
2980                }
2981            }
2982        }
2983
2984        let strong_connections = algo::tarjan_scc(&graph);
2985
2986        for (index, connections) in strong_connections.into_iter().enumerate() {
2987            // If there's only one function, then it's only self recursive
2988            if connections.len() < 2 {
2989                continue;
2990            }
2991
2992            let cyclic_function_names = connections
2993                .iter()
2994                .map(|index| values.get(index).unwrap())
2995                .collect_vec();
2996
2997            // TODO: Maybe I could come up with a name based off the functions involved?
2998            let function_key = FunctionAccessKey {
2999                function_name: format!("__cyclic_function_{index}"),
3000                module_name: "".to_string(),
3001            };
3002
3003            let mut path = TreePath::new();
3004            let mut cycle_of_functions = vec![];
3005            let mut cycle_deps = vec![];
3006
3007            let function_list = cyclic_function_names
3008                .iter()
3009                .map(|(key, variant)| {
3010                    format!(
3011                        "{}{}{}",
3012                        key.module_name,
3013                        key.function_name,
3014                        if variant.is_empty() {
3015                            "".to_string()
3016                        } else {
3017                            format!("_{variant}")
3018                        }
3019                    )
3020                })
3021                .collect_vec();
3022
3023            // By doing this any vars that "call" into a function in the cycle will be
3024            // redirected to call the cyclic function instead with the proper index
3025            for (index, (func_name, variant)) in cyclic_function_names.iter().enumerate() {
3026                self.cyclic_functions.insert(
3027                    (func_name.clone(), variant.clone()),
3028                    (function_list.clone(), index, function_key.clone()),
3029                );
3030
3031                let (tree_path, func) = functions_to_hoist
3032                    .get_mut(func_name)
3033                    .expect("Missing Function Definition")
3034                    .get_mut(variant)
3035                    .expect("Missing Function Variant Definition");
3036
3037                match func {
3038                    HoistableFunction::Function { params, body, deps } => {
3039                        cycle_of_functions.push((params.clone(), body.clone()));
3040                        cycle_deps.push(deps.clone());
3041                    }
3042
3043                    _ => unreachable!(),
3044                }
3045
3046                if !path.was_set() {
3047                    path = tree_path.clone();
3048                } else {
3049                    path = path.common_ancestor(tree_path);
3050                }
3051
3052                // Here we change function to be link so all functions that depend on it know its a
3053                // cyclic function
3054                *func = HoistableFunction::CyclicLink(function_key.clone());
3055            }
3056
3057            let cyclic_function = HoistableFunction::CyclicFunction {
3058                functions: cycle_of_functions,
3059                deps: cycle_deps
3060                    .into_iter()
3061                    .flatten()
3062                    .dedup()
3063                    // Make sure to filter out cyclic dependencies
3064                    .filter(|dependency| {
3065                        !cyclic_function_names.iter().any(|(func_name, variant)| {
3066                            func_name == &dependency.0 && variant == &dependency.1
3067                        })
3068                    })
3069                    .collect_vec(),
3070            };
3071
3072            let mut cyclic_map = IndexMap::new();
3073            cyclic_map.insert("".to_string(), (path, cyclic_function));
3074
3075            functions_to_hoist.insert(function_key, cyclic_map);
3076        }
3077
3078        // Rest of code is for hoisting functions
3079        // TODO: replace with graph implementation of sorting
3080        let mut sorted_function_vec: Vec<(FunctionAccessKey, String)> = vec![];
3081
3082        let functions_to_hoist_cloned = functions_to_hoist.clone();
3083
3084        let mut sorting_attempts: u64 = 0;
3085        while let Some((generic_func, variant)) = validator_hoistable.pop() {
3086            assert!(
3087                sorting_attempts < 5_000_000_000,
3088                "Sorting dependency attempts exceeded"
3089            );
3090
3091            let function_variants = functions_to_hoist_cloned
3092                .get(&generic_func)
3093                .unwrap_or_else(|| panic!("Missing Function Definition"));
3094
3095            let (_, function) = function_variants
3096                .get(&variant)
3097                .unwrap_or_else(|| panic!("Missing Function Variant Definition"));
3098
3099            match function {
3100                HoistableFunction::Function { deps, .. } => {
3101                    for (dep_generic_func, dep_variant) in deps.iter() {
3102                        if !(dep_generic_func == &generic_func && dep_variant == &variant) {
3103                            validator_hoistable
3104                                .insert(0, (dep_generic_func.clone(), dep_variant.clone()));
3105
3106                            sorted_function_vec.retain(|(generic_func, variant)| {
3107                                !(generic_func == dep_generic_func && variant == dep_variant)
3108                            });
3109                        }
3110                    }
3111
3112                    // Fix dependencies path to be updated to common ancestor
3113                    for (dep_key, dep_variant) in deps {
3114                        let (func_tree_path, _) = functions_to_hoist
3115                            .get(&generic_func)
3116                            .unwrap()
3117                            .get(&variant)
3118                            .unwrap()
3119                            .clone();
3120
3121                        let (dep_path, _) = functions_to_hoist
3122                            .get_mut(dep_key)
3123                            .unwrap()
3124                            .get_mut(dep_variant)
3125                            .unwrap();
3126
3127                        *dep_path = func_tree_path.common_ancestor(dep_path);
3128                    }
3129                    sorted_function_vec.push((generic_func, variant));
3130                }
3131                HoistableFunction::Link(_) => todo!("Deal with Link later"),
3132                HoistableFunction::CyclicLink(cyclic_name) => {
3133                    validator_hoistable.insert(0, (cyclic_name.clone(), "".to_string()));
3134
3135                    sorted_function_vec.retain(|(generic_func, variant)| {
3136                        !(generic_func == cyclic_name && variant.is_empty())
3137                    });
3138
3139                    let (func_tree_path, _) = functions_to_hoist
3140                        .get(&generic_func)
3141                        .unwrap()
3142                        .get(&variant)
3143                        .unwrap()
3144                        .clone();
3145
3146                    let (dep_path, _) = functions_to_hoist
3147                        .get_mut(cyclic_name)
3148                        .unwrap()
3149                        .get_mut("")
3150                        .unwrap();
3151
3152                    *dep_path = func_tree_path.common_ancestor(dep_path);
3153                }
3154                HoistableFunction::CyclicFunction { deps, .. } => {
3155                    for (dep_generic_func, dep_variant) in deps.iter() {
3156                        if !(dep_generic_func == &generic_func && dep_variant == &variant) {
3157                            validator_hoistable
3158                                .insert(0, (dep_generic_func.clone(), dep_variant.clone()));
3159
3160                            sorted_function_vec.retain(|(generic_func, variant)| {
3161                                !(generic_func == dep_generic_func && variant == dep_variant)
3162                            });
3163                        }
3164                    }
3165
3166                    // Fix dependencies path to be updated to common ancestor
3167                    for (dep_key, dep_variant) in deps {
3168                        let (func_tree_path, _) = functions_to_hoist
3169                            .get(&generic_func)
3170                            .unwrap()
3171                            .get(&variant)
3172                            .unwrap()
3173                            .clone();
3174
3175                        let (dep_path, _) = functions_to_hoist
3176                            .get_mut(dep_key)
3177                            .unwrap()
3178                            .get_mut(dep_variant)
3179                            .unwrap();
3180
3181                        *dep_path = func_tree_path.common_ancestor(dep_path);
3182                    }
3183                    sorted_function_vec.push((generic_func, variant));
3184                }
3185            }
3186
3187            sorting_attempts += 1;
3188        }
3189        sorted_function_vec.dedup();
3190
3191        // Now we need to hoist the functions to the top of the validator
3192        for (key, variant) in sorted_function_vec {
3193            if hoisted_functions
3194                .iter()
3195                .any(|(func_key, func_variant)| func_key == &key && func_variant == &variant)
3196            {
3197                continue;
3198            }
3199
3200            let function_variants = functions_to_hoist
3201                .get(&key)
3202                .unwrap_or_else(|| panic!("Missing Function Definition"));
3203
3204            let (tree_path, function) = function_variants
3205                .get(&variant)
3206                .unwrap_or_else(|| panic!("Missing Function Variant Definition"));
3207
3208            self.hoist_function(
3209                &mut air_tree,
3210                tree_path,
3211                function,
3212                (&key, &variant),
3213                &functions_to_hoist,
3214                &mut hoisted_functions,
3215            );
3216        }
3217
3218        air_tree
3219    }
3220
3221    fn hoist_function(
3222        &mut self,
3223        air_tree: &mut AirTree,
3224        tree_path: &TreePath,
3225        function: &HoistableFunction,
3226        key_var: (&FunctionAccessKey, &String),
3227        functions_to_hoist: &IndexMap<
3228            FunctionAccessKey,
3229            IndexMap<String, (TreePath, HoistableFunction)>,
3230        >,
3231        hoisted_functions: &mut Vec<(FunctionAccessKey, String)>,
3232    ) {
3233        match function {
3234            HoistableFunction::Function {
3235                body,
3236                deps: func_deps,
3237                params,
3238            } => {
3239                let mut body = body.clone();
3240
3241                let (key, variant) = key_var;
3242
3243                // check for recursiveness
3244                let is_recursive = func_deps
3245                    .iter()
3246                    .any(|(dep_key, dep_variant)| dep_key == key && dep_variant == variant);
3247
3248                // first grab dependencies
3249                let func_params = params;
3250
3251                let deps = (tree_path, func_deps.clone());
3252
3253                let recursive_nonstatics = if is_recursive {
3254                    modify_self_calls(&mut body, key, variant, func_params)
3255                } else {
3256                    func_params.clone()
3257                };
3258
3259                let node_to_edit = air_tree.find_air_tree_node(tree_path);
3260
3261                let defined_function = AirTree::define_func(
3262                    &key.function_name,
3263                    &key.module_name,
3264                    variant,
3265                    func_params.clone(),
3266                    is_recursive,
3267                    recursive_nonstatics,
3268                    body,
3269                    node_to_edit.clone(),
3270                );
3271
3272                let defined_dependencies = self.hoist_dependent_functions(
3273                    deps,
3274                    (key, variant),
3275                    hoisted_functions,
3276                    functions_to_hoist,
3277                    defined_function,
3278                );
3279
3280                // now hoist full function onto validator tree
3281                *node_to_edit = defined_dependencies;
3282
3283                hoisted_functions.push((key.clone(), variant.clone()));
3284            }
3285            HoistableFunction::CyclicFunction {
3286                functions,
3287                deps: func_deps,
3288            } => {
3289                let (key, variant) = key_var;
3290
3291                let deps = (tree_path, func_deps.clone());
3292
3293                let mut functions = functions.clone();
3294
3295                for (_, body) in functions.iter_mut() {
3296                    modify_cyclic_calls(body, key, &self.cyclic_functions);
3297                }
3298
3299                let node_to_edit = air_tree.find_air_tree_node(tree_path);
3300
3301                let cyclic_func = AirTree::define_cyclic_func(
3302                    &key.function_name,
3303                    &key.module_name,
3304                    variant,
3305                    functions,
3306                    node_to_edit.clone(),
3307                );
3308
3309                let defined_dependencies = self.hoist_dependent_functions(
3310                    deps,
3311                    (key, variant),
3312                    hoisted_functions,
3313                    functions_to_hoist,
3314                    cyclic_func,
3315                );
3316
3317                // now hoist full function onto validator tree
3318                *node_to_edit = defined_dependencies;
3319
3320                hoisted_functions.push((key.clone(), variant.clone()));
3321            }
3322            HoistableFunction::Link(_) => {
3323                todo!("This should probably be unreachable when I get to it")
3324            }
3325            HoistableFunction::CyclicLink(_) => {
3326                unreachable!("Sorted functions should not contain cyclic links")
3327            }
3328        }
3329    }
3330
3331    fn hoist_dependent_functions(
3332        &mut self,
3333        deps: (&TreePath, Vec<(FunctionAccessKey, String)>),
3334        func_key_variant: (&FunctionAccessKey, &Variant),
3335        hoisted_functions: &mut Vec<(FunctionAccessKey, String)>,
3336        functions_to_hoist: &IndexMap<
3337            FunctionAccessKey,
3338            IndexMap<String, (TreePath, HoistableFunction)>,
3339        >,
3340        air_tree: AirTree,
3341    ) -> AirTree {
3342        let (key, variant) = func_key_variant;
3343        let (func_path, func_deps) = deps;
3344
3345        let mut deps_vec = func_deps;
3346        let mut sorted_dep_vec = vec![];
3347
3348        while let Some(dep) = deps_vec.pop() {
3349            let function_variants = functions_to_hoist
3350                .get(&dep.0)
3351                .unwrap_or_else(|| panic!("Missing Function Definition"));
3352
3353            let (_, function) = function_variants
3354                .get(&dep.1)
3355                .unwrap_or_else(|| panic!("Missing Function Variant Definition"));
3356
3357            match function {
3358                HoistableFunction::Function { deps, .. } => {
3359                    for (dep_generic_func, dep_variant) in deps.iter() {
3360                        if !(dep_generic_func == &dep.0 && dep_variant == &dep.1) {
3361                            sorted_dep_vec.retain(|(generic_func, variant)| {
3362                                !(generic_func == dep_generic_func && variant == dep_variant)
3363                            });
3364
3365                            deps_vec.insert(0, (dep_generic_func.clone(), dep_variant.clone()));
3366                        }
3367                    }
3368
3369                    sorted_dep_vec.push((dep.0.clone(), dep.1.clone()));
3370                }
3371                HoistableFunction::CyclicFunction { deps, .. } => {
3372                    for (dep_generic_func, dep_variant) in deps.iter() {
3373                        if !(dep_generic_func == &dep.0 && dep_variant == &dep.1) {
3374                            sorted_dep_vec.retain(|(generic_func, variant)| {
3375                                !(generic_func == dep_generic_func && variant == dep_variant)
3376                            });
3377
3378                            deps_vec.insert(0, (dep_generic_func.clone(), dep_variant.clone()));
3379                        }
3380                    }
3381                    sorted_dep_vec.push((dep.0.clone(), dep.1.clone()));
3382                }
3383                HoistableFunction::Link(_) => todo!("Deal with Link later"),
3384                HoistableFunction::CyclicLink(cyclic_func) => {
3385                    sorted_dep_vec.retain(|(generic_func, variant)| {
3386                        !(generic_func == cyclic_func && variant.is_empty())
3387                    });
3388
3389                    deps_vec.insert(0, (cyclic_func.clone(), "".to_string()));
3390                }
3391            }
3392        }
3393
3394        sorted_dep_vec.dedup();
3395
3396        sorted_dep_vec
3397            .into_iter()
3398            .fold(air_tree, |then, (dep_key, dep_variant)| {
3399                if
3400                // if the dependency is the same as the function we're hoisting
3401                // or we hoisted it, then skip it
3402                hoisted_functions
3403                    .iter()
3404                    .any(|(generic, variant)| generic == &dep_key && variant == &dep_variant)
3405                    || (&dep_key == key && &dep_variant == variant)
3406                {
3407                    return then;
3408                }
3409
3410                let dependency = functions_to_hoist
3411                    .get(&dep_key)
3412                    .unwrap_or_else(|| panic!("Missing Function Definition"));
3413
3414                let (dep_path, dep_function) = dependency
3415                    .get(&dep_variant)
3416                    .unwrap_or_else(|| panic!("Missing Function Variant Definition"));
3417
3418                // In the case of zero args, we need to hoist the dependency function to the top of the zero arg function
3419                // The dependency we are hoisting should have an equal path to the function we hoisted
3420                // if we are going to hoist it
3421                if &dep_path.common_ancestor(func_path) == func_path {
3422                    match dep_function.clone() {
3423                        HoistableFunction::Function {
3424                            body: mut dep_air_tree,
3425                            deps: dependency_deps,
3426                            params: dependent_params,
3427                        } => {
3428                            let is_dependent_recursive = dependency_deps
3429                                .iter()
3430                                .any(|(key, variant)| &dep_key == key && &dep_variant == variant);
3431
3432                            let recursive_nonstatics = if is_dependent_recursive {
3433                                modify_self_calls(
3434                                    &mut dep_air_tree,
3435                                    &dep_key,
3436                                    &dep_variant,
3437                                    &dependent_params,
3438                                )
3439                            } else {
3440                                dependent_params.clone()
3441                            };
3442
3443                            hoisted_functions.push((dep_key.clone(), dep_variant.clone()));
3444
3445                            AirTree::define_func(
3446                                &dep_key.function_name,
3447                                &dep_key.module_name,
3448                                &dep_variant,
3449                                dependent_params,
3450                                is_dependent_recursive,
3451                                recursive_nonstatics,
3452                                dep_air_tree,
3453                                then,
3454                            )
3455                        }
3456                        HoistableFunction::CyclicFunction { functions, .. } => {
3457                            let mut functions = functions.clone();
3458
3459                            for (_, body) in functions.iter_mut() {
3460                                modify_cyclic_calls(body, &dep_key, &self.cyclic_functions);
3461                            }
3462
3463                            hoisted_functions.push((dep_key.clone(), dep_variant.clone()));
3464
3465                            AirTree::define_cyclic_func(
3466                                &dep_key.function_name,
3467                                &dep_key.module_name,
3468                                &dep_variant,
3469                                functions,
3470                                then,
3471                            )
3472                        }
3473                        HoistableFunction::Link(_) => unreachable!(),
3474                        HoistableFunction::CyclicLink(_) => unreachable!(),
3475                    }
3476                } else {
3477                    then
3478                }
3479            })
3480    }
3481
3482    fn define_dependent_functions(
3483        &mut self,
3484        air_tree: &mut AirTree,
3485        function_usage: &mut IndexMap<
3486            FunctionAccessKey,
3487            IndexMap<String, (TreePath, HoistableFunction)>,
3488        >,
3489        used_functions: &mut Vec<(FunctionAccessKey, String)>,
3490        defined_functions: &[(FunctionAccessKey, String)],
3491        current_function_deps: &mut Vec<(FunctionAccessKey, String)>,
3492        mut function_tree_path: TreePath,
3493    ) {
3494        let Some((depth, index)) = function_tree_path.pop() else {
3495            return;
3496        };
3497
3498        function_tree_path.push(depth, index);
3499
3500        self.find_function_vars_and_depth(
3501            air_tree,
3502            function_usage,
3503            current_function_deps,
3504            &mut function_tree_path,
3505            depth + 1,
3506            Fields::FirstField,
3507        );
3508
3509        for (generic_function_key, variant_name) in current_function_deps.iter() {
3510            if !used_functions
3511                .iter()
3512                .any(|(key, name)| key == generic_function_key && name == variant_name)
3513                && !defined_functions
3514                    .iter()
3515                    .any(|(key, name)| key == generic_function_key && name == variant_name)
3516            {
3517                used_functions.push((generic_function_key.clone(), variant_name.clone()));
3518            }
3519        }
3520    }
3521
3522    fn find_function_vars_and_depth(
3523        &mut self,
3524        air_tree: &mut AirTree,
3525        function_usage: &mut IndexMap<
3526            FunctionAccessKey,
3527            IndexMap<String, (TreePath, HoistableFunction)>,
3528        >,
3529        dependency_functions: &mut Vec<(FunctionAccessKey, String)>,
3530        path: &mut TreePath,
3531        current_depth: usize,
3532        depth_index: Fields,
3533    ) {
3534        air_tree.traverse_tree_with_path(
3535            path,
3536            current_depth,
3537            depth_index,
3538            &mut |air_tree, tree_path| {
3539                if let AirTree::Var {
3540                    constructor,
3541                    variant_name,
3542                    ..
3543                } = air_tree
3544                {
3545                    let ValueConstructorVariant::ModuleFn {
3546                        name: func_name,
3547                        module,
3548                        builtin: None,
3549                        ..
3550                    } = &constructor.variant
3551                    else {
3552                        return;
3553                    };
3554
3555                    let function_var_tipo = &constructor.tipo;
3556
3557                    let generic_function_key = FunctionAccessKey {
3558                        module_name: module.clone(),
3559                        function_name: func_name.clone(),
3560                    };
3561
3562                    let function_def = self.functions.get(&generic_function_key);
3563
3564                    let Some(function_def) = function_def else {
3565                        let code_gen_func = self
3566                            .code_gen_functions
3567                            .get(&generic_function_key.function_name)
3568                            .unwrap_or_else(|| {
3569                                panic!(
3570                                    "Missing function definition for {}. Known functions: {:?}",
3571                                    generic_function_key.function_name,
3572                                    self.functions.keys(),
3573                                )
3574                            });
3575
3576                        if !dependency_functions
3577                            .iter()
3578                            .any(|(key, name)| key == &generic_function_key && name.is_empty())
3579                        {
3580                            dependency_functions
3581                                .push((generic_function_key.clone(), "".to_string()));
3582                        }
3583
3584                        // Code gen functions are already monomorphized
3585                        if let Some(func_variants) = function_usage.get_mut(&generic_function_key) {
3586                            let (path, _) = func_variants.get_mut("").unwrap();
3587                            *path = path.common_ancestor(tree_path);
3588                        } else {
3589                            // Shortcut path for compiler generated functions
3590                            let CodeGenFunction::Function { body, params } = code_gen_func else {
3591                                unreachable!()
3592                            };
3593
3594                            let mut function_variant_path = IndexMap::new();
3595
3596                            let mut body = AirTree::no_op(body.clone());
3597
3598                            body.traverse_tree_with(&mut |air_tree, _| {
3599                                erase_opaque_type_operations(air_tree, &self.data_types);
3600                            });
3601
3602                            function_variant_path.insert(
3603                                "".to_string(),
3604                                (
3605                                    tree_path.clone(),
3606                                    HoistableFunction::Function {
3607                                        body,
3608                                        deps: vec![],
3609                                        params: params.clone(),
3610                                    },
3611                                ),
3612                            );
3613
3614                            function_usage.insert(generic_function_key, function_variant_path);
3615                        }
3616                        return;
3617                    };
3618
3619                    let mut function_var_types = function_var_tipo
3620                        .arg_types()
3621                        .unwrap_or_else(|| panic!("Expected a function tipo with arg types"));
3622
3623                    function_var_types.push(
3624                        function_var_tipo
3625                            .return_type()
3626                            .unwrap_or_else(|| panic!("Should have return type")),
3627                    );
3628
3629                    let mut function_def_types = function_def
3630                        .arguments
3631                        .iter()
3632                        .map(|arg| convert_opaque_type(&arg.tipo, &self.data_types, true))
3633                        .collect_vec();
3634
3635                    function_def_types.push(convert_opaque_type(
3636                        &function_def.return_type,
3637                        &self.data_types,
3638                        true,
3639                    ));
3640
3641                    let mono_types: IndexMap<u64, Rc<Type>> = if !function_def_types.is_empty() {
3642                        function_def_types
3643                            .iter()
3644                            .zip(function_var_types.iter())
3645                            .flat_map(|(func_tipo, var_tipo)| {
3646                                get_generic_id_and_type(func_tipo, var_tipo)
3647                            })
3648                            .collect()
3649                    } else {
3650                        IndexMap::new()
3651                    };
3652
3653                    // Don't sort here. Mono types map is already in argument order.
3654                    let variant = mono_types
3655                        .iter()
3656                        .map(|(_, tipo)| get_generic_variant_name(tipo))
3657                        .join("");
3658
3659                    variant_name.clone_from(&variant);
3660
3661                    if !dependency_functions
3662                        .iter()
3663                        .any(|(key, name)| key == &generic_function_key && name == &variant)
3664                    {
3665                        dependency_functions.push((generic_function_key.clone(), variant.clone()));
3666                    }
3667
3668                    if let Some(func_variants) = function_usage.get_mut(&generic_function_key) {
3669                        if let Some((path, _)) = func_variants.get_mut(&variant) {
3670                            *path = path.common_ancestor(tree_path);
3671                        } else {
3672                            let args = function_def.arguments.clone();
3673
3674                            let params = args
3675                                .iter()
3676                                .map(|arg| {
3677                                    arg.arg_name
3678                                        .get_variable_name()
3679                                        .map(|arg| {
3680                                            introduce_name(&mut self.interner, &arg.to_string())
3681                                        })
3682                                        .unwrap_or_else(|| DISCARDED.to_string())
3683                                })
3684                                .collect_vec();
3685
3686                            let mut function_air_tree_body = AirTree::no_op(self.build(
3687                                &function_def.body,
3688                                &generic_function_key.module_name,
3689                                &[],
3690                            ));
3691
3692                            function_air_tree_body.traverse_tree_with(&mut |air_tree, _| {
3693                                erase_opaque_type_operations(air_tree, &self.data_types);
3694                                monomorphize(air_tree, &mono_types);
3695                            });
3696
3697                            args.iter().for_each(|arg| {
3698                                arg.arg_name.get_variable_name().iter().for_each(|arg| {
3699                                    self.interner.pop_text(arg.to_string());
3700                                })
3701                            });
3702
3703                            func_variants.insert(
3704                                variant,
3705                                (
3706                                    tree_path.clone(),
3707                                    HoistableFunction::Function {
3708                                        body: function_air_tree_body,
3709                                        deps: vec![],
3710                                        params,
3711                                    },
3712                                ),
3713                            );
3714                        }
3715                    } else {
3716                        let args = function_def.arguments.clone();
3717
3718                        let params = args
3719                            .iter()
3720                            .map(|arg| {
3721                                arg.arg_name
3722                                    .get_variable_name()
3723                                    .map(|arg| introduce_name(&mut self.interner, &arg.to_string()))
3724                                    .unwrap_or_else(|| DISCARDED.to_string())
3725                            })
3726                            .collect_vec();
3727
3728                        let mut function_air_tree_body = AirTree::no_op(self.build(
3729                            &function_def.body,
3730                            &generic_function_key.module_name,
3731                            &[],
3732                        ));
3733
3734                        function_air_tree_body.traverse_tree_with(&mut |air_tree, _| {
3735                            erase_opaque_type_operations(air_tree, &self.data_types);
3736                            monomorphize(air_tree, &mono_types);
3737                        });
3738
3739                        let mut function_variant_path = IndexMap::new();
3740
3741                        args.iter().for_each(|arg| {
3742                            arg.arg_name
3743                                .get_variable_name()
3744                                .iter()
3745                                .for_each(|arg| self.interner.pop_text(arg.to_string()))
3746                        });
3747
3748                        function_variant_path.insert(
3749                            variant,
3750                            (
3751                                tree_path.clone(),
3752                                HoistableFunction::Function {
3753                                    body: function_air_tree_body,
3754                                    deps: vec![],
3755                                    params,
3756                                },
3757                            ),
3758                        );
3759
3760                        function_usage.insert(generic_function_key, function_variant_path);
3761                    }
3762                }
3763            },
3764        );
3765    }
3766
3767    fn uplc_code_gen(&mut self, mut ir_stack: Vec<Air>) -> Term<Name> {
3768        let mut arg_stack: Vec<Term<Name>> = vec![];
3769
3770        while let Some(air_element) = ir_stack.pop() {
3771            let arg = self.gen_uplc(air_element, &mut arg_stack);
3772            if let Some(arg) = arg {
3773                arg_stack.push(arg);
3774            }
3775        }
3776        assert!(arg_stack.len() == 1, "Expected one term on the stack");
3777        arg_stack.pop().unwrap()
3778    }
3779
3780    fn gen_uplc(&mut self, ir: Air, arg_stack: &mut Vec<Term<Name>>) -> Option<Term<Name>> {
3781        match ir {
3782            Air::Int { value } => Some(Term::integer(value.parse().unwrap())),
3783            Air::String { value } => Some(Term::string(value)),
3784            Air::ByteArray { bytes } => Some(Term::byte_string(bytes)),
3785            Air::Bool { value } => Some(Term::bool(value)),
3786            Air::CurvePoint { point, .. } => match point {
3787                Curve::Bls12_381(Bls12_381Point::G1(g1)) => Some(Term::bls12_381_g1(g1)),
3788                Curve::Bls12_381(Bls12_381Point::G2(g2)) => Some(Term::bls12_381_g2(g2)),
3789            },
3790            Air::Var {
3791                name,
3792                constructor,
3793                variant_name,
3794            } => match &constructor.variant {
3795                ValueConstructorVariant::LocalVariable { .. } => Some(Term::Var(
3796                    Name {
3797                        text: name,
3798                        unique: 0.into(),
3799                    }
3800                    .into(),
3801                )),
3802                ValueConstructorVariant::ModuleConstant { module, name, .. } => {
3803                    let access_key = FunctionAccessKey {
3804                        module_name: module.clone(),
3805                        function_name: name.clone(),
3806                    };
3807
3808                    let definition = self
3809                        .constants
3810                        .get(&access_key)
3811                        .unwrap_or_else(|| panic!("unknown constant {module}.{name}"));
3812
3813                    let mut value =
3814                        AirTree::no_op(self.build(definition, &access_key.module_name, &[]));
3815
3816                    value.traverse_tree_with(&mut |air_tree, _| {
3817                        erase_opaque_type_operations(air_tree, &self.data_types);
3818                    });
3819
3820                    value = self.hoist_functions_to_validator(value);
3821
3822                    let term = self.uplc_code_gen(value.to_vec());
3823
3824                    let mut program =
3825                        self.new_program(self.special_functions.apply_used_functions(term));
3826
3827                    let mut interner = CodeGenInterner::new();
3828
3829                    interner.program(&mut program);
3830
3831                    let eval_program: Program<NamedDeBruijn> =
3832                        program.clean_up_no_inlines().try_into().unwrap();
3833
3834                    Some(
3835                        eval_program
3836                            .eval(ExBudget::max())
3837                            .result()
3838                            .unwrap_or_else(|e| panic!("Failed to evaluate constant: {e:#?}"))
3839                            .try_into()
3840                            .unwrap(),
3841                    )
3842                }
3843                ValueConstructorVariant::ModuleFn {
3844                    name: func_name,
3845                    module,
3846                    builtin,
3847                    ..
3848                } => {
3849                    if let Some(func) = builtin {
3850                        return self.gen_uplc(
3851                            Air::Builtin {
3852                                count: 0,
3853                                func: *func,
3854                                tipo: constructor.tipo,
3855                            },
3856                            arg_stack,
3857                        );
3858                    }
3859
3860                    if let Some((names, index, cyclic_name)) = self.cyclic_functions.get(&(
3861                        FunctionAccessKey {
3862                            module_name: module.clone(),
3863                            function_name: func_name.clone(),
3864                        },
3865                        variant_name.clone(),
3866                    )) {
3867                        let cyclic_var_name = if cyclic_name.module_name.is_empty() {
3868                            cyclic_name.function_name.to_string()
3869                        } else {
3870                            format!("{}_{}", cyclic_name.module_name, cyclic_name.function_name)
3871                        };
3872
3873                        let index_name = names[*index].clone();
3874
3875                        let mut arg_var = Term::var(index_name.clone());
3876
3877                        for name in names.iter().rev() {
3878                            arg_var = arg_var.lambda(name);
3879                        }
3880
3881                        let term = Term::var(cyclic_var_name).apply(arg_var);
3882
3883                        Some(term)
3884                    } else {
3885                        let name = if !module.is_empty() {
3886                            format!("{module}_{func_name}{variant_name}")
3887                        } else {
3888                            format!("{func_name}{variant_name}")
3889                        };
3890
3891                        Some(Term::Var(
3892                            Name {
3893                                text: name,
3894                                unique: 0.into(),
3895                            }
3896                            .into(),
3897                        ))
3898                    }
3899                }
3900                ValueConstructorVariant::Record {
3901                    name: constr_name, ..
3902                } => {
3903                    if constructor.tipo.is_bool() {
3904                        Some(Term::bool(constr_name == "True"))
3905                    } else if constructor.tipo.is_void() {
3906                        Some(Term::Constant(UplcConstant::Unit.into()))
3907                    } else if constructor.is_pair() {
3908                        let args = constructor.tipo.arg_types().unwrap();
3909                        let mut args = args.iter();
3910                        let arg_left = args.next().unwrap();
3911                        let arg_right = args.next().unwrap();
3912                        Some(
3913                            Term::mk_pair_data()
3914                                .apply(convert_type_to_data(
3915                                    Term::var("left".to_string()),
3916                                    arg_left,
3917                                    &self.data_types,
3918                                ))
3919                                .apply(convert_type_to_data(
3920                                    Term::var("right".to_string()),
3921                                    arg_right,
3922                                    &self.data_types,
3923                                ))
3924                                .lambda("right".to_string())
3925                                .lambda("left".to_string()),
3926                        )
3927                    } else {
3928                        let data_type = crate::tipo::lookup_data_type_by_tipo(
3929                            &self.data_types,
3930                            &constructor.tipo,
3931                        )
3932                        .unwrap_or_else(|| {
3933                            panic!(
3934                                "could not find data-type definition for {} within known set: {:?}",
3935                                constructor.tipo.to_pretty(0),
3936                                self.data_types.keys()
3937                            )
3938                        });
3939
3940                        let (constr_index, constr_type) =
3941                            get_constr_index_variant(&data_type, constr_name).unwrap();
3942
3943                        let list_decorator = data_type
3944                            .decorators
3945                            .iter()
3946                            .any(|dec| matches!(dec.kind, DecoratorKind::List));
3947
3948                        let mut term = Term::empty_list();
3949
3950                        if constr_type.arguments.is_empty() {
3951                            if !list_decorator {
3952                                term = Term::constr_data()
3953                                    .apply(Term::integer(constr_index.into()))
3954                                    .apply(term);
3955                            }
3956
3957                            let mut program = self.new_program(term);
3958
3959                            let mut interner = CodeGenInterner::new();
3960
3961                            interner.program(&mut program);
3962
3963                            let eval_program: Program<NamedDeBruijn> =
3964                                program.clean_up_no_inlines().try_into().unwrap();
3965
3966                            let evaluated_term: Term<NamedDeBruijn> = eval_program
3967                                .eval(ExBudget::default())
3968                                .result()
3969                                .expect("Evaluated a constant record and got an error");
3970
3971                            term = evaluated_term.try_into().unwrap();
3972                        } else {
3973                            for (index, arg) in constructor
3974                                .tipo
3975                                .arg_types()
3976                                .unwrap()
3977                                .iter()
3978                                .enumerate()
3979                                .rev()
3980                            {
3981                                term = Term::mk_cons()
3982                                    .apply(convert_type_to_data(
3983                                        Term::var(format!("arg_{index}")),
3984                                        arg,
3985                                        &self.data_types,
3986                                    ))
3987                                    .apply(term);
3988                            }
3989                            if !list_decorator {
3990                                term = Term::constr_data()
3991                                    .apply(Term::integer(constr_index.into()))
3992                                    .apply(term);
3993                            }
3994
3995                            for (index, _) in constr_type.arguments.iter().enumerate().rev() {
3996                                term = term.lambda(format!("arg_{index}"))
3997                            }
3998                        }
3999                        Some(term)
4000                    }
4001                }
4002            },
4003            Air::Void => Some(Term::Constant(UplcConstant::Unit.into())),
4004            Air::List { count, tipo, tail } => {
4005                let mut args = vec![];
4006
4007                for _ in 0..count {
4008                    let arg = arg_stack.pop().unwrap();
4009                    args.push(arg);
4010                }
4011                let mut constants = vec![];
4012                for arg in &args {
4013                    let maybe_const = extract_constant(arg);
4014                    if let Some(c) = maybe_const {
4015                        constants.push(c);
4016                    }
4017                }
4018
4019                if constants.len() == args.len() && !tail {
4020                    let list = if tipo.is_map() {
4021                        let mut convert_keys = vec![];
4022                        let mut convert_values = vec![];
4023                        for constant in constants {
4024                            match constant.as_ref() {
4025                                UplcConstant::ProtoPair(_, _, fst, snd) => {
4026                                    convert_keys.push(fst.clone());
4027                                    convert_values.push(snd.clone());
4028                                }
4029                                _ => unreachable!(),
4030                            }
4031                        }
4032
4033                        let convert_keys = builder::convert_constants_to_data(convert_keys);
4034                        let convert_values = builder::convert_constants_to_data(convert_values);
4035
4036                        Term::Constant(
4037                            UplcConstant::ProtoList(
4038                                UplcType::Pair(UplcType::Data.into(), UplcType::Data.into()),
4039                                convert_keys
4040                                    .into_iter()
4041                                    .zip(convert_values)
4042                                    .map(|(key, value)| {
4043                                        UplcConstant::ProtoPair(
4044                                            UplcType::Data,
4045                                            UplcType::Data,
4046                                            key.into(),
4047                                            value.into(),
4048                                        )
4049                                    })
4050                                    .collect_vec(),
4051                            )
4052                            .into(),
4053                        )
4054                    } else {
4055                        Term::Constant(
4056                            UplcConstant::ProtoList(
4057                                UplcType::Data,
4058                                builder::convert_constants_to_data(constants),
4059                            )
4060                            .into(),
4061                        )
4062                    };
4063
4064                    Some(list)
4065                } else {
4066                    let mut term = if tail {
4067                        args.pop().unwrap()
4068                    } else if tipo.is_map() {
4069                        Term::empty_map()
4070                    } else {
4071                        Term::empty_list()
4072                    };
4073
4074                    // move this down here since the constant list path doesn't need to do this
4075                    let list_element_type = tipo.get_inner_types()[0].clone();
4076
4077                    for arg in args.into_iter().rev() {
4078                        let list_item = if tipo.is_map() {
4079                            arg
4080                        } else {
4081                            builder::convert_type_to_data(arg, &list_element_type, &self.data_types)
4082                        };
4083                        term = Term::mk_cons().apply(list_item).apply(term);
4084                    }
4085                    Some(term)
4086                }
4087            }
4088            Air::ListAccessor {
4089                names,
4090                tail,
4091                // TODO: rename tipo -
4092                // tipo here refers to the list type while the actual return
4093                // type is nothing since this is an assignment over some expression
4094                tipo,
4095                expect_level,
4096            } => {
4097                let value = arg_stack.pop().unwrap();
4098
4099                let mut term = arg_stack.pop().unwrap();
4100
4101                let otherwise = if matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) {
4102                    arg_stack.pop().unwrap()
4103                } else {
4104                    Term::Error.delay()
4105                };
4106
4107                let list_id = self.id_gen.next();
4108
4109                let mut id_list = vec![];
4110                id_list.push(list_id);
4111
4112                names.iter().for_each(|_| {
4113                    id_list.push(self.id_gen.next());
4114                });
4115
4116                let names_types = tipo
4117                    .get_inner_types()
4118                    .into_iter()
4119                    .cycle()
4120                    .take(names.len())
4121                    .zip(names)
4122                    .zip(id_list)
4123                    .map(|((tipo, name), id)| (name, tipo, id))
4124                    .collect_vec();
4125
4126                term = builder::list_access_to_uplc(
4127                    &names_types,
4128                    tail,
4129                    term,
4130                    true,
4131                    expect_level,
4132                    otherwise,
4133                    &self.data_types,
4134                )
4135                .apply(value);
4136
4137                Some(term)
4138            }
4139            Air::Fn {
4140                params,
4141                allow_inline,
4142            } => {
4143                let mut term = arg_stack.pop().unwrap();
4144
4145                for param in params.iter().rev() {
4146                    term = term.lambda(param);
4147                }
4148                term = if allow_inline {
4149                    term
4150                } else {
4151                    term.lambda(NO_INLINE)
4152                };
4153
4154                if params.is_empty() {
4155                    Some(term.delay())
4156                } else {
4157                    Some(term)
4158                }
4159            }
4160            Air::Call { count, .. } => {
4161                if count >= 1 {
4162                    let mut term = arg_stack.pop().unwrap();
4163                    for _ in 0..count {
4164                        let arg = arg_stack.pop().unwrap();
4165
4166                        term = term.apply(arg);
4167                    }
4168                    Some(term)
4169                } else {
4170                    let term = arg_stack.pop().unwrap();
4171                    match term.pierce_no_inlines_ref() {
4172                        Term::Var(_) => Some(term.force()),
4173                        Term::Delay(inner_term) => Some(inner_term.as_ref().clone()),
4174                        Term::Apply { .. } => Some(term.force()),
4175                        _ => unreachable!(
4176                            "Shouldn't call anything other than var or apply\n{:#?}",
4177                            term
4178                        ),
4179                    }
4180                }
4181            }
4182            Air::Builtin { func, tipo, count } => {
4183                let mut arg_vec = vec![];
4184                for _ in 0..count {
4185                    arg_vec.push(arg_stack.pop().unwrap());
4186                }
4187
4188                let ret_tipo = match tipo.as_ref() {
4189                    Type::Fn { ret, .. } => ret,
4190                    // In this case the Air Opcode only holds the return type and not the function type
4191                    _ => &tipo,
4192                };
4193
4194                let term = match &func {
4195                    DefaultFunction::IfThenElse
4196                    | DefaultFunction::ChooseUnit
4197                    | DefaultFunction::Trace
4198                    | DefaultFunction::ChooseList
4199                    | DefaultFunction::ChooseData
4200                    | DefaultFunction::MkCons
4201                    | DefaultFunction::UnConstrData => {
4202                        builder::special_case_builtin(&func, tipo, count, arg_vec, &self.data_types)
4203                    }
4204                    DefaultFunction::FstPair | DefaultFunction::SndPair => {
4205                        builder::undata_builtin(&func, count, ret_tipo, arg_vec, &self.data_types)
4206                    }
4207                    DefaultFunction::HeadList if !tipo.is_pair() => {
4208                        builder::undata_builtin(&func, count, ret_tipo, arg_vec, &self.data_types)
4209                    }
4210                    _ => {
4211                        let mut term: Term<Name> = func.into();
4212
4213                        term = builder::apply_builtin_forces(term, func.force_count());
4214
4215                        if func.arg_is_unit() {
4216                            term = term.apply(Term::unit())
4217                        } else {
4218                            for arg in arg_vec {
4219                                term = term.apply(arg.clone());
4220                            }
4221                        }
4222
4223                        term
4224                    }
4225                };
4226
4227                Some(term)
4228            }
4229            Air::BinOp {
4230                name: mut op,
4231                // changed this to argument tipo
4232                left_tipo,
4233                right_tipo,
4234                ..
4235            } => {
4236                let mut left = arg_stack.pop().unwrap();
4237                let mut right = arg_stack.pop().unwrap();
4238
4239                let uplc_type = left_tipo.get_uplc_type();
4240
4241                // When operators are symmetric, favor putting constant first to allow currying
4242                // optimisation to kick-in more easily.
4243                if !left.is_constant() && right.is_constant() {
4244                    // If the operator is symmetric, it's safe to swap left and right
4245                    if op.is_symmetric() {
4246                        std::mem::swap(&mut left, &mut right);
4247                    // Special case for SubInt, which is easy to transform into a sum
4248                    } else if matches!(op, BinOp::SubInt)
4249                        && let Some(minus_right) = right.try_negate()
4250                    {
4251                        right = left;
4252                        left = minus_right;
4253                        op = BinOp::AddInt;
4254                    }
4255                }
4256
4257                let term = match op {
4258                    BinOp::And => left.delayed_if_then_else(right, Term::bool(false)),
4259                    BinOp::Or => left.delayed_if_then_else(Term::bool(true), right),
4260                    BinOp::Eq | BinOp::NotEq => {
4261                        let builtin = match &uplc_type {
4262                            Some(UplcType::Integer) => Term::equals_integer(),
4263                            Some(UplcType::String) => Term::equals_string(),
4264                            Some(UplcType::ByteString) => Term::equals_bytestring(),
4265                            Some(UplcType::Bls12_381G1Element) => Term::bls12_381_g1_equal(),
4266                            Some(UplcType::Bls12_381G2Element) => Term::bls12_381_g2_equal(),
4267                            Some(UplcType::Bool | UplcType::Unit) => Term::unit(),
4268                            Some(UplcType::List(_) | UplcType::Pair(_, _) | UplcType::Data)
4269                            | None => Term::equals_data(),
4270                            Some(UplcType::Bls12_381MlResult) => {
4271                                panic!("ML Result equality is not supported")
4272                            }
4273                        };
4274
4275                        let binop_eq = match uplc_type {
4276                            Some(UplcType::Bool) => {
4277                                if matches!(op, BinOp::Eq) {
4278                                    if left.is_true() {
4279                                        right
4280                                    } else if left.is_false() {
4281                                        right.if_then_else(Term::bool(false), Term::bool(true))
4282                                    } else {
4283                                        left.delayed_if_then_else(
4284                                            right.clone(),
4285                                            right.if_then_else(Term::bool(false), Term::bool(true)),
4286                                        )
4287                                    }
4288                                } else {
4289                                    if left.is_true() {
4290                                        right.if_then_else(Term::bool(false), Term::bool(true))
4291                                    } else if left.is_false() {
4292                                        right
4293                                    } else {
4294                                        left.delayed_if_then_else(
4295                                            right
4296                                                .clone()
4297                                                .if_then_else(Term::bool(false), Term::bool(true)),
4298                                            right.clone(),
4299                                        )
4300                                    }
4301                                }
4302                            }
4303                            Some(UplcType::List(_)) if left_tipo.is_map() => builtin
4304                                .apply(Term::map_data().apply(left))
4305                                .apply(Term::map_data().apply(right)),
4306                            Some(UplcType::List(_)) => builtin
4307                                .apply(Term::list_data().apply(left))
4308                                .apply(Term::list_data().apply(right)),
4309                            Some(UplcType::Pair(_, _)) => {
4310                                builtin
4311                                    .apply(Term::map_data().apply(
4312                                        Term::mk_cons().apply(left).apply(Term::empty_map()),
4313                                    ))
4314                                    .apply(Term::map_data().apply(
4315                                        Term::mk_cons().apply(right).apply(Term::empty_map()),
4316                                    ))
4317                            }
4318                            Some(
4319                                UplcType::Data
4320                                | UplcType::Bls12_381G1Element
4321                                | UplcType::Bls12_381G2Element
4322                                | UplcType::Bls12_381MlResult
4323                                | UplcType::Integer
4324                                | UplcType::String
4325                                | UplcType::ByteString,
4326                            ) => builtin.apply(left).apply(right),
4327
4328                            None => {
4329                                let mut left = left;
4330                                let mut right = right;
4331
4332                                let left_data_type =
4333                                    lookup_data_type_by_tipo(&self.data_types, &left_tipo);
4334
4335                                let right_data_type =
4336                                    lookup_data_type_by_tipo(&self.data_types, &right_tipo);
4337
4338                                if left_data_type
4339                                    .map(|d| {
4340                                        d.decorators
4341                                            .iter()
4342                                            .any(|dec| matches!(dec.kind, DecoratorKind::List))
4343                                    })
4344                                    .unwrap_or(false)
4345                                {
4346                                    left = Term::list_data().apply(left)
4347                                }
4348
4349                                if right_data_type
4350                                    .map(|d| {
4351                                        d.decorators
4352                                            .iter()
4353                                            .any(|dec| matches!(dec.kind, DecoratorKind::List))
4354                                    })
4355                                    .unwrap_or(false)
4356                                {
4357                                    right = Term::list_data().apply(right)
4358                                }
4359
4360                                builtin.apply(left).apply(right)
4361                            }
4362                            Some(UplcType::Unit) => {
4363                                left.choose_unit(right.choose_unit(Term::bool(true)))
4364                            }
4365                        };
4366
4367                        if !left_tipo.is_bool() && matches!(op, BinOp::NotEq) {
4368                            binop_eq.if_then_else(Term::bool(false), Term::bool(true))
4369                        } else {
4370                            binop_eq
4371                        }
4372                    }
4373                    BinOp::LtInt => Term::Builtin(DefaultFunction::LessThanInteger)
4374                        .apply(left)
4375                        .apply(right),
4376                    BinOp::LtEqInt => Term::Builtin(DefaultFunction::LessThanEqualsInteger)
4377                        .apply(left)
4378                        .apply(right),
4379                    BinOp::GtEqInt => Term::Builtin(DefaultFunction::LessThanEqualsInteger)
4380                        .apply(right)
4381                        .apply(left),
4382                    BinOp::GtInt => Term::Builtin(DefaultFunction::LessThanInteger)
4383                        .apply(right)
4384                        .apply(left),
4385                    BinOp::AddInt => Term::add_integer().apply(left).apply(right),
4386                    BinOp::SubInt => Term::Builtin(DefaultFunction::SubtractInteger)
4387                        .apply(left)
4388                        .apply(right),
4389                    BinOp::MultInt => Term::Builtin(DefaultFunction::MultiplyInteger)
4390                        .apply(left)
4391                        .apply(right),
4392                    BinOp::DivInt => Term::Builtin(DefaultFunction::DivideInteger)
4393                        .apply(left)
4394                        .apply(right),
4395                    BinOp::ModInt => Term::Builtin(DefaultFunction::ModInteger)
4396                        .apply(left)
4397                        .apply(right),
4398                };
4399                Some(term)
4400            }
4401            Air::DefineFunc {
4402                func_name,
4403                module_name,
4404                variant_name,
4405                variant,
4406            } => {
4407                let func_name = if module_name.is_empty() {
4408                    format!("{func_name}{variant_name}")
4409                } else {
4410                    format!("{module_name}_{func_name}{variant_name}")
4411                };
4412
4413                match variant {
4414                    air::FunctionVariants::Standard(params) => {
4415                        let mut func_body = arg_stack.pop().unwrap();
4416
4417                        let term = arg_stack.pop().unwrap();
4418
4419                        if params.is_empty() {
4420                            func_body = func_body.delay();
4421                        }
4422
4423                        let func_body = params
4424                            .into_iter()
4425                            .rfold(func_body, |term, arg| term.lambda(arg))
4426                            .lambda(NO_INLINE);
4427
4428                        Some(term.lambda(func_name).apply(func_body))
4429                    }
4430                    air::FunctionVariants::Recursive {
4431                        params,
4432                        recursive_nonstatic_params,
4433                    } => {
4434                        let mut func_body = arg_stack.pop().unwrap();
4435
4436                        let term = arg_stack.pop().unwrap();
4437
4438                        let no_statics = recursive_nonstatic_params == params;
4439
4440                        if recursive_nonstatic_params.is_empty() || params.is_empty() {
4441                            func_body = func_body.delay();
4442                        }
4443
4444                        let func_body = recursive_nonstatic_params
4445                            .iter()
4446                            .rfold(func_body, |term, arg| term.lambda(arg));
4447
4448                        let func_body = func_body.lambda(func_name.clone());
4449
4450                        if no_statics {
4451                            // If we don't have any recursive-static params, we can just emit the function as is
4452                            Some(
4453                                term.lambda(func_name.clone())
4454                                    .apply(
4455                                        Term::var(func_name.clone())
4456                                            .apply(Term::var(func_name.clone())),
4457                                    )
4458                                    .lambda(func_name)
4459                                    .apply(func_body.lambda(NO_INLINE)),
4460                            )
4461                        } else {
4462                            // If we have parameters that remain static in each recursive call,
4463                            // we can construct an *outer* function to take those in
4464                            // and simplify the recursive part to only accept the non-static arguments
4465                            let mut recursive_func_body =
4466                                Term::var(&func_name).apply(Term::var(&func_name));
4467
4468                            if recursive_nonstatic_params.is_empty() {
4469                                recursive_func_body = recursive_func_body.force();
4470                            }
4471
4472                            // Introduce a parameter for each parameter
4473                            // NOTE: we use recursive_nonstatic_params here because
4474                            // if this is recursive, those are the ones that need to be passed
4475                            // each time
4476                            for param in recursive_nonstatic_params.into_iter() {
4477                                recursive_func_body = recursive_func_body.apply(Term::var(param));
4478                            }
4479
4480                            // Then construct an outer function with *all* parameters, not just the nonstatic ones.
4481                            let mut outer_func_body =
4482                                recursive_func_body.lambda(&func_name).apply(func_body);
4483
4484                            // Now, add *all* parameters, so that other call sites don't know the difference
4485                            outer_func_body = params
4486                                .clone()
4487                                .into_iter()
4488                                .rfold(outer_func_body, |term, arg| term.lambda(arg));
4489
4490                            // And finally, fold that definition into the rest of our program
4491                            Some(
4492                                term.lambda(&func_name)
4493                                    .apply(outer_func_body.lambda(NO_INLINE)),
4494                            )
4495                        }
4496                    }
4497                    air::FunctionVariants::Cyclic(contained_functions) => {
4498                        let mut cyclic_functions = vec![];
4499
4500                        for params in contained_functions {
4501                            let func_body = arg_stack.pop().unwrap();
4502
4503                            cyclic_functions.push((params, func_body));
4504                        }
4505                        let mut term = arg_stack.pop().unwrap();
4506
4507                        let mut cyclic_body = Term::var("__chooser");
4508
4509                        for (params, func_body) in cyclic_functions.into_iter() {
4510                            let mut function = func_body;
4511                            if params.is_empty() {
4512                                function = function.delay();
4513                            } else {
4514                                for param in params.iter().rev() {
4515                                    function = function.lambda(param);
4516                                }
4517                            }
4518
4519                            // We basically Scott encode our function bodies and use the chooser function
4520                            // to determine which function body and params is run
4521                            // For example say there is a cycle of 3 function bodies
4522                            // Our choose function can look like this:
4523                            // \func1 -> \func2 -> \func3 -> func1
4524                            // In this case our chooser is a function that takes in 3 functions
4525                            // and returns the first one to run
4526                            cyclic_body = cyclic_body.apply(function)
4527                        }
4528
4529                        term = term
4530                            .lambda(&func_name)
4531                            .apply(Term::var(&func_name).apply(Term::var(&func_name)))
4532                            .lambda(&func_name)
4533                            .apply(
4534                                cyclic_body
4535                                    .lambda("__chooser")
4536                                    .lambda(func_name)
4537                                    .lambda(NO_INLINE),
4538                            );
4539
4540                        Some(term)
4541                    }
4542                }
4543            }
4544
4545            Air::Let { name } => {
4546                let arg = arg_stack.pop().unwrap();
4547
4548                let mut term = arg_stack.pop().unwrap();
4549
4550                term = term.lambda(name).apply(arg);
4551
4552                Some(term)
4553            }
4554            Air::CastFromData { tipo, full_cast } => {
4555                let mut term = arg_stack.pop().unwrap();
4556
4557                term = if full_cast {
4558                    unknown_data_to_type(term, &tipo, &self.data_types)
4559                } else {
4560                    known_data_to_type(term, &tipo, &self.data_types)
4561                };
4562
4563                if extract_constant(term.pierce_no_inlines_ref()).is_some() {
4564                    let mut program = self.new_program(term);
4565
4566                    let mut interner = CodeGenInterner::new();
4567
4568                    interner.program(&mut program);
4569
4570                    let eval_program: Program<NamedDeBruijn> =
4571                        program.clean_up_no_inlines().try_into().unwrap();
4572
4573                    let evaluated_term: Term<NamedDeBruijn> = eval_program
4574                        .eval(ExBudget::default())
4575                        .result()
4576                        .expect("Evaluated on unwrapping a data constant and got an error");
4577
4578                    term = evaluated_term.try_into().unwrap();
4579                }
4580
4581                Some(term)
4582            }
4583            Air::CastToData { tipo } => {
4584                let mut term = arg_stack.pop().unwrap();
4585
4586                if extract_constant(term.pierce_no_inlines_ref()).is_some() {
4587                    term = builder::convert_type_to_data(term, &tipo, &self.data_types);
4588
4589                    let mut program = self.new_program(term);
4590
4591                    let mut interner = CodeGenInterner::new();
4592
4593                    interner.program(&mut program);
4594
4595                    let eval_program: Program<NamedDeBruijn> =
4596                        program.clean_up_no_inlines().try_into().unwrap();
4597
4598                    let evaluated_term: Term<NamedDeBruijn> = eval_program
4599                        .eval(ExBudget::default())
4600                        .result()
4601                        .expect("Evaluated on wrapping a constant into data and got an error");
4602
4603                    term = evaluated_term.try_into().unwrap();
4604                } else {
4605                    term = builder::convert_type_to_data(term, &tipo, &self.data_types);
4606                }
4607
4608                Some(term)
4609            }
4610            Air::AssertBool { is_true } => {
4611                let value = arg_stack.pop().unwrap();
4612
4613                let mut term = arg_stack.pop().unwrap();
4614                let otherwise = arg_stack.pop().unwrap();
4615
4616                if is_true {
4617                    term = value.if_then_else(term.delay(), otherwise).force()
4618                } else {
4619                    term = value.if_then_else(otherwise, term.delay()).force()
4620                }
4621                Some(term)
4622            }
4623            Air::When {
4624                subject_name,
4625                // using subject type here
4626                subject_tipo: tipo,
4627                ..
4628            } => {
4629                let subject = arg_stack.pop().unwrap();
4630
4631                let uplc_type = tipo.get_uplc_type();
4632
4633                let subject = match uplc_type {
4634                    Some(
4635                        UplcType::Bool
4636                        | UplcType::Integer
4637                        | UplcType::String
4638                        | UplcType::ByteString
4639                        | UplcType::Unit
4640                        | UplcType::List(_)
4641                        | UplcType::Pair(_, _)
4642                        | UplcType::Bls12_381G1Element
4643                        | UplcType::Bls12_381G2Element
4644                        | UplcType::Bls12_381MlResult,
4645                    ) => subject,
4646
4647                    Some(UplcType::Data) => subject,
4648
4649                    None => {
4650                        let data_type = lookup_data_type_by_tipo(&self.data_types, &tipo)
4651                            .expect("Found constr with no data type?");
4652
4653                        let list_decorator = data_type
4654                            .decorators
4655                            .iter()
4656                            .any(|dec| matches!(dec.kind, DecoratorKind::List));
4657
4658                        if list_decorator {
4659                            subject
4660                        } else {
4661                            Term::var(CONSTR_INDEX_EXPOSER).apply(subject)
4662                        }
4663                    }
4664                };
4665
4666                let mut term = arg_stack.pop().unwrap();
4667
4668                term = term.lambda(subject_name).apply(subject);
4669
4670                Some(term)
4671            }
4672            Air::Clause {
4673                subject_tipo: tipo,
4674                subject_name,
4675            } => {
4676                // clause to compare
4677                let clause = arg_stack.pop().unwrap();
4678
4679                // the body to be run if the clause matches
4680                let body = arg_stack.pop().unwrap();
4681
4682                // the next branch in the when expression
4683                // Expected to be delayed
4684                let term = arg_stack.pop().unwrap();
4685
4686                assert!(matches!(term, Term::Delay(_) | Term::Var(_)));
4687
4688                let other_clauses = term.clone();
4689
4690                let body = if tipo.is_bool() {
4691                    if matches!(clause, Term::Constant(boolean) if matches!(boolean.as_ref(), UplcConstant::Bool(true)))
4692                    {
4693                        Term::var(subject_name)
4694                            .if_then_else(body.delay(), other_clauses)
4695                            .force()
4696                    } else {
4697                        Term::var(subject_name)
4698                            .if_then_else(other_clauses, body.delay())
4699                            .force()
4700                    }
4701                } else {
4702                    let uplc_type = tipo.get_uplc_type();
4703
4704                    let condition = match uplc_type {
4705                        Some(
4706                            UplcType::Bool
4707                            | UplcType::Unit
4708                            | UplcType::List(_)
4709                            | UplcType::Pair(_, _)
4710                            | UplcType::Bls12_381MlResult,
4711                        ) => unreachable!("{:#?}", tipo),
4712                        Some(UplcType::Data) => unimplemented!(),
4713                        Some(UplcType::Integer) => Term::equals_integer()
4714                            .apply(clause)
4715                            .apply(Term::var(subject_name)),
4716                        Some(UplcType::String) => Term::equals_string()
4717                            .apply(clause)
4718                            .apply(Term::var(subject_name)),
4719                        Some(UplcType::ByteString) => Term::equals_bytestring()
4720                            .apply(clause)
4721                            .apply(Term::var(subject_name)),
4722                        Some(UplcType::Bls12_381G1Element) => Term::bls12_381_g1_equal()
4723                            .apply(clause)
4724                            .apply(Term::var(subject_name)),
4725                        Some(UplcType::Bls12_381G2Element) => Term::bls12_381_g2_equal()
4726                            .apply(clause)
4727                            .apply(Term::var(subject_name)),
4728                        None => Term::equals_integer()
4729                            .apply(clause)
4730                            .apply(Term::var(subject_name)),
4731                    };
4732
4733                    condition.delay_true_if_then_else(body, other_clauses)
4734                };
4735
4736                Some(body)
4737            }
4738            Air::ListClause {
4739                tail_name,
4740                next_tail_name,
4741                ..
4742            } => {
4743                // no longer need to pop off discard
4744                let body = arg_stack.pop().unwrap();
4745                let mut term = arg_stack.pop().unwrap();
4746
4747                assert!(matches!(term, Term::Delay(_)));
4748
4749                term = if let Some((current_tail, next_tail_name)) = next_tail_name {
4750                    term.force()
4751                        .lambda(next_tail_name)
4752                        .apply(Term::tail_list().apply(Term::var(current_tail.clone())))
4753                        .delay()
4754                } else {
4755                    term
4756                };
4757
4758                term = Term::var(tail_name).delay_empty_choose_list(body, term);
4759
4760                Some(term)
4761            }
4762            Air::If { .. } => {
4763                let condition = arg_stack.pop().unwrap();
4764                let then = arg_stack.pop().unwrap();
4765                let mut term = arg_stack.pop().unwrap();
4766
4767                term = condition.delayed_if_then_else(then, term);
4768
4769                Some(term)
4770            }
4771            Air::Constr { tag, count, tipo } => {
4772                let mut arg_vec = vec![];
4773                for _ in 0..count {
4774                    arg_vec.push(arg_stack.pop().unwrap());
4775                }
4776
4777                let mut term = Term::empty_list();
4778
4779                for (index, arg) in arg_vec.iter().enumerate().rev() {
4780                    term = Term::mk_cons()
4781                        .apply(builder::convert_type_to_data(
4782                            arg.clone(),
4783                            &tipo.arg_types().unwrap()[index],
4784                            &self.data_types,
4785                        ))
4786                        .apply(term);
4787                }
4788
4789                if let Some(constr_index) = tag {
4790                    term = Term::constr_data()
4791                        .apply(Term::integer(constr_index.into()))
4792                        .apply(term);
4793                }
4794
4795                if arg_vec.iter().all(|item| {
4796                    let maybe_const = extract_constant(item.pierce_no_inlines_ref());
4797                    maybe_const.is_some()
4798                }) {
4799                    let mut program = self.new_program(term);
4800
4801                    let mut interner = CodeGenInterner::new();
4802
4803                    interner.program(&mut program);
4804
4805                    let eval_program: Program<NamedDeBruijn> =
4806                        program.clean_up_no_inlines().try_into().unwrap();
4807
4808                    let evaluated_term: Term<NamedDeBruijn> = eval_program
4809                        .eval(ExBudget::default())
4810                        .result()
4811                        .expect("Evaluated a constant record with args and got an error");
4812
4813                    term = evaluated_term.try_into().unwrap();
4814                }
4815
4816                Some(term)
4817            }
4818            Air::FieldsExpose {
4819                indices,
4820                is_expect,
4821                list_decorator,
4822            } => {
4823                let mut id_list = vec![];
4824
4825                let mut value = arg_stack.pop().unwrap();
4826
4827                let mut term = arg_stack.pop().unwrap();
4828
4829                let otherwise = if is_expect {
4830                    arg_stack.pop().unwrap()
4831                } else {
4832                    Term::Error.delay()
4833                };
4834
4835                let list_id = self.id_gen.next();
4836
4837                id_list.push(list_id);
4838
4839                indices.iter().for_each(|_| {
4840                    id_list.push(self.id_gen.next());
4841                });
4842
4843                let names_types = indices
4844                    .iter()
4845                    .cloned()
4846                    .zip(id_list)
4847                    .map(|(item, id)| (item.1, item.2, id))
4848                    .collect_vec();
4849
4850                let named_indices = names_types
4851                    .iter()
4852                    .skip_while(|(name, _, _)| name == DISCARDED)
4853                    .collect_vec();
4854
4855                if !named_indices.is_empty() || is_expect {
4856                    term = builder::list_access_to_uplc(
4857                        &names_types,
4858                        false,
4859                        term,
4860                        false,
4861                        is_expect.into(),
4862                        otherwise,
4863                        &self.data_types,
4864                    );
4865
4866                    if !list_decorator {
4867                        value = Term::var(CONSTR_FIELDS_EXPOSER).apply(value);
4868                    }
4869
4870                    term = term.apply(value);
4871
4872                    Some(term)
4873                } else {
4874                    Some(term)
4875                }
4876            }
4877            Air::FieldsEmpty { list_decorator } => {
4878                let mut value = arg_stack.pop().unwrap();
4879
4880                let mut term = arg_stack.pop().unwrap();
4881                let otherwise = arg_stack.pop().unwrap();
4882
4883                if !list_decorator {
4884                    value = Term::var(CONSTR_FIELDS_EXPOSER).apply(value)
4885                }
4886
4887                term = value.choose_list(term.delay(), otherwise).force();
4888
4889                Some(term)
4890            }
4891            Air::ListEmpty => {
4892                let value = arg_stack.pop().unwrap();
4893
4894                let mut term = arg_stack.pop().unwrap();
4895                let otherwise = arg_stack.pop().unwrap();
4896
4897                term = value.choose_list(term.delay(), otherwise).force();
4898
4899                Some(term)
4900            }
4901            Air::Tuple { count, tipo } => {
4902                let mut args = vec![];
4903
4904                let tuple_sub_types = tipo.get_inner_types();
4905
4906                for _ in 0..count {
4907                    let arg = arg_stack.pop().unwrap();
4908                    args.push(arg);
4909                }
4910                let mut constants = vec![];
4911                for arg in &args {
4912                    let maybe_const = extract_constant(arg);
4913                    if let Some(c) = maybe_const {
4914                        constants.push(c);
4915                    }
4916                }
4917
4918                if constants.len() == args.len() {
4919                    let data_constants = builder::convert_constants_to_data(constants);
4920
4921                    let term = Term::Constant(
4922                        UplcConstant::ProtoList(UplcType::Data, data_constants).into(),
4923                    );
4924                    Some(term)
4925                } else {
4926                    let mut term = Term::empty_list();
4927                    for (arg, tipo) in args.into_iter().zip(tuple_sub_types).rev() {
4928                        term = Term::mk_cons()
4929                            .apply(builder::convert_type_to_data(arg, &tipo, &self.data_types))
4930                            .apply(term);
4931                    }
4932                    Some(term)
4933                }
4934            }
4935            Air::Pair { tipo } => {
4936                let fst = arg_stack.pop().unwrap();
4937                let snd = arg_stack.pop().unwrap();
4938
4939                match (extract_constant(&fst), extract_constant(&snd)) {
4940                    (Some(fst), Some(snd)) => {
4941                        let mut pair_fields = builder::convert_constants_to_data(vec![fst, snd]);
4942                        let term = Term::Constant(
4943                            UplcConstant::ProtoPair(
4944                                UplcType::Data,
4945                                UplcType::Data,
4946                                pair_fields.remove(0).into(),
4947                                pair_fields.remove(0).into(),
4948                            )
4949                            .into(),
4950                        );
4951                        Some(term)
4952                    }
4953                    _ => {
4954                        let term = Term::mk_pair_data()
4955                            .apply(builder::convert_type_to_data(
4956                                fst,
4957                                &tipo.get_inner_types()[0],
4958                                &self.data_types,
4959                            ))
4960                            .apply(builder::convert_type_to_data(
4961                                snd,
4962                                &tipo.get_inner_types()[1],
4963                                &self.data_types,
4964                            ));
4965
4966                        Some(term)
4967                    }
4968                }
4969            }
4970            Air::RecordUpdate {
4971                highest_index,
4972                indices,
4973                tipo,
4974            } => {
4975                let tail_name_prefix = "__tail_index";
4976
4977                let data_type =
4978                    lookup_data_type_by_tipo(&self.data_types, &tipo).unwrap_or_else(|| {
4979                        panic!("Attempted record update on an unknown type!\ntype: {tipo:#?}")
4980                    });
4981
4982                assert!(
4983                    !data_type.is_never(),
4984                    "Attempted record update on a Never type.",
4985                );
4986
4987                let list_decorator = data_type
4988                    .decorators
4989                    .iter()
4990                    .any(|dec| matches!(dec.kind, DecoratorKind::List));
4991
4992                let constructor_field_count = data_type.constructors[0].arguments.len();
4993                let mut record = arg_stack.pop().unwrap();
4994
4995                let mut args = IndexMap::new();
4996                let mut unchanged_field_indices = vec![];
4997                // plus 2 so we get one index higher than the record update index
4998                // then we add that and any other unchanged fields to an array to later create the
4999                // lambda bindings
5000                unchanged_field_indices.push(0);
5001                let mut prev_index = 0;
5002
5003                for (index, tipo) in indices
5004                    .into_iter()
5005                    .sorted_by(|(index1, _), (index2, _)| index1.cmp(index2))
5006                {
5007                    let arg = arg_stack.pop().unwrap();
5008                    args.insert(index, (tipo.clone(), arg));
5009
5010                    for field_index in (prev_index + 1)..index {
5011                        unchanged_field_indices.push(field_index);
5012                    }
5013                    prev_index = index;
5014                }
5015
5016                unchanged_field_indices.push(prev_index + 1);
5017
5018                let mut term = Term::var(format!("{tail_name_prefix}_{}", highest_index + 1));
5019
5020                for current_index in (0..(highest_index + 1)).rev() {
5021                    let tail_name = format!("{tail_name_prefix}_{current_index}");
5022
5023                    if let Some((tipo, arg)) = args.get(&current_index) {
5024                        term = Term::mk_cons()
5025                            .apply(builder::convert_type_to_data(
5026                                arg.clone(),
5027                                tipo,
5028                                &self.data_types,
5029                            ))
5030                            .apply(term);
5031                    } else {
5032                        term = Term::mk_cons()
5033                            .apply(Term::head_list().apply(Term::var(tail_name)))
5034                            .apply(term);
5035                    }
5036                }
5037
5038                if !list_decorator {
5039                    term = Term::constr_data()
5040                        .apply(Term::integer(0.into()))
5041                        .apply(term);
5042                }
5043
5044                if unchanged_field_indices.len() > 1 {
5045                    let (prev_index, rest_list) = unchanged_field_indices
5046                        .split_last()
5047                        .unwrap_or_else(|| panic!("WHAT HAPPENED"));
5048
5049                    let mut prev_index = *prev_index;
5050
5051                    for index in rest_list.iter().rev() {
5052                        let index = *index;
5053                        let suffix_tail = format!("{tail_name_prefix}_{prev_index}");
5054                        let tail = format!("{tail_name_prefix}_{index}");
5055
5056                        let mut tail_list = Term::var(tail);
5057
5058                        if index < prev_index {
5059                            tail_list = tail_list.repeat_tail_list(prev_index - index);
5060
5061                            if prev_index == constructor_field_count {
5062                                term = term.lambda(suffix_tail).apply(Term::empty_list());
5063                            } else {
5064                                term = term.lambda(suffix_tail).apply(tail_list);
5065                            }
5066                        }
5067                        prev_index = index;
5068                    }
5069                }
5070
5071                if !list_decorator {
5072                    record = Term::var(CONSTR_FIELDS_EXPOSER).apply(record)
5073                }
5074
5075                term = term.lambda(format!("{tail_name_prefix}_0")).apply(record);
5076
5077                Some(term)
5078            }
5079            Air::UnOp { op } => {
5080                let value = arg_stack.pop().unwrap();
5081
5082                let term = match op {
5083                    UnOp::Not => value.if_then_else(Term::bool(false), Term::bool(true)),
5084                    UnOp::Negate => {
5085                        if let Term::Constant(c) = &value {
5086                            if let UplcConstant::Integer(i) = c.as_ref() {
5087                                Term::integer(-i)
5088                            } else {
5089                                Term::subtract_integer()
5090                                    .apply(Term::integer(0.into()))
5091                                    .apply(value)
5092                            }
5093                        } else {
5094                            Term::subtract_integer()
5095                                .apply(Term::integer(0.into()))
5096                                .apply(value)
5097                        }
5098                    }
5099                };
5100
5101                Some(term)
5102            }
5103            Air::TupleAccessor {
5104                tipo,
5105                names,
5106                is_expect,
5107            } => {
5108                let inner_types = tipo.get_inner_types();
5109                let value = arg_stack.pop().unwrap();
5110
5111                let mut term = arg_stack.pop().unwrap();
5112                let otherwise = if is_expect {
5113                    arg_stack.pop().unwrap()
5114                } else {
5115                    Term::Error.delay()
5116                };
5117                let list_id = self.id_gen.next();
5118
5119                let mut id_list = vec![];
5120                id_list.push(list_id);
5121
5122                names.iter().for_each(|_| {
5123                    id_list.push(self.id_gen.next());
5124                });
5125
5126                let names_types = names
5127                    .into_iter()
5128                    .zip(inner_types)
5129                    .zip(id_list)
5130                    .map(|((name, tipo), id)| (name, tipo, id))
5131                    .collect_vec();
5132
5133                term = builder::list_access_to_uplc(
5134                    &names_types,
5135                    false,
5136                    term,
5137                    false,
5138                    is_expect.into(),
5139                    otherwise,
5140                    &self.data_types,
5141                )
5142                .apply(value);
5143
5144                Some(term)
5145            }
5146            Air::PairAccessor {
5147                fst,
5148                snd,
5149                tipo,
5150                is_expect,
5151            } => {
5152                let inner_types = tipo.get_inner_types();
5153                let value = arg_stack.pop().unwrap();
5154
5155                let mut term = arg_stack.pop().unwrap();
5156                let otherwise = if is_expect {
5157                    arg_stack.pop().unwrap()
5158                } else {
5159                    Term::Error.delay()
5160                };
5161
5162                let list_id = self.id_gen.next();
5163
5164                if let Some(name) = snd {
5165                    let value = Term::snd_pair().apply(Term::var(format!("__pair_{list_id}")));
5166                    term = if is_expect {
5167                        if otherwise == Term::Error.delay() {
5168                            term.lambda(name).apply(unknown_data_to_type(
5169                                value,
5170                                &inner_types[1],
5171                                &self.data_types,
5172                            ))
5173                        } else {
5174                            softcast_data_to_type_otherwise(
5175                                value,
5176                                &name,
5177                                &inner_types[1],
5178                                term,
5179                                otherwise.clone(),
5180                                &self.data_types,
5181                            )
5182                        }
5183                    } else {
5184                        term.lambda(name).apply(known_data_to_type(
5185                            value,
5186                            &inner_types[1],
5187                            &self.data_types,
5188                        ))
5189                    }
5190                }
5191
5192                if let Some(name) = fst {
5193                    let value = Term::fst_pair().apply(Term::var(format!("__pair_{list_id}")));
5194                    term = if is_expect {
5195                        if otherwise == Term::Error.delay() {
5196                            term.lambda(name).apply(unknown_data_to_type(
5197                                value,
5198                                &inner_types[0],
5199                                &self.data_types,
5200                            ))
5201                        } else {
5202                            softcast_data_to_type_otherwise(
5203                                value,
5204                                &name,
5205                                &inner_types[0],
5206                                term,
5207                                otherwise,
5208                                &self.data_types,
5209                            )
5210                        }
5211                    } else {
5212                        term.lambda(name).apply(known_data_to_type(
5213                            value,
5214                            &inner_types[0],
5215                            &self.data_types,
5216                        ))
5217                    }
5218                }
5219
5220                term = term.lambda(format!("__pair_{list_id}")).apply(value);
5221
5222                Some(term)
5223            }
5224            Air::Trace { .. } => {
5225                let text = arg_stack.pop().unwrap();
5226
5227                let term = arg_stack.pop().unwrap();
5228
5229                let term = term.delayed_trace(text);
5230
5231                Some(term)
5232            }
5233            Air::ErrorTerm { validator, .. } => {
5234                if validator {
5235                    Some(Term::Error.apply(Term::Error.force()))
5236                } else {
5237                    Some(Term::Error)
5238                }
5239            }
5240
5241            Air::NoOp => None,
5242            Air::SoftCastLet { name, tipo } => {
5243                let value = arg_stack.pop().unwrap();
5244                let then = arg_stack.pop().unwrap();
5245                let otherwise = arg_stack.pop().unwrap();
5246
5247                if otherwise == Term::Error.delay() {
5248                    Some(then.lambda(name).apply(unknown_data_to_type(
5249                        value,
5250                        &tipo,
5251                        &self.data_types,
5252                    )))
5253                } else {
5254                    Some(softcast_data_to_type_otherwise(
5255                        value,
5256                        &name,
5257                        &tipo,
5258                        then,
5259                        otherwise,
5260                        &self.data_types,
5261                    ))
5262                }
5263            }
5264            Air::ExtractField { tipo } => {
5265                let arg = arg_stack.pop().unwrap();
5266
5267                Some(known_data_to_type(
5268                    Term::head_list().apply(arg),
5269                    &tipo,
5270                    &self.data_types,
5271                ))
5272            }
5273        }
5274    }
5275}
5276
5277fn handle_assigns(
5278    subject_name: &String,
5279    subject_tipo: Rc<Type>,
5280    assigns: &[Assigned],
5281    stick_set: &mut TreeSet,
5282    then: AirTree,
5283) -> AirTree {
5284    match assigns {
5285        [] => then,
5286        [assign, rest @ ..] => {
5287            let Assigned { path, assigned } = assign;
5288
5289            let current_tipo = get_tipo_by_path(subject_tipo.clone(), path);
5290            let builtins_path = Builtins::new_from_path(subject_tipo.clone(), path.clone());
5291            let current_subject_name = if builtins_path.is_empty() {
5292                subject_name.clone()
5293            } else {
5294                format!("{subject_name}_{builtins_path}")
5295            };
5296
5297            // Transition process from previous to current
5298            let builtins_to_add = stick_set.diff_union_builtins(builtins_path.clone());
5299
5300            // Previous path to apply the transition process too
5301            let prev_builtins = Builtins {
5302                vec: builtins_path.vec[0..(builtins_path.len() - builtins_to_add.len())].to_vec(),
5303            };
5304
5305            let prev_subject_name = if prev_builtins.is_empty() {
5306                subject_name.clone()
5307            } else {
5308                format!("{subject_name}_{prev_builtins}")
5309            };
5310            let prev_tipo = prev_builtins
5311                .vec
5312                .last()
5313                .map_or(subject_tipo.clone(), |last| last.tipo());
5314
5315            let assignment = AirTree::let_assignment(
5316                assigned,
5317                AirTree::local_var(current_subject_name, current_tipo),
5318                handle_assigns(subject_name, subject_tipo, rest, stick_set, then),
5319            );
5320
5321            builtins_to_add.produce_air(prev_subject_name, prev_tipo, assignment)
5322        }
5323    }
5324}